Compare commits
3 Commits
4cf063dce0
...
a67a2ec7ea
| Author | SHA1 | Date | |
|---|---|---|---|
| a67a2ec7ea | |||
| f71220fa80 | |||
| f957d36e14 |
|
|
@ -2,7 +2,7 @@
|
|||
"loading": "በመጫን ላይ",
|
||||
"welcome_back": "እንኳን በደህና ተመለሱ",
|
||||
"checking_user_info": "የተጠቃሚ መረጃን በማረጋገጥ ላይ",
|
||||
"dont_have_account": "መለያ የለዎትም? ይመዝገቡ",
|
||||
"dont_have_account": "መለያ የለዎትም?",
|
||||
"email": "ኢሜይል",
|
||||
"password": "የይለፍ ቃል",
|
||||
"forgot_password": "የይለፍ ቃል ረሱ?",
|
||||
|
|
@ -43,14 +43,14 @@
|
|||
"continue_learning": "መማርን ይቀጥሉ",
|
||||
"start_learning": "ትምህርትን ይጀምሩ",
|
||||
"completed": "ተጠናቋል",
|
||||
"take_practice": " ልምምድ ያድርጉ",
|
||||
"take_practice": "ልምምድ ያድርጉ",
|
||||
"your_current_level": "የአሁኑ ደረጃዎ",
|
||||
"overall_progress": "አጠቃላይ እድገት",
|
||||
"great_work": "በርቱ! በጣም ጥሩ እየሰሩ ነው",
|
||||
"view_module": "ሞጁሉን ይመልከቱ",
|
||||
"progress": "እድገት",
|
||||
"keep_going": " ይቀጥሉ - ከግማሽ በላይ ጨርሰዋል ",
|
||||
"lessons_in_module": " በዚህ ሞጁል ውስጥ ያሉ ትምህርቶች ",
|
||||
"keep_going": "ይቀጥሉ - ከግማሽ በላይ ጨርሰዋል ",
|
||||
"lessons_in_module": "በዚህ ሞጁል ውስጥ ያሉ ትምህርቶች ",
|
||||
"practice": "ልምምድ",
|
||||
"start": "ጀምር",
|
||||
"in_progress": "በሂደት ላይ",
|
||||
|
|
@ -134,7 +134,7 @@
|
|||
"ask_you_few_actions": "ጥቂት ጥያቄዎችን እጠይቅሃለሁ፣ አንተም በተፈጥሮ መልስ ልትሰጥ ትችላለህ።",
|
||||
"begin_level_practice": "የደረጃ ልምምድን ጀምር",
|
||||
"lets_practice_course": "የኮርሱን ልምምድ እንለማመድ",
|
||||
"lets_quick_practice": "በዚህ ደረጃ የተማርከውን በፍጥነት እንከልስ!",
|
||||
"lets_quick_review": "በዚህ ደረጃ የተማርከውን በፍጥነት እንከልስ!",
|
||||
"speaking": "እየተናገረ ነው",
|
||||
"you_have_finished_practice": "ልምምድህን አጠናቀቅህ",
|
||||
"view_results": "ውጤቶቼን እይ",
|
||||
|
|
@ -153,15 +153,36 @@
|
|||
"what_should_we_call_you": "ምን ብለን እንጠራህ?",
|
||||
"name_for_personalization": "በመማር ጉዞህ ውስጥ ለግል ለማድረግ ስምህን እንጠቀማለን።",
|
||||
"choose_your_gender": "ጾታህን ምረጥ",
|
||||
"gender_for_personalization": "በጾታህ መሰረት የመማር ተሞክሮህን እናበጅለታለን።"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
"gender_for_personalization": "በጾታህ መሰረት የመማር ተሞክሮህን እናበጅለታለን።",
|
||||
"age_range": "በየትኛው የእድሜ ክልል ውስጥ ነህ?",
|
||||
"age_for_personalization": "በእድሜህ መሰረት የመማር ተሞክሮህን እናበጅለታለን።",
|
||||
"educational_background": "አሁን ያለህ የትምህርት ደረጃ ምንድን ነው?",
|
||||
"education_for_personalization": "ይህ ትምህርቶችን ከልምድህ ጋር እንዲስማሙ ለማድረግ ይረዳናል።",
|
||||
"your_occupation": "ስራህ ምንድን ነው?",
|
||||
"occupation_for_personalization": "በስራህ መሰረት የመማር ተሞክሮህን እናበጅለታለን።",
|
||||
"location": "ከየት ነህ?",
|
||||
"select_country_region": "አገርህን እና ክልልህን ከተቆልቋይ ዝርዝሩ ምረጥ",
|
||||
"select_country": "አገር ምረጥ",
|
||||
"learning_goal": "የመማር ዓላማህን ምረጥ",
|
||||
"language_goal": "እንግሊዝኛህን ለማሻሻል ዋና ዓላማህ ምንድን ነው?",
|
||||
"your_goal": "ዓላማህ የመማር ጉዞህን እንዲስማማ ለማድረግ ይረዳናል።",
|
||||
"write_your_goal": "ዓላማህን ጻፍ…",
|
||||
"challenge_you_face": "What challenge do you face most with English?",
|
||||
"evey_one_has_strugle": "ሁሉም ሰው ችግሮች አሉት፣ የአንተን እንጀምር እንፍታ",
|
||||
"write_your_challenge": "ችግርህን ጻፍ…",
|
||||
"topic_interest": "በጣም የሚስቡህ ርዕሶች የትኞቹ ናቸው?",
|
||||
"favourite_topic": "የምትወዳቸው ርዕሶች አስደሳች እና ከሕይወትህ ጋር የተዛመዱ ትምህርቶችን ለመፍጠር ይረዱናል።",
|
||||
"your_interest": "ፍላጎትህን ጻፍ…",
|
||||
"want_quick_assessment": "የእንግሊዝኛ ደረጃህን ለማወቅ ፈጣን ግምገማ ትፈልጋለህ?",
|
||||
"answer_quick_questions": "የእንግሊዝኛ ችሎታህን ለመረዳት ጥቂት ፈጣን ጥያቄዎችን መልስ።",
|
||||
"skip": "ዝለል",
|
||||
"finish_level": "ደረጃውን አጠናቅቅ",
|
||||
"likely_speaker": "አንተ ምናልባት ተናጋሪ ነህ",
|
||||
"great_job": "በጣም ጥሩ ስራ! ለመሻሻል ቀጣዩ ደረጃህ ይኸው ነው።",
|
||||
"lets_start_practice": "ልምምድህን እንጀምር",
|
||||
"welcome_abroad": "እንኳን ደህና መጣህ",
|
||||
"ready_to_explore": "የግል ትምህርቶችህን ለማሰስ ዝግጁ ነህ።",
|
||||
"finish": "አጠናቅቅ"
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
{"loading": "Loading",
|
||||
"welcome_back": "Welcome back",
|
||||
"checking_user_info": "Checking user info",
|
||||
"dont_have_account": "Don't have an account? Register",
|
||||
"dont_have_account": "Don't have an account?",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"forgot_password": "Forgot password?",
|
||||
|
|
@ -153,7 +153,34 @@
|
|||
"what_should_we_call_you": "What should we call you?",
|
||||
"name_for_personalization": "We’ll use your name to personalize your learning journey.",
|
||||
"choose_your_gender": "Choose your gender?",
|
||||
"gender_for_personalization": "We’ll personalize your learning experience based on your gender."
|
||||
|
||||
|
||||
"gender_for_personalization": "We’ll personalize your learning experience based on your gender.",
|
||||
"age_range": "Which age range are you in?",
|
||||
"age_for_personalization": "We’ll personalize your learning experience based on your age.",
|
||||
"educational_background": "What’s your current educational level?",
|
||||
"education_for_personalization": "This helps us tailor your lessons to your experience.",
|
||||
"your_occupation": "What’s your occupation?",
|
||||
"occupation_for_personalization": "We’ll personalize your learning experience based on your occupation.",
|
||||
"location": "Where are you from?",
|
||||
"select_country_region": "Select your country and region from the dropdown",
|
||||
"select_country": "Select country",
|
||||
"learning_goal": "Choose your learning goal.",
|
||||
"language_goal": "What’s your main goal for improving your English?",
|
||||
"your_goal": "Your goal helps us tailor your learning journey.",
|
||||
"write_your_goal": "Write your goal…",
|
||||
"challenge_you_face": "What challenge do you face most with English?",
|
||||
"evey_one_has_strugle": "Everyone has struggles, let’s start fixing yours",
|
||||
"write_your_challenge": "Write your challenge…",
|
||||
"topic_interest": "Which topics interest you most?",
|
||||
"favourite_topic": "Your favorite topics help us create fun, relatable lessons.",
|
||||
"your_interest": "Write your interest…",
|
||||
"want_quick_assessment": "Want a quick assessment to know your English level?",
|
||||
"answer_quick_questions": "Answer a few quick questions to help us understand your English proficiency.",
|
||||
"skip": "Skip",
|
||||
"finish_level": "Finish Level",
|
||||
"likely_speaker": "You’re likely speaker of",
|
||||
"great_job": "Great Job! Here’s your next step to keep improving.",
|
||||
"lets_start_practice": "Let's start your practice",
|
||||
"welcome_abroad": "Welcome aboard",
|
||||
"ready_to_explore": "You’re ready to explore your personalized lessons.",
|
||||
"finish": "Finish"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@ import 'package:yimaru_app/services/audio_player_service.dart';
|
|||
import 'package:yimaru_app/services/voice_recorder_service.dart';
|
||||
import 'package:yimaru_app/services/in_app_update_service.dart';
|
||||
import 'package:yimaru_app/ui/views/learn_program/learn_program_view.dart';
|
||||
import 'package:yimaru_app/ui/views/learn_course/learn_course_view.dart';
|
||||
import 'package:yimaru_app/ui/views/assessment/assessment_view.dart';
|
||||
import 'package:yimaru_app/services/vimeo_service.dart';
|
||||
import 'package:yimaru_app/services/url_launcher_service.dart';
|
||||
|
|
@ -57,6 +56,8 @@ import 'package:yimaru_app/ui/views/course_unit/course_unit_view.dart';
|
|||
import 'package:yimaru_app/services/localization_service.dart';
|
||||
import 'package:yimaru_app/ui/views/landing/landing_view.dart';
|
||||
import 'package:yimaru_app/ui/views/course_module/course_module_view.dart';
|
||||
import 'package:yimaru_app/services/onboarding_service.dart';
|
||||
import 'package:yimaru_app/ui/views/learn_course/learn_course_view.dart';
|
||||
// @stacked-import
|
||||
|
||||
@StackedApp(
|
||||
|
|
@ -88,7 +89,6 @@ import 'package:yimaru_app/ui/views/course_module/course_module_view.dart';
|
|||
MaterialRoute(page: DuolingoView),
|
||||
MaterialRoute(page: CourseView),
|
||||
MaterialRoute(page: LearnProgramView),
|
||||
MaterialRoute(page: LearnCourseView),
|
||||
MaterialRoute(page: AssessmentView),
|
||||
MaterialRoute(page: LearnSubscriptionView),
|
||||
MaterialRoute(page: ArifPayView),
|
||||
|
|
@ -123,6 +123,7 @@ import 'package:yimaru_app/ui/views/course_module/course_module_view.dart';
|
|||
LazySingleton(classType: PhoneCallerService),
|
||||
LazySingleton(classType: LearnService),
|
||||
LazySingleton(classType: LocalizationService),
|
||||
LazySingleton(classType: OnboardingService),
|
||||
// @stacked-service
|
||||
],
|
||||
bottomsheets: [
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import '../services/in_app_update_service.dart';
|
|||
import '../services/learn_service.dart';
|
||||
import '../services/localization_service.dart';
|
||||
import '../services/notification_service.dart';
|
||||
import '../services/onboarding_service.dart';
|
||||
import '../services/permission_handler_service.dart';
|
||||
import '../services/phone_caller_service.dart';
|
||||
import '../services/secure_storage_service.dart';
|
||||
|
|
@ -65,4 +66,5 @@ Future<void> setupLocator(
|
|||
locator.registerLazySingleton(() => PhoneCallerService());
|
||||
locator.registerLazySingleton(() => LearnService());
|
||||
locator.registerLazySingleton(() => LocalizationService());
|
||||
locator.registerLazySingleton(() => OnboardingService());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@
|
|||
// **************************************************************************
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:flutter/material.dart' as _i37;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/material.dart' as _i37;
|
||||
import 'package:stacked/stacked.dart' as _i1;
|
||||
import 'package:stacked_services/stacked_services.dart' as _i46;
|
||||
import 'package:yimaru_app/models/course.dart' as _i42;
|
||||
|
|
@ -20,30 +20,30 @@ import 'package:yimaru_app/models/learn_module.dart' as _i39;
|
|||
import 'package:yimaru_app/ui/common/enmus.dart' as _i41;
|
||||
import 'package:yimaru_app/ui/views/account_privacy/account_privacy_view.dart'
|
||||
as _i9;
|
||||
import 'package:yimaru_app/ui/views/arif_pay/arif_pay_view.dart' as _i32;
|
||||
import 'package:yimaru_app/ui/views/assessment/assessment_view.dart' as _i30;
|
||||
import 'package:yimaru_app/ui/views/arif_pay/arif_pay_view.dart' as _i31;
|
||||
import 'package:yimaru_app/ui/views/assessment/assessment_view.dart' as _i29;
|
||||
import 'package:yimaru_app/ui/views/call_support/call_support_view.dart'
|
||||
as _i12;
|
||||
import 'package:yimaru_app/ui/views/course/course_view.dart' as _i27;
|
||||
import 'package:yimaru_app/ui/views/course_catalog/course_catalog_view.dart'
|
||||
as _i33;
|
||||
as _i32;
|
||||
import 'package:yimaru_app/ui/views/course_lesson_detail/course_lesson_detail_view.dart'
|
||||
as _i25;
|
||||
import 'package:yimaru_app/ui/views/course_module/course_module_view.dart'
|
||||
as _i36;
|
||||
as _i35;
|
||||
import 'package:yimaru_app/ui/views/course_payment/course_payment_view.dart'
|
||||
as _i23;
|
||||
import 'package:yimaru_app/ui/views/course_unit/course_unit_view.dart' as _i34;
|
||||
import 'package:yimaru_app/ui/views/course_unit/course_unit_view.dart' as _i33;
|
||||
import 'package:yimaru_app/ui/views/downloads/downloads_view.dart' as _i7;
|
||||
import 'package:yimaru_app/ui/views/duolingo/duolingo_view.dart' as _i26;
|
||||
import 'package:yimaru_app/ui/views/failure/failure_view.dart' as _i24;
|
||||
import 'package:yimaru_app/ui/views/forget_password/forget_password_view.dart'
|
||||
as _i20;
|
||||
import 'package:yimaru_app/ui/views/home/home_view.dart' as _i2;
|
||||
import 'package:yimaru_app/ui/views/landing/landing_view.dart' as _i35;
|
||||
import 'package:yimaru_app/ui/views/landing/landing_view.dart' as _i34;
|
||||
import 'package:yimaru_app/ui/views/language/language_view.dart' as _i13;
|
||||
import 'package:yimaru_app/ui/views/learn_course/learn_course_view.dart'
|
||||
as _i29;
|
||||
as _i36;
|
||||
import 'package:yimaru_app/ui/views/learn_lesson/learn_lesson_view.dart'
|
||||
as _i19;
|
||||
import 'package:yimaru_app/ui/views/learn_lesson_detail/learn_lesson_detail_view.dart'
|
||||
|
|
@ -55,7 +55,7 @@ import 'package:yimaru_app/ui/views/learn_practice/learn_practice_view.dart'
|
|||
import 'package:yimaru_app/ui/views/learn_program/learn_program_view.dart'
|
||||
as _i28;
|
||||
import 'package:yimaru_app/ui/views/learn_subscription/learn_subscription_view.dart'
|
||||
as _i31;
|
||||
as _i30;
|
||||
import 'package:yimaru_app/ui/views/login/login_view.dart' as _i17;
|
||||
import 'package:yimaru_app/ui/views/onboarding/onboarding_view.dart' as _i3;
|
||||
import 'package:yimaru_app/ui/views/privacy_policy/privacy_policy_view.dart'
|
||||
|
|
@ -127,8 +127,6 @@ class Routes {
|
|||
|
||||
static const learnProgramView = '/learn-program-view';
|
||||
|
||||
static const learnCourseView = '/learn-course-view';
|
||||
|
||||
static const assessmentView = '/assessment-view';
|
||||
|
||||
static const learnSubscriptionView = '/learn-subscription-view';
|
||||
|
|
@ -143,6 +141,7 @@ class Routes {
|
|||
|
||||
static const courseModuleView = '/course-module-view';
|
||||
|
||||
static const learnCourseView = '/learn-course-view';
|
||||
|
||||
static const all = <String>{
|
||||
homeView,
|
||||
|
|
@ -172,7 +171,6 @@ class Routes {
|
|||
duolingoView,
|
||||
courseView,
|
||||
learnProgramView,
|
||||
learnCourseView,
|
||||
assessmentView,
|
||||
learnSubscriptionView,
|
||||
arifPayView,
|
||||
|
|
@ -180,6 +178,7 @@ class Routes {
|
|||
courseUnitView,
|
||||
landingView,
|
||||
courseModuleView,
|
||||
learnCourseView,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -293,41 +292,37 @@ class StackedRouter extends _i1.RouterBase {
|
|||
Routes.learnProgramView,
|
||||
page: _i28.LearnProgramView,
|
||||
),
|
||||
_i1.RouteDef(
|
||||
Routes.learnCourseView,
|
||||
page: _i29.LearnCourseView,
|
||||
),
|
||||
_i1.RouteDef(
|
||||
Routes.assessmentView,
|
||||
page: _i30.AssessmentView,
|
||||
page: _i29.AssessmentView,
|
||||
),
|
||||
_i1.RouteDef(
|
||||
Routes.learnSubscriptionView,
|
||||
page: _i31.LearnSubscriptionView,
|
||||
page: _i30.LearnSubscriptionView,
|
||||
),
|
||||
_i1.RouteDef(
|
||||
Routes.arifPayView,
|
||||
page: _i32.ArifPayView,
|
||||
page: _i31.ArifPayView,
|
||||
),
|
||||
_i1.RouteDef(
|
||||
Routes.courseCatalogView,
|
||||
page: _i33.CourseCatalogView,
|
||||
page: _i32.CourseCatalogView,
|
||||
),
|
||||
_i1.RouteDef(
|
||||
Routes.courseUnitView,
|
||||
page: _i34.CourseUnitView,
|
||||
page: _i33.CourseUnitView,
|
||||
),
|
||||
_i1.RouteDef(
|
||||
Routes.landingView,
|
||||
page: _i35.LandingView,
|
||||
page: _i34.LandingView,
|
||||
),
|
||||
_i1.RouteDef(
|
||||
Routes.courseModuleView,
|
||||
page: _i36.CourseModuleView,
|
||||
page: _i35.CourseModuleView,
|
||||
),
|
||||
_i1.RouteDef(
|
||||
Routes.learnCourseView,
|
||||
page: _i29.LearnCourseView,
|
||||
page: _i36.LearnCourseView,
|
||||
),
|
||||
];
|
||||
|
||||
|
|
@ -577,72 +572,72 @@ class StackedRouter extends _i1.RouterBase {
|
|||
settings: data,
|
||||
);
|
||||
},
|
||||
_i29.LearnCourseView: (data) {
|
||||
final args = data.getArgs<LearnCourseViewArguments>(nullOk: false);
|
||||
return _i37.MaterialPageRoute<dynamic>(
|
||||
builder: (context) => _i29.LearnCourseView(key: args.key, id: args.id),
|
||||
settings: data,
|
||||
);
|
||||
},
|
||||
_i30.AssessmentView: (data) {
|
||||
_i29.AssessmentView: (data) {
|
||||
final args = data.getArgs<AssessmentViewArguments>(nullOk: false);
|
||||
return _i37.MaterialPageRoute<dynamic>(
|
||||
builder: (context) =>
|
||||
_i30.AssessmentView(key: args.key, data: args.data),
|
||||
_i29.AssessmentView(key: args.key, data: args.data),
|
||||
settings: data,
|
||||
);
|
||||
},
|
||||
_i31.LearnSubscriptionView: (data) {
|
||||
_i30.LearnSubscriptionView: (data) {
|
||||
final args = data.getArgs<LearnSubscriptionViewArguments>(
|
||||
orElse: () => const LearnSubscriptionViewArguments(),
|
||||
);
|
||||
return _i37.MaterialPageRoute<dynamic>(
|
||||
builder: (context) => _i31.LearnSubscriptionView(key: args.key),
|
||||
builder: (context) => _i30.LearnSubscriptionView(key: args.key),
|
||||
settings: data,
|
||||
);
|
||||
},
|
||||
_i32.ArifPayView: (data) {
|
||||
_i31.ArifPayView: (data) {
|
||||
final args = data.getArgs<ArifPayViewArguments>(nullOk: false);
|
||||
return _i37.MaterialPageRoute<dynamic>(
|
||||
builder: (context) =>
|
||||
_i32.ArifPayView(key: args.key, phone: args.phone),
|
||||
_i31.ArifPayView(key: args.key, phone: args.phone),
|
||||
settings: data,
|
||||
);
|
||||
},
|
||||
_i33.CourseCatalogView: (data) {
|
||||
_i32.CourseCatalogView: (data) {
|
||||
final args = data.getArgs<CourseCatalogViewArguments>(
|
||||
orElse: () => const CourseCatalogViewArguments(),
|
||||
);
|
||||
return _i37.MaterialPageRoute<dynamic>(
|
||||
builder: (context) => _i33.CourseCatalogView(key: args.key),
|
||||
builder: (context) => _i32.CourseCatalogView(key: args.key),
|
||||
settings: data,
|
||||
);
|
||||
},
|
||||
_i34.CourseUnitView: (data) {
|
||||
_i33.CourseUnitView: (data) {
|
||||
final args = data.getArgs<CourseUnitViewArguments>(nullOk: false);
|
||||
return _i37.MaterialPageRoute<dynamic>(
|
||||
builder: (context) =>
|
||||
_i34.CourseUnitView(key: args.key, catalog: args.catalog),
|
||||
_i33.CourseUnitView(key: args.key, catalog: args.catalog),
|
||||
settings: data,
|
||||
);
|
||||
},
|
||||
_i35.LandingView: (data) {
|
||||
_i34.LandingView: (data) {
|
||||
final args = data.getArgs<LandingViewArguments>(
|
||||
orElse: () => const LandingViewArguments(),
|
||||
);
|
||||
return _i37.MaterialPageRoute<dynamic>(
|
||||
builder: (context) => _i35.LandingView(key: args.key),
|
||||
builder: (context) => _i34.LandingView(key: args.key),
|
||||
settings: data,
|
||||
);
|
||||
},
|
||||
_i36.CourseModuleView: (data) {
|
||||
_i35.CourseModuleView: (data) {
|
||||
final args = data.getArgs<CourseModuleViewArguments>(nullOk: false);
|
||||
return _i37.MaterialPageRoute<dynamic>(
|
||||
builder: (context) => _i36.CourseModuleView(
|
||||
builder: (context) => _i35.CourseModuleView(
|
||||
key: args.key, module: args.module, catalog: args.catalog),
|
||||
settings: data,
|
||||
);
|
||||
},
|
||||
_i36.LearnCourseView: (data) {
|
||||
final args = data.getArgs<LearnCourseViewArguments>(nullOk: false);
|
||||
return _i37.MaterialPageRoute<dynamic>(
|
||||
builder: (context) => _i36.LearnCourseView(key: args.key, id: args.id),
|
||||
settings: data,
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
@override
|
||||
|
|
@ -1328,33 +1323,6 @@ class LearnProgramViewArguments {
|
|||
}
|
||||
}
|
||||
|
||||
class LearnCourseViewArguments {
|
||||
const LearnCourseViewArguments({
|
||||
this.key,
|
||||
required this.id,
|
||||
});
|
||||
|
||||
final _i37.Key? key;
|
||||
|
||||
final int id;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '{"key": "$key", "id": "$id"}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(covariant LearnCourseViewArguments other) {
|
||||
if (identical(this, other)) return true;
|
||||
return other.key == key && other.id == id;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return key.hashCode ^ id.hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
class AssessmentViewArguments {
|
||||
const AssessmentViewArguments({
|
||||
this.key,
|
||||
|
|
@ -1534,6 +1502,33 @@ class CourseModuleViewArguments {
|
|||
}
|
||||
}
|
||||
|
||||
class LearnCourseViewArguments {
|
||||
const LearnCourseViewArguments({
|
||||
this.key,
|
||||
required this.id,
|
||||
});
|
||||
|
||||
final _i37.Key? key;
|
||||
|
||||
final int id;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '{"key": "$key", "id": "$id"}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(covariant LearnCourseViewArguments other) {
|
||||
if (identical(this, other)) return true;
|
||||
return other.key == key && other.id == id;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return key.hashCode ^ id.hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
extension NavigatorStateExtension on _i46.NavigationService {
|
||||
Future<dynamic> navigateToHomeView({
|
||||
_i37.Key? key,
|
||||
|
|
@ -1991,23 +1986,6 @@ extension NavigatorStateExtension on _i46.NavigationService {
|
|||
transition: transition);
|
||||
}
|
||||
|
||||
Future<dynamic> navigateToLearnCourseView({
|
||||
_i37.Key? key,
|
||||
required int id,
|
||||
int? routerId,
|
||||
bool preventDuplicates = true,
|
||||
Map<String, String>? parameters,
|
||||
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
|
||||
transition,
|
||||
}) async {
|
||||
return navigateTo<dynamic>(Routes.learnCourseView,
|
||||
arguments: LearnCourseViewArguments(key: key, id: id),
|
||||
id: routerId,
|
||||
preventDuplicates: preventDuplicates,
|
||||
parameters: parameters,
|
||||
transition: transition);
|
||||
}
|
||||
|
||||
Future<dynamic> navigateToAssessmentView({
|
||||
_i37.Key? key,
|
||||
required Map<String, dynamic> data,
|
||||
|
|
@ -2126,7 +2104,22 @@ extension NavigatorStateExtension on _i46.NavigationService {
|
|||
transition: transition);
|
||||
}
|
||||
|
||||
|
||||
Future<dynamic> navigateToLearnCourseView({
|
||||
_i37.Key? key,
|
||||
required int id,
|
||||
int? routerId,
|
||||
bool preventDuplicates = true,
|
||||
Map<String, String>? parameters,
|
||||
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
|
||||
transition,
|
||||
}) async {
|
||||
return navigateTo<dynamic>(Routes.learnCourseView,
|
||||
arguments: LearnCourseViewArguments(key: key, id: id),
|
||||
id: routerId,
|
||||
preventDuplicates: preventDuplicates,
|
||||
parameters: parameters,
|
||||
transition: transition);
|
||||
}
|
||||
|
||||
Future<dynamic> replaceWithHomeView({
|
||||
_i37.Key? key,
|
||||
|
|
@ -2584,23 +2577,6 @@ extension NavigatorStateExtension on _i46.NavigationService {
|
|||
transition: transition);
|
||||
}
|
||||
|
||||
Future<dynamic> replaceWithLearnCourseView({
|
||||
_i37.Key? key,
|
||||
required int id,
|
||||
int? routerId,
|
||||
bool preventDuplicates = true,
|
||||
Map<String, String>? parameters,
|
||||
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
|
||||
transition,
|
||||
}) async {
|
||||
return replaceWith<dynamic>(Routes.learnCourseView,
|
||||
arguments: LearnCourseViewArguments(key: key, id: id),
|
||||
id: routerId,
|
||||
preventDuplicates: preventDuplicates,
|
||||
parameters: parameters,
|
||||
transition: transition);
|
||||
}
|
||||
|
||||
Future<dynamic> replaceWithAssessmentView({
|
||||
_i37.Key? key,
|
||||
required Map<String, dynamic> data,
|
||||
|
|
@ -2719,5 +2695,20 @@ extension NavigatorStateExtension on _i46.NavigationService {
|
|||
transition: transition);
|
||||
}
|
||||
|
||||
|
||||
Future<dynamic> replaceWithLearnCourseView({
|
||||
_i37.Key? key,
|
||||
required int id,
|
||||
int? routerId,
|
||||
bool preventDuplicates = true,
|
||||
Map<String, String>? parameters,
|
||||
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
|
||||
transition,
|
||||
}) async {
|
||||
return replaceWith<dynamic>(Routes.learnCourseView,
|
||||
arguments: LearnCourseViewArguments(key: key, id: id),
|
||||
id: routerId,
|
||||
preventDuplicates: preventDuplicates,
|
||||
parameters: parameters,
|
||||
transition: transition);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,9 @@ Future<void> main() async {
|
|||
EasyLocalization(
|
||||
supportedLocales: const [
|
||||
Locale('en'),
|
||||
Locale('አማ'),
|
||||
Locale(
|
||||
'am',
|
||||
),
|
||||
],
|
||||
path: 'assets/translations',
|
||||
startLocale: const Locale('en'),
|
||||
|
|
|
|||
19
lib/models/app_update.dart
Normal file
19
lib/models/app_update.dart
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'app_update.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class AppUpdate {
|
||||
@JsonKey(name: 'force_update')
|
||||
final bool? forceUpdate;
|
||||
|
||||
@JsonKey(name: 'update_available')
|
||||
final bool? updateAvailable;
|
||||
|
||||
const AppUpdate({this.forceUpdate, this.updateAvailable});
|
||||
|
||||
factory AppUpdate.fromJson(Map<String, dynamic> json) =>
|
||||
_$AppUpdateFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$AppUpdateToJson(this);
|
||||
}
|
||||
17
lib/models/app_update.g.dart
Normal file
17
lib/models/app_update.g.dart
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'app_update.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
AppUpdate _$AppUpdateFromJson(Map<String, dynamic> json) => AppUpdate(
|
||||
forceUpdate: json['force_update'] as bool?,
|
||||
updateAvailable: json['update_available'] as bool?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$AppUpdateToJson(AppUpdate instance) => <String, dynamic>{
|
||||
'force_update': instance.forceUpdate,
|
||||
'update_available': instance.updateAvailable,
|
||||
};
|
||||
17
lib/models/field_option.dart
Normal file
17
lib/models/field_option.dart
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'field_option.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class FieldOption {
|
||||
final String? code;
|
||||
|
||||
final String? label;
|
||||
|
||||
const FieldOption({this.code, this.label});
|
||||
|
||||
factory FieldOption.fromJson(Map<String, dynamic> json) =>
|
||||
_$FieldOptionFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$FieldOptionToJson(this);
|
||||
}
|
||||
18
lib/models/field_option.g.dart
Normal file
18
lib/models/field_option.g.dart
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'field_option.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
FieldOption _$FieldOptionFromJson(Map<String, dynamic> json) => FieldOption(
|
||||
code: json['code'] as String?,
|
||||
label: json['label'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$FieldOptionToJson(FieldOption instance) =>
|
||||
<String, dynamic>{
|
||||
'code': instance.code,
|
||||
'label': instance.label,
|
||||
};
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:dio/dio.dart';
|
||||
import 'package:yimaru_app/models/app_update.dart';
|
||||
import 'package:yimaru_app/models/learn_lesson.dart';
|
||||
import 'package:yimaru_app/models/learn_practice.dart';
|
||||
import 'package:yimaru_app/models/learn_program.dart';
|
||||
|
|
@ -12,6 +13,7 @@ import 'package:yimaru_app/ui/common/app_constants.dart';
|
|||
import '../app/app.locator.dart';
|
||||
import '../models/course_module.dart';
|
||||
import '../models/course_unit.dart';
|
||||
import '../models/field_option.dart';
|
||||
import '../models/learn_course.dart';
|
||||
import '../models/learn_module.dart';
|
||||
import '../models/learn_question.dart';
|
||||
|
|
@ -379,6 +381,222 @@ class ApiService {
|
|||
}
|
||||
}
|
||||
|
||||
// Get educational levels
|
||||
Future<List<FieldOption>> getEducationalLevels() async {
|
||||
try {
|
||||
List<FieldOption> levels = [];
|
||||
|
||||
final Response response = await _service.dio.get(
|
||||
'$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kFieldOptions?field_key=education_level');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var data = response.data;
|
||||
var decodedData = data['data']['education_level'] as List;
|
||||
levels = decodedData.map(
|
||||
(e) {
|
||||
return FieldOption.fromJson(e);
|
||||
},
|
||||
).toList();
|
||||
return levels;
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Get ethiopia regions
|
||||
Future<List<FieldOption>> getEthiopiaRegions() async {
|
||||
try {
|
||||
List<FieldOption> levels = [];
|
||||
|
||||
final Response response = await _service.dio.get(
|
||||
'$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kFieldOptions?field_key=ethiopia_regions');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var data = response.data;
|
||||
var decodedData = data['data']['ethiopia_regions'] as List;
|
||||
levels = decodedData.map(
|
||||
(e) {
|
||||
return FieldOption.fromJson(e);
|
||||
},
|
||||
).toList();
|
||||
return levels;
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Get language challenges
|
||||
Future<List<FieldOption>> getLanguageChallenges() async {
|
||||
try {
|
||||
List<FieldOption> levels = [];
|
||||
|
||||
final Response response = await _service.dio.get(
|
||||
'$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kFieldOptions?field_key=language_challange');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var data = response.data;
|
||||
var decodedData = data['data']['language_challange'] as List;
|
||||
levels = decodedData.map(
|
||||
(e) {
|
||||
return FieldOption.fromJson(e);
|
||||
},
|
||||
).toList();
|
||||
return levels;
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Get occupations
|
||||
Future<List<FieldOption>> getOccupations() async {
|
||||
try {
|
||||
List<FieldOption> levels = [];
|
||||
|
||||
final Response response = await _service.dio.get(
|
||||
'$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kFieldOptions?field_key=occupation');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var data = response.data;
|
||||
var decodedData = data['data']['occupation'] as List;
|
||||
levels = decodedData.map(
|
||||
(e) {
|
||||
return FieldOption.fromJson(e);
|
||||
},
|
||||
).toList();
|
||||
return levels;
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Get age group
|
||||
Future<List<FieldOption>> getAgeGroups() async {
|
||||
try {
|
||||
List<FieldOption> levels = [];
|
||||
|
||||
final Response response = await _service.dio.get(
|
||||
'$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kFieldOptions?field_key=age_group');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var data = response.data;
|
||||
var decodedData = data['data']['age_group'] as List;
|
||||
levels = decodedData.map(
|
||||
(e) {
|
||||
return FieldOption.fromJson(e);
|
||||
},
|
||||
).toList();
|
||||
return levels;
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Get countries
|
||||
Future<List<FieldOption>> getCountries() async {
|
||||
try {
|
||||
List<FieldOption> levels = [];
|
||||
|
||||
final Response response = await _service.dio.get(
|
||||
'$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kFieldOptions?field_key=country');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var data = response.data;
|
||||
var decodedData = data['data']['country'] as List;
|
||||
levels = decodedData.map(
|
||||
(e) {
|
||||
return FieldOption.fromJson(e);
|
||||
},
|
||||
).toList();
|
||||
return levels;
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Get topics
|
||||
Future<List<FieldOption>> getTopics() async {
|
||||
try {
|
||||
List<FieldOption> levels = [];
|
||||
|
||||
final Response response = await _service.dio.get(
|
||||
'$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kFieldOptions?field_key=favourite_topic');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var data = response.data;
|
||||
var decodedData = data['data']['favourite_topic'] as List;
|
||||
levels = decodedData.map(
|
||||
(e) {
|
||||
return FieldOption.fromJson(e);
|
||||
},
|
||||
).toList();
|
||||
return levels;
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Get topics
|
||||
Future<List<FieldOption>> getLanguageGoals() async {
|
||||
try {
|
||||
List<FieldOption> levels = [];
|
||||
|
||||
final Response response = await _service.dio.get(
|
||||
'$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kFieldOptions?field_key=language_goal');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var data = response.data;
|
||||
var decodedData = data['data']['language_goal'] as List;
|
||||
levels = decodedData.map(
|
||||
(e) {
|
||||
return FieldOption.fromJson(e);
|
||||
},
|
||||
).toList();
|
||||
return levels;
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Get learning goal
|
||||
Future<List<FieldOption>> getLearningGoals() async {
|
||||
try {
|
||||
List<FieldOption> levels = [];
|
||||
|
||||
final Response response = await _service.dio.get(
|
||||
'$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kFieldOptions?field_key=learning_goal');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var data = response.data;
|
||||
var decodedData = data['data']['learning_goal'] as List;
|
||||
levels = decodedData.map(
|
||||
(e) {
|
||||
return FieldOption.fromJson(e);
|
||||
},
|
||||
).toList();
|
||||
return levels;
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Get assessment questions
|
||||
Future<List<AssessmentQuestion>> getAssessmentQuestions(int id) async {
|
||||
try {
|
||||
|
|
@ -795,4 +1013,29 @@ class ApiService {
|
|||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Check update
|
||||
Future<Map<String, dynamic>> checkUpdate(Map<String, dynamic> data) async {
|
||||
try {
|
||||
Response response = await _service.dio.get(
|
||||
'$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kAppUrl/$kApiVersionUrl/$kCheckUrl',
|
||||
data: data,
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return {
|
||||
'status': ResponseStatus.success,
|
||||
'data': AppUpdate.fromJson(response.data['data']),
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
'status': ResponseStatus.failure,
|
||||
};
|
||||
}
|
||||
} on DioException {
|
||||
return {
|
||||
'status': ResponseStatus.failure,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,62 +1,51 @@
|
|||
import 'package:battery_plus/battery_plus.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:in_app_update/in_app_update.dart';
|
||||
import 'package:stacked_services/stacked_services.dart';
|
||||
import 'package:storage_info/storage_info.dart';
|
||||
import 'package:yimaru_app/app/app.locator.dart';
|
||||
import 'package:yimaru_app/models/app_update.dart';
|
||||
import 'package:yimaru_app/services/status_checker_service.dart';
|
||||
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||
|
||||
import '../ui/common/ui_helpers.dart';
|
||||
import 'api_service.dart';
|
||||
|
||||
class InAppUpdateService {
|
||||
// Dependency Injection
|
||||
final _apiService = locator<ApiService>();
|
||||
|
||||
final _navigationService = locator<NavigationService>();
|
||||
|
||||
Future<int> getBatteryLevel() async {
|
||||
final battery = Battery();
|
||||
final batteryLevel = await battery.batteryLevel;
|
||||
return batteryLevel;
|
||||
}
|
||||
|
||||
Future<int> getAvailableStorage() async {
|
||||
try {
|
||||
final availableStorage =
|
||||
await StorageInfo().getStorageFreeSpace(SpaceUnit.Bytes);
|
||||
return availableStorage.toInt(); // Convert GB to bytes
|
||||
} catch (e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
final _statusCheckerService = locator<StatusCheckerService>();
|
||||
|
||||
Future<void> checkForUpdate() async {
|
||||
const requiredStorage = 500 * 1024 * 1024;
|
||||
|
||||
final batteryLevel =
|
||||
await getBatteryLevel(); // Implement getBatteryLevel function
|
||||
final int storageAvailable =
|
||||
await getAvailableStorage(); // Implement getAvailableStorage
|
||||
if (batteryLevel < 20 || storageAvailable < requiredStorage) {
|
||||
if (batteryLevel < 20 || storageAvailable < requiredStorage) {
|
||||
// KewedeConst().showErrorToast(
|
||||
// 'Unable to update app, please charge your phone & free up space.');
|
||||
} else if (batteryLevel < 20) {
|
||||
// KewedeConst()
|
||||
// .showErrorToast('Unable to update app, please charge your phone.');
|
||||
} else if (storageAvailable < requiredStorage) {
|
||||
// KewedeConst()
|
||||
// .showErrorToast('Unable to update app, please free up space.');
|
||||
}
|
||||
// Show user-friendly message explaining why update failed and suggesting solutions (e.g., charge device, free up space)
|
||||
}
|
||||
try {
|
||||
final info = await InAppUpdate.checkForUpdate();
|
||||
if (info.updateAvailability == UpdateAvailability.updateAvailable) {
|
||||
AppUpdateResult result = await InAppUpdate.performImmediateUpdate();
|
||||
final String version = await _statusCheckerService.getAppVersion();
|
||||
|
||||
if (version != info.availableVersionCode.toString()) {
|
||||
Map<String, dynamic> data = {
|
||||
'platform': 'ANDROID',
|
||||
'version_code': info.availableVersionCode
|
||||
};
|
||||
Map<String, dynamic> response = await _apiService.checkUpdate(data);
|
||||
|
||||
if (response['status'] == ResponseStatus.success) {
|
||||
AppUpdate update = response['data'] as AppUpdate;
|
||||
if (update.updateAvailable ?? false) {
|
||||
if (update.forceUpdate ?? false) {
|
||||
AppUpdateResult result =
|
||||
await InAppUpdate.performImmediateUpdate();
|
||||
if (result == AppUpdateResult.userDeniedUpdate) {
|
||||
showErrorToast('An update is required to continue using this app.');
|
||||
showErrorToast(
|
||||
'An update is required to continue using this app.');
|
||||
_navigationService.back();
|
||||
}
|
||||
} else {
|
||||
await InAppUpdate.startFlexibleUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ... rest of your update logic ...
|
||||
} on PlatformException {
|
||||
// Handle specific error code for better user experience and potentially different error messages for each issue
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ class LocalizationService with ListenableServiceMixin {
|
|||
Map<String, dynamic> get selectedLanguage => _selectedLanguage;
|
||||
|
||||
final List<Map<String, dynamic>> _languages = [
|
||||
{'code': 'አማ', 'language': 'አማርኛ'},
|
||||
{'code': 'am', 'language': 'አማርኛ'},
|
||||
{'code': 'en', 'language': 'English'},
|
||||
];
|
||||
|
||||
|
|
@ -37,7 +37,7 @@ class LocalizationService with ListenableServiceMixin {
|
|||
required Map<String, dynamic> title}) async {
|
||||
_selectedLanguage = title;
|
||||
|
||||
if (title['code'] == 'አማ') {
|
||||
if (title['code'] == 'am') {
|
||||
await setAmharicLanguage(context);
|
||||
} else {
|
||||
await setEnglishLanguage(context);
|
||||
|
|
@ -50,17 +50,15 @@ class LocalizationService with ListenableServiceMixin {
|
|||
|
||||
if (language == 'en') {
|
||||
_selectedLanguage = {'code': 'en', 'language': 'English'};
|
||||
|
||||
} else {
|
||||
_selectedLanguage = {'code': 'አማ', 'language': 'አማርኛ'};
|
||||
_selectedLanguage = {'code': 'am', 'language': 'አማርኛ'};
|
||||
}
|
||||
notifyListeners();
|
||||
print('SELECTED LANGUAGE: $language $_selectedLanguage');
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> setAmharicLanguage(BuildContext context) async {
|
||||
await context.setLocale(const Locale('አማ'));
|
||||
await _secureService.setString('language', 'አማ');
|
||||
await context.setLocale(const Locale('am'));
|
||||
await _secureService.setString('language', 'am');
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
|
|
|
|||
104
lib/services/onboarding_service.dart
Normal file
104
lib/services/onboarding_service.dart
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/models/field_option.dart';
|
||||
|
||||
import '../app/app.locator.dart';
|
||||
import 'api_service.dart';
|
||||
|
||||
class OnboardingService with ListenableServiceMixin {
|
||||
// Dependency injection
|
||||
final _apiService = locator<ApiService>();
|
||||
|
||||
// Initialization
|
||||
learnService() {
|
||||
listenToReactiveValues([
|
||||
_topics,
|
||||
_regions,
|
||||
_ageGroups,
|
||||
_countries,
|
||||
_challenges,
|
||||
_occupations,
|
||||
_learningGoals,
|
||||
_languageGoals,
|
||||
_educationalBackgrounds
|
||||
]);
|
||||
}
|
||||
|
||||
// Topics
|
||||
List<FieldOption> _topics = [];
|
||||
|
||||
List<FieldOption> get topics => _topics;
|
||||
|
||||
// Regions
|
||||
List<FieldOption> _regions = [];
|
||||
|
||||
List<FieldOption> get regions => _regions;
|
||||
|
||||
// Age groups
|
||||
List<FieldOption> _ageGroups = [];
|
||||
|
||||
List<FieldOption> get ageGroups => _ageGroups;
|
||||
|
||||
// Countries
|
||||
List<FieldOption> _countries = [];
|
||||
|
||||
List<FieldOption> get countries => _countries;
|
||||
|
||||
// Challenges
|
||||
List<FieldOption> _challenges = [];
|
||||
|
||||
List<FieldOption> get challenges => _challenges;
|
||||
|
||||
// Occupations
|
||||
List<FieldOption> _occupations = [];
|
||||
|
||||
List<FieldOption> get occupations => _occupations;
|
||||
|
||||
// Learning goals
|
||||
List<FieldOption> _learningGoals = [];
|
||||
|
||||
List<FieldOption> get learningGoals => _learningGoals;
|
||||
|
||||
// Language goals
|
||||
List<FieldOption> _languageGoals = [];
|
||||
|
||||
List<FieldOption> get languageGoals => _languageGoals;
|
||||
|
||||
// Educational backgrounds
|
||||
List<FieldOption> _educationalBackgrounds = [];
|
||||
|
||||
List<FieldOption> get educationalBackgrounds => _educationalBackgrounds;
|
||||
|
||||
// Onboarding fields
|
||||
Future<bool> getOnboardingFields() async {
|
||||
_topics = await _apiService.getTopics();
|
||||
|
||||
_ageGroups = await _apiService.getAgeGroups();
|
||||
|
||||
_countries = await _apiService.getCountries();
|
||||
|
||||
_occupations = await _apiService.getOccupations();
|
||||
|
||||
_regions = await _apiService.getEthiopiaRegions();
|
||||
|
||||
_learningGoals = await _apiService.getLearningGoals();
|
||||
|
||||
_languageGoals = await _apiService.getLanguageGoals();
|
||||
|
||||
_challenges = await _apiService.getLanguageChallenges();
|
||||
|
||||
_educationalBackgrounds = await _apiService.getEducationalLevels();
|
||||
notifyListeners();
|
||||
|
||||
if (_topics.isNotEmpty &&
|
||||
_ageGroups.isNotEmpty &&
|
||||
_countries.isNotEmpty &&
|
||||
_occupations.isNotEmpty &&
|
||||
_regions.isNotEmpty &&
|
||||
_learningGoals.isNotEmpty &&
|
||||
_challenges.isNotEmpty &&
|
||||
_educationalBackgrounds.isNotEmpty) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:internet_connection_checker_plus/internet_connection_checker_plus.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:yimaru_app/services/secure_storage_service.dart';
|
||||
|
||||
import '../app/app.locator.dart';
|
||||
|
|
@ -25,4 +26,16 @@ class StatusCheckerService {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Get app version
|
||||
Future<String> getAppVersion() async {
|
||||
PackageInfo packageInfo = await PackageInfo.fromPlatform();
|
||||
|
||||
String version = packageInfo.version; // e.g. 1.0.0
|
||||
String buildNumber = packageInfo.buildNumber; // version code
|
||||
|
||||
print("Version: $version");
|
||||
print("Build Number: $buildNumber");
|
||||
return buildNumber;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
// Endpoints
|
||||
String kBaseUrl = 'https://api.yimaruacademy.com';
|
||||
|
||||
String kAppUrl = 'app';
|
||||
|
||||
String kApiUrl = 'api';
|
||||
|
||||
String kUnitsUrl = 'units';
|
||||
|
||||
String kCheckUrl = 'check';
|
||||
|
||||
String kApiVersionUrl = 'v1';
|
||||
|
||||
String kLevelsUrl = 'levels';
|
||||
|
|
@ -15,6 +19,8 @@ String kModulesUrl = 'modules';
|
|||
|
||||
String kLessonsUrl = 'lessons';
|
||||
|
||||
String kVersionUrl = 'version';
|
||||
|
||||
String kProgramsUrl = 'programs';
|
||||
|
||||
String kRegisterUrl = 'register';
|
||||
|
|
@ -47,6 +53,8 @@ String kSubmodulesUrl = 'sub-modules';
|
|||
|
||||
String kSubcoursesUrl = 'sub-courses';
|
||||
|
||||
String kFieldOptions = 'field-options';
|
||||
|
||||
String kResetPassword = 'resetPassword';
|
||||
|
||||
String kQuestionSetsUrl = 'question-sets';
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class CodegenLoader extends AssetLoader{
|
|||
"loading": "በመጫን ላይ",
|
||||
"welcome_back": "እንኳን በደህና ተመለሱ",
|
||||
"checking_user_info": "የተጠቃሚ መረጃን በማረጋገጥ ላይ",
|
||||
"dont_have_account": "መለያ የለዎትም? ይመዝገቡ",
|
||||
"dont_have_account": "መለያ የለዎትም?",
|
||||
"email": "ኢሜይል",
|
||||
"password": "የይለፍ ቃል",
|
||||
"forgot_password": "የይለፍ ቃል ረሱ?",
|
||||
|
|
@ -59,14 +59,14 @@ class CodegenLoader extends AssetLoader{
|
|||
"continue_learning": "መማርን ይቀጥሉ",
|
||||
"start_learning": "ትምህርትን ይጀምሩ",
|
||||
"completed": "ተጠናቋል",
|
||||
"take_practice": " ልምምድ ያድርጉ",
|
||||
"take_practice": "ልምምድ ያድርጉ",
|
||||
"your_current_level": "የአሁኑ ደረጃዎ",
|
||||
"overall_progress": "አጠቃላይ እድገት",
|
||||
"great_work": "በርቱ! በጣም ጥሩ እየሰሩ ነው",
|
||||
"view_module": "ሞጁሉን ይመልከቱ",
|
||||
"progress": "እድገት",
|
||||
"keep_going": " ይቀጥሉ - ከግማሽ በላይ ጨርሰዋል ",
|
||||
"lessons_in_module": " በዚህ ሞጁል ውስጥ ያሉ ትምህርቶች ",
|
||||
"keep_going": "ይቀጥሉ - ከግማሽ በላይ ጨርሰዋል ",
|
||||
"lessons_in_module": "በዚህ ሞጁል ውስጥ ያሉ ትምህርቶች ",
|
||||
"practice": "ልምምድ",
|
||||
"start": "ጀምር",
|
||||
"in_progress": "በሂደት ላይ",
|
||||
|
|
@ -150,7 +150,7 @@ class CodegenLoader extends AssetLoader{
|
|||
"ask_you_few_actions": "ጥቂት ጥያቄዎችን እጠይቅሃለሁ፣ አንተም በተፈጥሮ መልስ ልትሰጥ ትችላለህ።",
|
||||
"begin_level_practice": "የደረጃ ልምምድን ጀምር",
|
||||
"lets_practice_course": "የኮርሱን ልምምድ እንለማመድ",
|
||||
"lets_quick_practice": "በዚህ ደረጃ የተማርከውን በፍጥነት እንከልስ!",
|
||||
"lets_quick_review": "በዚህ ደረጃ የተማርከውን በፍጥነት እንከልስ!",
|
||||
"speaking": "እየተናገረ ነው",
|
||||
"you_have_finished_practice": "ልምምድህን አጠናቀቅህ",
|
||||
"view_results": "ውጤቶቼን እይ",
|
||||
|
|
@ -169,13 +169,42 @@ class CodegenLoader extends AssetLoader{
|
|||
"what_should_we_call_you": "ምን ብለን እንጠራህ?",
|
||||
"name_for_personalization": "በመማር ጉዞህ ውስጥ ለግል ለማድረግ ስምህን እንጠቀማለን።",
|
||||
"choose_your_gender": "ጾታህን ምረጥ",
|
||||
"gender_for_personalization": "በጾታህ መሰረት የመማር ተሞክሮህን እናበጅለታለን።"
|
||||
"gender_for_personalization": "በጾታህ መሰረት የመማር ተሞክሮህን እናበጅለታለን።",
|
||||
"age_range": "በየትኛው የእድሜ ክልል ውስጥ ነህ?",
|
||||
"age_for_personalization": "በእድሜህ መሰረት የመማር ተሞክሮህን እናበጅለታለን።",
|
||||
"educational_background": "አሁን ያለህ የትምህርት ደረጃ ምንድን ነው?",
|
||||
"education_for_personalization": "ይህ ትምህርቶችን ከልምድህ ጋር እንዲስማሙ ለማድረግ ይረዳናል።",
|
||||
"your_occupation": "ስራህ ምንድን ነው?",
|
||||
"occupation_for_personalization": "በስራህ መሰረት የመማር ተሞክሮህን እናበጅለታለን።",
|
||||
"location": "ከየት ነህ?",
|
||||
"select_country_region": "አገርህን እና ክልልህን ከተቆልቋይ ዝርዝሩ ምረጥ",
|
||||
"select_country": "አገር ምረጥ",
|
||||
"learning_goal": "የመማር ዓላማህን ምረጥ",
|
||||
"language_goal": "እንግሊዝኛህን ለማሻሻል ዋና ዓላማህ ምንድን ነው?",
|
||||
"your_goal": "ዓላማህ የመማር ጉዞህን እንዲስማማ ለማድረግ ይረዳናል።",
|
||||
"write_your_goal": "ዓላማህን ጻፍ…",
|
||||
"challenge_you_face": "What challenge do you face most with English?",
|
||||
"evey_one_has_strugle": "ሁሉም ሰው ችግሮች አሉት፣ የአንተን እንጀምር እንፍታ",
|
||||
"write_your_challenge": "ችግርህን ጻፍ…",
|
||||
"topic_interest": "በጣም የሚስቡህ ርዕሶች የትኞቹ ናቸው?",
|
||||
"favourite_topic": "የምትወዳቸው ርዕሶች አስደሳች እና ከሕይወትህ ጋር የተዛመዱ ትምህርቶችን ለመፍጠር ይረዱናል።",
|
||||
"your_interest": "ፍላጎትህን ጻፍ…",
|
||||
"want_quick_assessment": "የእንግሊዝኛ ደረጃህን ለማወቅ ፈጣን ግምገማ ትፈልጋለህ?",
|
||||
"answer_quick_questions": "የእንግሊዝኛ ችሎታህን ለመረዳት ጥቂት ፈጣን ጥያቄዎችን መልስ።",
|
||||
"skip": "ዝለል",
|
||||
"finish_level": "ደረጃውን አጠናቅቅ",
|
||||
"likely_speaker": "አንተ ምናልባት ተናጋሪ ነህ",
|
||||
"great_job": "በጣም ጥሩ ስራ! ለመሻሻል ቀጣዩ ደረጃህ ይኸው ነው።",
|
||||
"lets_start_practice": "ልምምድህን እንጀምር",
|
||||
"welcome_abroad": "እንኳን ደህና መጣህ",
|
||||
"ready_to_explore": "የግል ትምህርቶችህን ለማሰስ ዝግጁ ነህ።",
|
||||
"finish": "አጠናቅቅ"
|
||||
};
|
||||
static const Map<String,dynamic> _en = {
|
||||
"loading": "Loading",
|
||||
"welcome_back": "Welcome back",
|
||||
"checking_user_info": "Checking user info",
|
||||
"dont_have_account": "Don't have an account? Register",
|
||||
"dont_have_account": "Don't have an account?",
|
||||
"email": "Email",
|
||||
"password": "Password",
|
||||
"forgot_password": "Forgot password?",
|
||||
|
|
@ -326,7 +355,36 @@ static const Map<String,dynamic> _en = {
|
|||
"what_should_we_call_you": "What should we call you?",
|
||||
"name_for_personalization": "We’ll use your name to personalize your learning journey.",
|
||||
"choose_your_gender": "Choose your gender?",
|
||||
"gender_for_personalization": "We’ll personalize your learning experience based on your gender."
|
||||
"gender_for_personalization": "We’ll personalize your learning experience based on your gender.",
|
||||
"age_range": "Which age range are you in?",
|
||||
"age_for_personalization": "We’ll personalize your learning experience based on your age.",
|
||||
"educational_background": "What’s your current educational level?",
|
||||
"education_for_personalization": "This helps us tailor your lessons to your experience.",
|
||||
"your_occupation": "What’s your occupation?",
|
||||
"occupation_for_personalization": "We’ll personalize your learning experience based on your occupation.",
|
||||
"location": "Where are you from?",
|
||||
"select_country_region": "Select your country and region from the dropdown",
|
||||
"select_country": "Select country",
|
||||
"learning_goal": "Choose your learning goal.",
|
||||
"language_goal": "What’s your main goal for improving your English?",
|
||||
"your_goal": "Your goal helps us tailor your learning journey.",
|
||||
"write_your_goal": "Write your goal…",
|
||||
"challenge_you_face": "What challenge do you face most with English?",
|
||||
"evey_one_has_strugle": "Everyone has struggles, let’s start fixing yours",
|
||||
"write_your_challenge": "Write your challenge…",
|
||||
"topic_interest": "Which topics interest you most?",
|
||||
"favourite_topic": "Your favorite topics help us create fun, relatable lessons.",
|
||||
"your_interest": "Write your interest…",
|
||||
"want_quick_assessment": "Want a quick assessment to know your English level?",
|
||||
"answer_quick_questions": "Answer a few quick questions to help us understand your English proficiency.",
|
||||
"skip": "Skip",
|
||||
"finish_level": "Finish Level",
|
||||
"likely_speaker": "You’re likely speaker of",
|
||||
"great_job": "Great Job! Here’s your next step to keep improving.",
|
||||
"lets_start_practice": "Let's start your practice",
|
||||
"welcome_abroad": "Welcome aboard",
|
||||
"ready_to_explore": "You’re ready to explore your personalized lessons.",
|
||||
"finish": "Finish"
|
||||
};
|
||||
static const Map<String, Map<String,dynamic>> mapLocales = {"am": _am, "en": _en};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ abstract class LocaleKeys {
|
|||
static const ask_you_few_actions = 'ask_you_few_actions';
|
||||
static const begin_level_practice = 'begin_level_practice';
|
||||
static const lets_practice_course = 'lets_practice_course';
|
||||
static const lets_quick_practice = 'lets_quick_practice';
|
||||
static const lets_quick_review = 'lets_quick_review';
|
||||
static const speaking = 'speaking';
|
||||
static const you_have_finished_practice = 'you_have_finished_practice';
|
||||
static const view_results = 'view_results';
|
||||
|
|
@ -156,5 +156,34 @@ abstract class LocaleKeys {
|
|||
static const name_for_personalization = 'name_for_personalization';
|
||||
static const choose_your_gender = 'choose_your_gender';
|
||||
static const gender_for_personalization = 'gender_for_personalization';
|
||||
static const age_range = 'age_range';
|
||||
static const age_for_personalization = 'age_for_personalization';
|
||||
static const educational_background = 'educational_background';
|
||||
static const education_for_personalization = 'education_for_personalization';
|
||||
static const your_occupation = 'your_occupation';
|
||||
static const occupation_for_personalization = 'occupation_for_personalization';
|
||||
static const location = 'location';
|
||||
static const select_country_region = 'select_country_region';
|
||||
static const select_country = 'select_country';
|
||||
static const learning_goal = 'learning_goal';
|
||||
static const language_goal = 'language_goal';
|
||||
static const your_goal = 'your_goal';
|
||||
static const write_your_goal = 'write_your_goal';
|
||||
static const challenge_you_face = 'challenge_you_face';
|
||||
static const evey_one_has_strugle = 'evey_one_has_strugle';
|
||||
static const write_your_challenge = 'write_your_challenge';
|
||||
static const topic_interest = 'topic_interest';
|
||||
static const favourite_topic = 'favourite_topic';
|
||||
static const your_interest = 'your_interest';
|
||||
static const want_quick_assessment = 'want_quick_assessment';
|
||||
static const answer_quick_questions = 'answer_quick_questions';
|
||||
static const skip = 'skip';
|
||||
static const finish_level = 'finish_level';
|
||||
static const likely_speaker = 'likely_speaker';
|
||||
static const great_job = 'great_job';
|
||||
static const lets_start_practice = 'lets_start_practice';
|
||||
static const welcome_abroad = 'welcome_abroad';
|
||||
static const ready_to_explore = 'ready_to_explore';
|
||||
static const finish = 'finish';
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@ class AccountPrivacyViewModel extends ReactiveViewModel {
|
|||
final _localizationService = locator<LocalizationService>();
|
||||
|
||||
@override
|
||||
List<ListenableServiceMixin> get listenableServices =>
|
||||
[ _localizationService];
|
||||
List<ListenableServiceMixin> get listenableServices => [_localizationService];
|
||||
|
||||
// Languages
|
||||
Map<String, dynamic> get _selectedLanguage =>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import 'package:easy_localization/easy_localization.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/translations/locale_keys.g.dart';
|
||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||
import 'package:yimaru_app/ui/widgets/large_app_bar.dart';
|
||||
|
|
@ -67,17 +69,19 @@ class AssessmentIntroScreen extends ViewModelWidget<AssessmentViewModel> {
|
|||
showBackButton: true,
|
||||
onPop: viewModel.goBack,
|
||||
showLanguageSelection: true,
|
||||
language: viewModel.selectedLanguage['code'],
|
||||
onLanguage: () async => await viewModel.navigateToLanguage(),
|
||||
language: viewModel.selectedLanguage['code'] == 'am'
|
||||
? 'አማ'
|
||||
: viewModel.selectedLanguage['code'],
|
||||
);
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
'Want a quick assessment to know your English level?',
|
||||
LocaleKeys.want_quick_assessment.tr(),
|
||||
style: style25DG600,
|
||||
);
|
||||
|
||||
Widget _buildSubtitle() => Text(
|
||||
'Answer a few quick questions to help us understand your English proficiency.',
|
||||
LocaleKeys.answer_quick_questions.tr(),
|
||||
style: style14MG400,
|
||||
);
|
||||
|
||||
|
|
@ -96,9 +100,9 @@ class AssessmentIntroScreen extends ViewModelWidget<AssessmentViewModel> {
|
|||
CustomElevatedButton(
|
||||
height: 55,
|
||||
safe: false,
|
||||
text: 'Continue',
|
||||
borderRadius: 12,
|
||||
foregroundColor: kcWhite,
|
||||
text: LocaleKeys.cont.tr(),
|
||||
backgroundColor: kcPrimaryColor,
|
||||
onTap: () async => await _next(viewModel),
|
||||
);
|
||||
|
|
@ -111,9 +115,9 @@ class AssessmentIntroScreen extends ViewModelWidget<AssessmentViewModel> {
|
|||
Widget _buildSkipButton(AssessmentViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
text: 'Skip',
|
||||
borderRadius: 12,
|
||||
backgroundColor: kcWhite,
|
||||
text: LocaleKeys.skip.tr(),
|
||||
borderColor: kcPrimaryColor,
|
||||
foregroundColor: kcPrimaryColor,
|
||||
onTap: () => viewModel.next(page: 3),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import 'package:easy_localization/easy_localization.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/translations/locale_keys.g.dart';
|
||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||
import 'package:yimaru_app/ui/widgets/custom_small_radio_button.dart';
|
||||
|
|
@ -134,8 +136,8 @@ class AssessmentQuestionsScreen extends ViewModelWidget<AssessmentViewModel> {
|
|||
foregroundColor: kcWhite,
|
||||
text: viewModel.currentQuestionIndex ==
|
||||
viewModel.assessmentQuestions.length - 1
|
||||
? 'Finish Level'
|
||||
: 'Continue',
|
||||
? LocaleKeys.finish_level.tr()
|
||||
: LocaleKeys.cont.tr(),
|
||||
backgroundColor: viewModel.selectedAnswers
|
||||
.containsKey('${viewModel.currentQuestionIndex + 1}')
|
||||
? kcPrimaryColor
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
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/translations/locale_keys.g.dart';
|
||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||
import 'package:yimaru_app/ui/widgets/large_app_bar.dart';
|
||||
|
|
@ -35,8 +37,10 @@ class AssessmentResultScreen extends ViewModelWidget<AssessmentViewModel> {
|
|||
showBackButton: true,
|
||||
onPop: viewModel.goBack,
|
||||
showLanguageSelection: true,
|
||||
language: viewModel.selectedLanguage['code'],
|
||||
onLanguage: () async => await viewModel.navigateToLanguage(),
|
||||
language: viewModel.selectedLanguage['code'] == 'am'
|
||||
? 'አማ'
|
||||
: viewModel.selectedLanguage['code'],
|
||||
);
|
||||
|
||||
Widget _buildExpandedBody(AssessmentViewModel viewModel) =>
|
||||
|
|
@ -75,13 +79,13 @@ class AssessmentResultScreen extends ViewModelWidget<AssessmentViewModel> {
|
|||
];
|
||||
|
||||
Widget _buildTitle(AssessmentViewModel viewModel) => Text(
|
||||
'You’re likely a ${viewModel.proficiencyLevel?.toUpperCase()} speaker!',
|
||||
'${LocaleKeys.likely_speaker.tr()} ${viewModel.proficiencyLevel?.toUpperCase()}',
|
||||
style: style25DG600,
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
||||
Widget _buildPrimarySubtitle() => Text(
|
||||
'Great Job! Here’s your next step to keep improving.',
|
||||
LocaleKeys.great_job.tr(),
|
||||
style: style14MG400,
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
|
@ -93,7 +97,7 @@ class AssessmentResultScreen extends ViewModelWidget<AssessmentViewModel> {
|
|||
'assets/icons/${viewModel.proficiencyLevel?.substring(0, 1).toLowerCase()}_${viewModel.proficiencyLevel?.substring(1).toLowerCase()}.svg');
|
||||
|
||||
Widget _buildSecondarySubtitle() => Text(
|
||||
'Let\'s start your practice',
|
||||
LocaleKeys.lets_start_practice.tr(),
|
||||
style: style14DG400,
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
|
@ -106,9 +110,9 @@ class AssessmentResultScreen extends ViewModelWidget<AssessmentViewModel> {
|
|||
Widget _buildContinueButton(AssessmentViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
text: 'Continue',
|
||||
borderRadius: 12,
|
||||
foregroundColor: kcWhite,
|
||||
text: LocaleKeys.cont.tr(),
|
||||
onTap: () => viewModel.next(),
|
||||
backgroundColor: kcPrimaryColor,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
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/translations/locale_keys.g.dart';
|
||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||
import 'package:yimaru_app/ui/widgets/large_app_bar.dart';
|
||||
|
|
@ -52,8 +54,10 @@ class StartLessonScreen extends ViewModelWidget<AssessmentViewModel> {
|
|||
showBackButton: true,
|
||||
onPop: viewModel.goBack,
|
||||
showLanguageSelection: true,
|
||||
language: viewModel.selectedLanguage['code'],
|
||||
onLanguage: () async => await viewModel.navigateToLanguage(),
|
||||
language: viewModel.selectedLanguage['code'] == 'am'
|
||||
? 'አማ'
|
||||
: viewModel.selectedLanguage['code'],
|
||||
);
|
||||
|
||||
Widget _buildExpandedBody(AssessmentViewModel viewModel) =>
|
||||
|
|
@ -92,7 +96,7 @@ class StartLessonScreen extends ViewModelWidget<AssessmentViewModel> {
|
|||
|
||||
Widget _buildTitle(AssessmentViewModel viewModel) => Text.rich(
|
||||
TextSpan(
|
||||
text: 'Welcome aboard',
|
||||
text: LocaleKeys.welcome_abroad.tr(),
|
||||
style: style25DG600,
|
||||
children: [
|
||||
TextSpan(
|
||||
|
|
@ -104,7 +108,7 @@ class StartLessonScreen extends ViewModelWidget<AssessmentViewModel> {
|
|||
);
|
||||
|
||||
Widget _buildSubtitle() => Text(
|
||||
'You’re ready to explore your personalized lessons.',
|
||||
LocaleKeys.ready_to_explore.tr(),
|
||||
style: style14MG400,
|
||||
);
|
||||
|
||||
|
|
@ -116,9 +120,9 @@ class StartLessonScreen extends ViewModelWidget<AssessmentViewModel> {
|
|||
Widget _buildContinueButton(AssessmentViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
text: 'Finish',
|
||||
borderRadius: 12,
|
||||
foregroundColor: kcWhite,
|
||||
text: LocaleKeys.finish.tr(),
|
||||
backgroundColor: kcPrimaryColor,
|
||||
onTap: () async => await _start(viewModel),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ class CallSupportView extends StackedView<CallSupportViewModel> {
|
|||
leadingIcon: Icons.call,
|
||||
foregroundColor: kcWhite,
|
||||
backgroundColor: kcPrimaryColor,
|
||||
text:LocaleKeys.tap_to_call.tr(),
|
||||
text: LocaleKeys.tap_to_call.tr(),
|
||||
onTap: () async => await viewModel.callSupport(),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,8 +96,10 @@ class RequestCodeScreen extends ViewModelWidget<ForgetPasswordViewModel> {
|
|||
showBackButton: true,
|
||||
showLanguageSelection: true,
|
||||
onPop: () => _inAppPop(viewModel),
|
||||
language: viewModel.selectedLanguage['code'],
|
||||
onLanguage: () async => await viewModel.navigateToLanguage(),
|
||||
language: viewModel.selectedLanguage['code'] == 'am'
|
||||
? 'አማ'
|
||||
: viewModel.selectedLanguage['code'],
|
||||
);
|
||||
|
||||
Widget _buildExpandedBody(
|
||||
|
|
|
|||
|
|
@ -96,8 +96,10 @@ class ResetPasswordScreen extends ViewModelWidget<ForgetPasswordViewModel> {
|
|||
showBackButton: true,
|
||||
showLanguageSelection: true,
|
||||
onPop: () => _inAppPop(viewModel),
|
||||
language: viewModel.selectedLanguage['code'],
|
||||
onLanguage: () async => await viewModel.navigateToLanguage(),
|
||||
language: viewModel.selectedLanguage['code'] == 'am'
|
||||
? 'አማ'
|
||||
: viewModel.selectedLanguage['code'],
|
||||
);
|
||||
|
||||
Widget _buildExpandedBody(ForgetPasswordViewModel viewModel) =>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import 'package:yimaru_app/ui/views/learn_program/learn_program_view.dart';
|
|||
import 'package:yimaru_app/ui/views/profile/profile_view.dart';
|
||||
|
||||
import '../../widgets/coming_soon.dart';
|
||||
import '../course/course_view.dart';
|
||||
import 'home_viewmodel.dart';
|
||||
|
||||
class HomeView extends StackedView<HomeViewModel> {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_carousel_widget/flutter_carousel_widget.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||
import 'package:yimaru_app/ui/views/landing/screens/first_landing_screen.dart';
|
||||
import 'package:yimaru_app/ui/views/landing/screens/fourth_landing_screen.dart';
|
||||
import 'package:yimaru_app/ui/views/landing/screens/second_landing_screen.dart';
|
||||
|
|
@ -23,19 +24,21 @@ class LandingView extends StackedView<LandingViewModel> {
|
|||
LandingViewModel viewModel,
|
||||
Widget? child,
|
||||
) =>
|
||||
_buildLandingScreens(viewModel);
|
||||
_buildLandingScreensWrapper(viewModel);
|
||||
|
||||
Widget _buildLandingScreensWrapper(LandingViewModel viewModel) => Scaffold(
|
||||
backgroundColor: kcPrimaryColor,
|
||||
body: _buildLandingScreens(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildLandingScreens(LandingViewModel viewModel) => FlutterCarousel(
|
||||
options: FlutterCarouselOptions(
|
||||
autoPlay: true,
|
||||
viewportFraction: 1,
|
||||
showIndicator: true,
|
||||
indicatorMargin: 40,
|
||||
showIndicator: false,
|
||||
height: double.maxFinite,
|
||||
slideIndicator: CircularSlideIndicator(
|
||||
slideIndicatorOptions:
|
||||
const SlideIndicatorOptions(indicatorRadius: 2.5),
|
||||
),
|
||||
controller: viewModel.controller,
|
||||
),
|
||||
items: _buildScreens(),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:flutter_carousel_widget/flutter_carousel_widget.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:stacked_services/stacked_services.dart';
|
||||
|
||||
|
|
@ -11,6 +12,16 @@ class LandingViewModel extends BaseViewModel {
|
|||
|
||||
final _authenticationService = locator<AuthenticationService>();
|
||||
|
||||
// Controller
|
||||
final FlutterCarouselController _controller = FlutterCarouselController();
|
||||
|
||||
FlutterCarouselController get controller => _controller;
|
||||
|
||||
// In-app navigation
|
||||
void next() {
|
||||
_controller.nextPage();
|
||||
}
|
||||
|
||||
// Navigation
|
||||
Future<void> navigateToLogin() async =>
|
||||
await _navigationService.replaceWithLoginView();
|
||||
|
|
@ -18,6 +29,7 @@ class LandingViewModel extends BaseViewModel {
|
|||
// Remote api call
|
||||
|
||||
// First time install
|
||||
|
||||
Future<void> setFirstTimeInstall() async {
|
||||
await runBusyFuture(_setFirstTimeInstall());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,10 +126,10 @@ class FirstLandingScreen extends ViewModelWidget<LandingViewModel> {
|
|||
Widget _buildContinueButton(LandingViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
text: 'Next',
|
||||
borderRadius: 25,
|
||||
text: 'Get Started',
|
||||
onTap: viewModel.next,
|
||||
backgroundColor: kcWhite,
|
||||
foregroundColor: kcPrimaryColor,
|
||||
onTap: () async => await viewModel.setFirstTimeInstall(),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,10 +127,10 @@ class SecondLandingScreen extends ViewModelWidget<LandingViewModel> {
|
|||
Widget _buildContinueButton(LandingViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
text: 'Next',
|
||||
borderRadius: 25,
|
||||
text: 'Get Started',
|
||||
onTap: viewModel.next,
|
||||
foregroundColor: kcWhite,
|
||||
backgroundColor: kcPrimaryColor,
|
||||
onTap: () async => await viewModel.setFirstTimeInstall(),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -122,15 +122,15 @@ class ThirdLandingScreen extends ViewModelWidget<LandingViewModel> {
|
|||
viewModel.isBusy ? _buildIndicator() : _buildContinueButton(viewModel);
|
||||
|
||||
Widget _buildIndicator() =>
|
||||
const CustomCircularProgressIndicator(color: kcWhite);
|
||||
const CustomCircularProgressIndicator(color: kcPrimaryColor);
|
||||
|
||||
Widget _buildContinueButton(LandingViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
text: 'Next',
|
||||
borderRadius: 25,
|
||||
text: 'Get Started',
|
||||
onTap: viewModel.next,
|
||||
foregroundColor: kcWhite,
|
||||
backgroundColor: kcPrimaryColor,
|
||||
onTap: () async => await viewModel.setFirstTimeInstall(),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ class LanguageView extends StackedView<LanguageViewModel> {
|
|||
Widget _buildAppbar(LanguageViewModel viewModel) => SmallAppBar(
|
||||
showBackButton: true,
|
||||
onPop: viewModel.pop,
|
||||
title:LocaleKeys.language_preference.tr() ,
|
||||
title: LocaleKeys.language_preference.tr(),
|
||||
);
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
|
|
@ -117,7 +117,7 @@ class LanguageView extends StackedView<LanguageViewModel> {
|
|||
);
|
||||
|
||||
Widget _buildSubtitle() => Text(
|
||||
LocaleKeys.switch_language_anytime.tr() ,
|
||||
LocaleKeys.switch_language_anytime.tr(),
|
||||
style: style14MG400,
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ class LearnCourseView extends StackedView<LearnCourseViewModel> {
|
|||
course: viewModel.courses[index],
|
||||
onViewTap: () async =>
|
||||
await viewModel.navigateToLearnModule(viewModel.courses[index]),
|
||||
onLockTap: () async => await viewModel.navigateToLearnSubscription(),
|
||||
onPracticeTap: () async => await viewModel.navigateToLearnPractice(
|
||||
id: viewModel.courses[index].id ?? 0,
|
||||
level: viewModel.courses[index].name ?? ''),
|
||||
|
|
@ -99,11 +100,13 @@ class LearnCourseView extends StackedView<LearnCourseViewModel> {
|
|||
Widget _buildTile({
|
||||
required LearnCourse course,
|
||||
required GestureTapCallback onViewTap,
|
||||
required GestureTapCallback onLockTap,
|
||||
required GestureTapCallback onPracticeTap,
|
||||
}) =>
|
||||
LearnCourseTile(
|
||||
course: course,
|
||||
onViewTap: onViewTap,
|
||||
onLockTap: onLockTap,
|
||||
onPracticeTap: onPracticeTap,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ class LearnCourseViewModel extends ReactiveViewModel {
|
|||
// Navigation
|
||||
void pop() => _navigationService.back();
|
||||
|
||||
Future<void> navigateToLearnSubscription() async =>
|
||||
await _navigationService.navigateToLearnSubscriptionView();
|
||||
|
||||
Future<void> navigateToLearnModule(LearnCourse course) async =>
|
||||
_navigationService.navigateToLearnModuleView(course: course);
|
||||
|
||||
|
|
@ -39,7 +42,7 @@ class LearnCourseViewModel extends ReactiveViewModel {
|
|||
level: level,
|
||||
practice: LearnPractices.course,
|
||||
label: LocaleKeys.begin_level_practice.tr(),
|
||||
subtitle: LocaleKeys.lets_quick_practice.tr(),
|
||||
subtitle: LocaleKeys.lets_quick_review.tr(),
|
||||
title: '${LocaleKeys.lets_practice_course.tr()} $level',
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -34,9 +34,9 @@ class LearnLessonViewModel extends ReactiveViewModel {
|
|||
await _navigationService.navigateToLearnPracticeView(
|
||||
id: id,
|
||||
practice: LearnPractices.lesson,
|
||||
label:LocaleKeys.start_practice.tr(),
|
||||
label: LocaleKeys.start_practice.tr(),
|
||||
title: LocaleKeys.lets_practice_module.tr(),
|
||||
subtitle:LocaleKeys.ask_you_few_actions.tr(),
|
||||
subtitle: LocaleKeys.ask_you_few_actions.tr(),
|
||||
);
|
||||
|
||||
Future<void> navigateToLearnLessonDetail(
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ class LearnLessonDetailView extends StackedView<LearnLessonDetailViewModel> {
|
|||
borderRadius: 12,
|
||||
foregroundColor: kcWhite,
|
||||
backgroundColor: kcPrimaryColor,
|
||||
text:LocaleKeys.take_practice.tr() ,
|
||||
text: LocaleKeys.take_practice.tr(),
|
||||
onTap: () async => await _navigate(viewModel),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,8 +29,6 @@ class LearnModuleViewModel extends ReactiveViewModel {
|
|||
// Navigation
|
||||
void pop() => _navigationService.back();
|
||||
|
||||
|
||||
|
||||
Future<void> navigateToLearnLesson(LearnModule module) async =>
|
||||
await _navigationService.navigateToLearnLessonView(module: module);
|
||||
|
||||
|
|
|
|||
|
|
@ -127,7 +127,8 @@ class InteractLearnPracticeScreen
|
|||
showBackButton: true,
|
||||
onPop: () async =>
|
||||
await _showSheet(context: context, viewModel: viewModel),
|
||||
title: '${LocaleKeys.practice_speaking.tr()} ($index/${viewModel.questions.length})');
|
||||
title:
|
||||
'${LocaleKeys.practice_speaking.tr()} ($index/${viewModel.questions.length})');
|
||||
|
||||
Widget _buildSpeakingIndicatorWrapper(LearnPracticeViewModel viewModel) =>
|
||||
Column(
|
||||
|
|
@ -160,7 +161,7 @@ class InteractLearnPracticeScreen
|
|||
);
|
||||
|
||||
Widget _buildSpeakingLabel() => Text(
|
||||
'${ LocaleKeys.you_are_speaking.tr()}...',
|
||||
'${LocaleKeys.you_are_speaking.tr()}...',
|
||||
style: style14P400,
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
|
@ -359,7 +360,7 @@ class InteractLearnPracticeScreen
|
|||
CustomColumnButton(
|
||||
color: kcRed,
|
||||
icon: Icons.close,
|
||||
label:LocaleKeys.cancel.tr() ,
|
||||
label: LocaleKeys.cancel.tr(),
|
||||
onTap: () async =>
|
||||
await _showSheet(context: context, viewModel: viewModel),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -158,6 +158,5 @@ class LearnPracticeAppreciationScreen
|
|||
onTap: () => viewModel.goTo(4),
|
||||
backgroundColor: kcPrimaryColor,
|
||||
text: LocaleKeys.view_results.tr(),
|
||||
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -180,6 +180,6 @@ class LearnPracticeDescriptionScreen
|
|||
foregroundColor: kcWhite,
|
||||
onTap: () => viewModel.goTo(2),
|
||||
backgroundColor: kcPrimaryColor,
|
||||
text: LocaleKeys.start_practice.tr() ,
|
||||
text: LocaleKeys.start_practice.tr(),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ class LearnPracticeFinishScreen
|
|||
Widget _buildAppBar(LearnPracticeViewModel viewModel) => SmallAppBar(
|
||||
showBackButton: true,
|
||||
onPop: viewModel.goBack,
|
||||
title:LocaleKeys.practice_speaking.tr(),
|
||||
title: LocaleKeys.practice_speaking.tr(),
|
||||
);
|
||||
|
||||
Widget _buildSpeakingIndicatorWrapper(LearnPracticeViewModel viewModel) =>
|
||||
|
|
@ -87,7 +87,7 @@ class LearnPracticeFinishScreen
|
|||
);
|
||||
|
||||
Widget _buildSubtitle() => Text(
|
||||
LocaleKeys.sound_confident.tr() ,
|
||||
LocaleKeys.sound_confident.tr(),
|
||||
style: style14DG400,
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
|
@ -120,7 +120,7 @@ class LearnPracticeFinishScreen
|
|||
onTap: viewModel.pop,
|
||||
foregroundColor: kcWhite,
|
||||
backgroundColor: kcPrimaryColor,
|
||||
text: LocaleKeys.continue_practice.tr() ,
|
||||
text: LocaleKeys.continue_practice.tr(),
|
||||
);
|
||||
|
||||
Widget _buildPracticeAgainButton(LearnPracticeViewModel viewModel) =>
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ class LearnPracticeResultScreen
|
|||
required LearnPracticeViewModel viewModel}) =>
|
||||
SmallAppBar(
|
||||
showBackButton: true,
|
||||
title:LocaleKeys.result.tr(),
|
||||
title: LocaleKeys.result.tr(),
|
||||
onPop: () async =>
|
||||
await _showSheet(context: context, viewModel: viewModel),
|
||||
);
|
||||
|
|
@ -176,7 +176,7 @@ class LearnPracticeResultScreen
|
|||
height: 55,
|
||||
borderRadius: 12,
|
||||
foregroundColor: kcWhite,
|
||||
text:LocaleKeys.cont.tr(),
|
||||
text: LocaleKeys.cont.tr(),
|
||||
backgroundColor: kcPrimaryColor,
|
||||
onTap: () async => await _navigate(viewModel),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ class StartLearnPracticeScreen extends ViewModelWidget<LearnPracticeViewModel> {
|
|||
];
|
||||
|
||||
Widget _buildActionLabel() => Text(
|
||||
LocaleKeys.tap_start_to_listen.tr() ,
|
||||
LocaleKeys.tap_start_to_listen.tr(),
|
||||
style: style14DG400,
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
|
@ -216,7 +216,7 @@ class StartLearnPracticeScreen extends ViewModelWidget<LearnPracticeViewModel> {
|
|||
Widget _buildReplyButtonWrapper() => Expanded(child: _buildReplyButton());
|
||||
|
||||
Widget _buildReplyButton() => CustomColumnButton(
|
||||
icon: Icons.replay, label:LocaleKeys.reply.tr() , color: kcLightGrey);
|
||||
icon: Icons.replay, label: LocaleKeys.reply.tr(), color: kcLightGrey);
|
||||
|
||||
Widget _buildMicButtonWrapper(LearnPracticeViewModel viewModel) =>
|
||||
Expanded(child: _buildMicButton(viewModel));
|
||||
|
|
|
|||
|
|
@ -89,14 +89,12 @@ class LearnProgramView extends StackedView<LearnProgramViewModel> {
|
|||
program: viewModel.learnPrograms[index],
|
||||
onTap: () async => await viewModel
|
||||
.navigateToLearnCourse(viewModel.learnPrograms[index].id ?? 0),
|
||||
onLockTap: () async => await viewModel.navigateToLearnSubscription(),
|
||||
),
|
||||
);
|
||||
|
||||
Widget _buildTile({
|
||||
required LearnProgram program,
|
||||
required GestureTapCallback onTap,
|
||||
required GestureTapCallback onLockTap,
|
||||
}) =>
|
||||
LearnProgramTile(onTap: onTap, program: program,onLockTap: onLockTap,);
|
||||
LearnProgramTile(onTap: onTap, program: program);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,9 +39,6 @@ class LearnProgramViewModel extends ReactiveViewModel {
|
|||
Future<void> navigateToLearnCourse(int id) async =>
|
||||
_navigationService.navigateToLearnCourseView(id: id);
|
||||
|
||||
Future<void> navigateToLearnSubscription() async =>
|
||||
await _navigationService.navigateToLearnSubscriptionView();
|
||||
|
||||
// Remote api call
|
||||
|
||||
// Learn programs
|
||||
|
|
|
|||
|
|
@ -92,8 +92,10 @@ class LoginOtpScreen extends ViewModelWidget<LoginViewModel> {
|
|||
Widget _buildAppBar(LoginViewModel viewModel) => LargeAppBar(
|
||||
showBackButton: false,
|
||||
showLanguageSelection: true,
|
||||
language: viewModel.selectedLanguage['code'],
|
||||
onLanguage: () async => await viewModel.navigateToLanguage(),
|
||||
language: viewModel.selectedLanguage['code'] == 'am'
|
||||
? 'አማ'
|
||||
: viewModel.selectedLanguage['code'],
|
||||
);
|
||||
|
||||
Widget _buildExpandedBody(
|
||||
|
|
|
|||
|
|
@ -88,8 +88,10 @@ class LoginWithEmailScreen extends ViewModelWidget<LoginViewModel> {
|
|||
Widget _buildAppBar(LoginViewModel viewModel) => LargeAppBar(
|
||||
showBackButton: false,
|
||||
showLanguageSelection: true,
|
||||
language: viewModel.selectedLanguage['code'],
|
||||
onLanguage: () async => await viewModel.navigateToLanguage(),
|
||||
language: viewModel.selectedLanguage['code'] == 'am'
|
||||
? 'አማ'
|
||||
: viewModel.selectedLanguage['code'],
|
||||
);
|
||||
|
||||
Widget _buildExpandedBody(
|
||||
|
|
|
|||
|
|
@ -85,8 +85,10 @@ class LoginWithPhoneNumberScreen extends ViewModelWidget<LoginViewModel> {
|
|||
showBackButton: true,
|
||||
showLanguageSelection: true,
|
||||
onPop: () => viewModel.goTo(0),
|
||||
language: viewModel.selectedLanguage['code'],
|
||||
onLanguage: () async => await viewModel.navigateToLanguage(),
|
||||
language: viewModel.selectedLanguage['code'] == 'am'
|
||||
? 'አማ'
|
||||
: viewModel.selectedLanguage['code'],
|
||||
);
|
||||
|
||||
Widget _buildExpandedBody(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:stacked/stacked_annotations.dart';
|
||||
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||
|
||||
import 'package:yimaru_app/ui/views/onboarding/screens/age_group_form_screen.dart';
|
||||
import 'package:yimaru_app/ui/views/onboarding/screens/challenge_form_screen.dart';
|
||||
|
|
@ -12,18 +13,16 @@ import 'package:yimaru_app/ui/views/onboarding/screens/language_goal_form_screen
|
|||
import 'package:yimaru_app/ui/views/onboarding/screens/learning_goal_form_screen.dart';
|
||||
import 'package:yimaru_app/ui/views/onboarding/screens/occupation_form_screen.dart';
|
||||
import 'package:yimaru_app/ui/views/onboarding/screens/topic_form_screen.dart';
|
||||
import 'package:yimaru_app/ui/views/startup/startup_view.dart';
|
||||
|
||||
import '../../common/validators/form_validator.dart';
|
||||
import 'onboarding_viewmodel.dart';
|
||||
import 'onboarding_view.form.dart';
|
||||
|
||||
@FormView(fields: [
|
||||
FormTextField(name: 'topic', validator: FormValidator.validateForm),
|
||||
FormTextField(
|
||||
name: 'fullName', validator: FormValidator.validateFullNameForm),
|
||||
FormTextField(name: 'region', validator: FormValidator.validateForm),
|
||||
FormTextField(name: 'challenge', validator: FormValidator.validateForm),
|
||||
FormTextField(name: 'languageGoal', validator: FormValidator.validateForm),
|
||||
])
|
||||
class OnboardingView extends StackedView<OnboardingViewModel>
|
||||
with $OnboardingView {
|
||||
|
|
@ -34,11 +33,8 @@ class OnboardingView extends StackedView<OnboardingViewModel>
|
|||
}
|
||||
|
||||
void _initClearData() {
|
||||
topicController.clear();
|
||||
regionController.clear();
|
||||
fullNameController.clear();
|
||||
challengeController.clear();
|
||||
languageGoalController.clear();
|
||||
}
|
||||
|
||||
void _clearDataOnNavigation(OnboardingViewModel viewModel) {
|
||||
|
|
@ -54,17 +50,15 @@ class OnboardingView extends StackedView<OnboardingViewModel>
|
|||
} else if (viewModel.currentPage == 4) {
|
||||
viewModel.resetOccupationFormScreen();
|
||||
} else if (viewModel.currentPage == 5) {
|
||||
regionController.clear();
|
||||
viewModel.resetCountryRegionFormScreen();
|
||||
} else if (viewModel.currentPage == 6) {
|
||||
viewModel.resetLearningGoalFormScreen();
|
||||
} else if (viewModel.currentPage == 7) {
|
||||
languageGoalController.clear();
|
||||
viewModel.resetLanguageGoalFormScreen();
|
||||
} else if (viewModel.currentPage == 8) {
|
||||
challengeController.clear();
|
||||
viewModel.resetChallengeFormScreen();
|
||||
} else if (viewModel.currentPage == 9) {
|
||||
topicController.clear();
|
||||
viewModel.resetTopicFormScreen();
|
||||
}
|
||||
}
|
||||
|
|
@ -77,7 +71,7 @@ class OnboardingView extends StackedView<OnboardingViewModel>
|
|||
}
|
||||
|
||||
@override
|
||||
void onViewModelReady(OnboardingViewModel viewModel) {
|
||||
void onViewModelReady(OnboardingViewModel viewModel) async {
|
||||
_initClearData();
|
||||
_initUserData(viewModel);
|
||||
syncFormWithViewModel(viewModel);
|
||||
|
|
@ -134,17 +128,13 @@ class OnboardingView extends StackedView<OnboardingViewModel>
|
|||
|
||||
Widget _buildOccupationForm() => const OccupationFormScreen();
|
||||
|
||||
Widget _buildCountryRegionForm() => CountryRegionFormScreen(
|
||||
regionController: regionController,
|
||||
);
|
||||
Widget _buildCountryRegionForm() => CountryRegionFormScreen(regionController: regionController);
|
||||
|
||||
Widget _buildLearningGoalForm() => const LearningGoalFormScreen();
|
||||
|
||||
Widget _buildLanguageGoalForm() =>
|
||||
LanguageGoalFormScreen(languageGoalController: languageGoalController);
|
||||
Widget _buildLanguageGoalForm() => const LanguageGoalFormScreen();
|
||||
|
||||
Widget _buildChallengeForm() =>
|
||||
ChallengeFormScreen(challengeController: challengeController);
|
||||
Widget _buildChallengeForm() => const ChallengeFormScreen();
|
||||
|
||||
Widget _buildTopicForm() => TopicFormScreen(topicController: topicController);
|
||||
Widget _buildTopicForm() => const TopicFormScreen();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,11 +13,8 @@ import 'package:yimaru_app/ui/common/validators/form_validator.dart';
|
|||
|
||||
const bool _autoTextFieldValidation = true;
|
||||
|
||||
const String TopicValueKey = 'topic';
|
||||
const String FullNameValueKey = 'fullName';
|
||||
const String RegionValueKey = 'region';
|
||||
const String ChallengeValueKey = 'challenge';
|
||||
const String LanguageGoalValueKey = 'languageGoal';
|
||||
|
||||
final Map<String, TextEditingController> _OnboardingViewTextEditingControllers =
|
||||
{};
|
||||
|
|
@ -25,31 +22,18 @@ final Map<String, TextEditingController> _OnboardingViewTextEditingControllers =
|
|||
final Map<String, FocusNode> _OnboardingViewFocusNodes = {};
|
||||
|
||||
final Map<String, String? Function(String?)?> _OnboardingViewTextValidations = {
|
||||
TopicValueKey: FormValidator.validateForm,
|
||||
FullNameValueKey: FormValidator.validateFullNameForm,
|
||||
RegionValueKey: FormValidator.validateForm,
|
||||
ChallengeValueKey: FormValidator.validateForm,
|
||||
LanguageGoalValueKey: FormValidator.validateForm,
|
||||
};
|
||||
|
||||
mixin $OnboardingView {
|
||||
TextEditingController get topicController =>
|
||||
_getFormTextEditingController(TopicValueKey);
|
||||
TextEditingController get fullNameController =>
|
||||
_getFormTextEditingController(FullNameValueKey);
|
||||
TextEditingController get regionController =>
|
||||
_getFormTextEditingController(RegionValueKey);
|
||||
TextEditingController get challengeController =>
|
||||
_getFormTextEditingController(ChallengeValueKey);
|
||||
TextEditingController get languageGoalController =>
|
||||
_getFormTextEditingController(LanguageGoalValueKey);
|
||||
|
||||
FocusNode get topicFocusNode => _getFormFocusNode(TopicValueKey);
|
||||
FocusNode get fullNameFocusNode => _getFormFocusNode(FullNameValueKey);
|
||||
FocusNode get regionFocusNode => _getFormFocusNode(RegionValueKey);
|
||||
FocusNode get challengeFocusNode => _getFormFocusNode(ChallengeValueKey);
|
||||
FocusNode get languageGoalFocusNode =>
|
||||
_getFormFocusNode(LanguageGoalValueKey);
|
||||
|
||||
TextEditingController _getFormTextEditingController(
|
||||
String key, {
|
||||
|
|
@ -75,11 +59,8 @@ mixin $OnboardingView {
|
|||
/// Registers a listener on every generated controller that calls [model.setData()]
|
||||
/// with the latest textController values
|
||||
void syncFormWithViewModel(FormStateHelper model) {
|
||||
topicController.addListener(() => _updateFormData(model));
|
||||
fullNameController.addListener(() => _updateFormData(model));
|
||||
regionController.addListener(() => _updateFormData(model));
|
||||
challengeController.addListener(() => _updateFormData(model));
|
||||
languageGoalController.addListener(() => _updateFormData(model));
|
||||
|
||||
_updateFormData(model, forceValidate: _autoTextFieldValidation);
|
||||
}
|
||||
|
|
@ -91,11 +72,8 @@ mixin $OnboardingView {
|
|||
'This feature was deprecated after 3.1.0.',
|
||||
)
|
||||
void listenToFormUpdated(FormViewModel model) {
|
||||
topicController.addListener(() => _updateFormData(model));
|
||||
fullNameController.addListener(() => _updateFormData(model));
|
||||
regionController.addListener(() => _updateFormData(model));
|
||||
challengeController.addListener(() => _updateFormData(model));
|
||||
languageGoalController.addListener(() => _updateFormData(model));
|
||||
|
||||
_updateFormData(model, forceValidate: _autoTextFieldValidation);
|
||||
}
|
||||
|
|
@ -105,11 +83,8 @@ mixin $OnboardingView {
|
|||
model.setData(
|
||||
model.formValueMap
|
||||
..addAll({
|
||||
TopicValueKey: topicController.text,
|
||||
FullNameValueKey: fullNameController.text,
|
||||
RegionValueKey: regionController.text,
|
||||
ChallengeValueKey: challengeController.text,
|
||||
LanguageGoalValueKey: languageGoalController.text,
|
||||
}),
|
||||
);
|
||||
|
||||
|
|
@ -151,22 +126,8 @@ extension ValueProperties on FormStateHelper {
|
|||
return !hasAnyValidationMessage;
|
||||
}
|
||||
|
||||
String? get topicValue => this.formValueMap[TopicValueKey] as String?;
|
||||
String? get fullNameValue => this.formValueMap[FullNameValueKey] as String?;
|
||||
String? get regionValue => this.formValueMap[RegionValueKey] as String?;
|
||||
String? get challengeValue => this.formValueMap[ChallengeValueKey] as String?;
|
||||
String? get languageGoalValue =>
|
||||
this.formValueMap[LanguageGoalValueKey] as String?;
|
||||
|
||||
set topicValue(String? value) {
|
||||
this.setData(
|
||||
this.formValueMap..addAll({TopicValueKey: value}),
|
||||
);
|
||||
|
||||
if (_OnboardingViewTextEditingControllers.containsKey(TopicValueKey)) {
|
||||
_OnboardingViewTextEditingControllers[TopicValueKey]?.text = value ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
set fullNameValue(String? value) {
|
||||
this.setData(
|
||||
|
|
@ -189,97 +150,41 @@ extension ValueProperties on FormStateHelper {
|
|||
}
|
||||
}
|
||||
|
||||
set challengeValue(String? value) {
|
||||
this.setData(
|
||||
this.formValueMap..addAll({ChallengeValueKey: value}),
|
||||
);
|
||||
|
||||
if (_OnboardingViewTextEditingControllers.containsKey(ChallengeValueKey)) {
|
||||
_OnboardingViewTextEditingControllers[ChallengeValueKey]?.text =
|
||||
value ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
set languageGoalValue(String? value) {
|
||||
this.setData(
|
||||
this.formValueMap..addAll({LanguageGoalValueKey: value}),
|
||||
);
|
||||
|
||||
if (_OnboardingViewTextEditingControllers.containsKey(
|
||||
LanguageGoalValueKey)) {
|
||||
_OnboardingViewTextEditingControllers[LanguageGoalValueKey]?.text =
|
||||
value ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
bool get hasTopic =>
|
||||
this.formValueMap.containsKey(TopicValueKey) &&
|
||||
(topicValue?.isNotEmpty ?? false);
|
||||
bool get hasFullName =>
|
||||
this.formValueMap.containsKey(FullNameValueKey) &&
|
||||
(fullNameValue?.isNotEmpty ?? false);
|
||||
bool get hasRegion =>
|
||||
this.formValueMap.containsKey(RegionValueKey) &&
|
||||
(regionValue?.isNotEmpty ?? false);
|
||||
bool get hasChallenge =>
|
||||
this.formValueMap.containsKey(ChallengeValueKey) &&
|
||||
(challengeValue?.isNotEmpty ?? false);
|
||||
bool get hasLanguageGoal =>
|
||||
this.formValueMap.containsKey(LanguageGoalValueKey) &&
|
||||
(languageGoalValue?.isNotEmpty ?? false);
|
||||
|
||||
bool get hasTopicValidationMessage =>
|
||||
this.fieldsValidationMessages[TopicValueKey]?.isNotEmpty ?? false;
|
||||
bool get hasFullNameValidationMessage =>
|
||||
this.fieldsValidationMessages[FullNameValueKey]?.isNotEmpty ?? false;
|
||||
bool get hasRegionValidationMessage =>
|
||||
this.fieldsValidationMessages[RegionValueKey]?.isNotEmpty ?? false;
|
||||
bool get hasChallengeValidationMessage =>
|
||||
this.fieldsValidationMessages[ChallengeValueKey]?.isNotEmpty ?? false;
|
||||
bool get hasLanguageGoalValidationMessage =>
|
||||
this.fieldsValidationMessages[LanguageGoalValueKey]?.isNotEmpty ?? false;
|
||||
|
||||
String? get topicValidationMessage =>
|
||||
this.fieldsValidationMessages[TopicValueKey];
|
||||
String? get fullNameValidationMessage =>
|
||||
this.fieldsValidationMessages[FullNameValueKey];
|
||||
String? get regionValidationMessage =>
|
||||
this.fieldsValidationMessages[RegionValueKey];
|
||||
String? get challengeValidationMessage =>
|
||||
this.fieldsValidationMessages[ChallengeValueKey];
|
||||
String? get languageGoalValidationMessage =>
|
||||
this.fieldsValidationMessages[LanguageGoalValueKey];
|
||||
}
|
||||
|
||||
extension Methods on FormStateHelper {
|
||||
void setTopicValidationMessage(String? validationMessage) =>
|
||||
this.fieldsValidationMessages[TopicValueKey] = validationMessage;
|
||||
void setFullNameValidationMessage(String? validationMessage) =>
|
||||
this.fieldsValidationMessages[FullNameValueKey] = validationMessage;
|
||||
void setRegionValidationMessage(String? validationMessage) =>
|
||||
this.fieldsValidationMessages[RegionValueKey] = validationMessage;
|
||||
void setChallengeValidationMessage(String? validationMessage) =>
|
||||
this.fieldsValidationMessages[ChallengeValueKey] = validationMessage;
|
||||
void setLanguageGoalValidationMessage(String? validationMessage) =>
|
||||
this.fieldsValidationMessages[LanguageGoalValueKey] = validationMessage;
|
||||
|
||||
/// Clears text input fields on the Form
|
||||
void clearForm() {
|
||||
topicValue = '';
|
||||
fullNameValue = '';
|
||||
regionValue = '';
|
||||
challengeValue = '';
|
||||
languageGoalValue = '';
|
||||
}
|
||||
|
||||
/// Validates text input fields on the Form
|
||||
void validateForm() {
|
||||
this.setValidationMessages({
|
||||
TopicValueKey: getValidationMessage(TopicValueKey),
|
||||
FullNameValueKey: getValidationMessage(FullNameValueKey),
|
||||
RegionValueKey: getValidationMessage(RegionValueKey),
|
||||
ChallengeValueKey: getValidationMessage(ChallengeValueKey),
|
||||
LanguageGoalValueKey: getValidationMessage(LanguageGoalValueKey),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -299,9 +204,6 @@ String? getValidationMessage(String key) {
|
|||
/// Updates the fieldsValidationMessages on the FormViewModel
|
||||
void updateValidationData(FormStateHelper model) =>
|
||||
model.setValidationMessages({
|
||||
TopicValueKey: getValidationMessage(TopicValueKey),
|
||||
FullNameValueKey: getValidationMessage(FullNameValueKey),
|
||||
RegionValueKey: getValidationMessage(RegionValueKey),
|
||||
ChallengeValueKey: getValidationMessage(ChallengeValueKey),
|
||||
LanguageGoalValueKey: getValidationMessage(LanguageGoalValueKey),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,8 +3,13 @@ import 'package:stacked/stacked.dart';
|
|||
import 'package:stacked_services/stacked_services.dart';
|
||||
import 'package:yimaru_app/app/app.router.dart';
|
||||
import '../../../app/app.locator.dart';
|
||||
import '../../../models/field_option.dart';
|
||||
import '../../../services/api_service.dart';
|
||||
import '../../../services/google_auth_service.dart';
|
||||
import '../../../services/localization_service.dart';
|
||||
import '../../../services/onboarding_service.dart';
|
||||
import '../../../services/status_checker_service.dart';
|
||||
import '../../common/enmus.dart';
|
||||
|
||||
class OnboardingViewModel extends ReactiveViewModel
|
||||
with FormStateHelper
|
||||
|
|
@ -15,6 +20,8 @@ class OnboardingViewModel extends ReactiveViewModel
|
|||
|
||||
final _googleAuthService = locator<GoogleAuthService>();
|
||||
|
||||
final _onboardingService = locator<OnboardingService>();
|
||||
|
||||
final _localizationService = locator<LocalizationService>();
|
||||
|
||||
@override
|
||||
|
|
@ -47,19 +54,15 @@ class OnboardingViewModel extends ReactiveViewModel
|
|||
bool get focusFullName => _focusFullName;
|
||||
|
||||
// Educational background
|
||||
final List<String> _educationalBackgrounds = [
|
||||
'No formal education',
|
||||
'Primary school',
|
||||
'Secondary /High school',
|
||||
'College / Diploma',
|
||||
'Bachelor’s and above',
|
||||
];
|
||||
List<FieldOption> get _educationalBackgrounds =>
|
||||
_onboardingService.educationalBackgrounds;
|
||||
|
||||
List<String> get educationalBackgrounds => _educationalBackgrounds;
|
||||
List<FieldOption> get educationalBackgrounds => _educationalBackgrounds;
|
||||
|
||||
String? _selectedEducationalBackground;
|
||||
FieldOption? _selectedEducationalBackground;
|
||||
|
||||
String? get selectedEducationalBackground => _selectedEducationalBackground;
|
||||
FieldOption? get selectedEducationalBackground =>
|
||||
_selectedEducationalBackground;
|
||||
|
||||
// Gender
|
||||
final List<String> _gendersEn = [
|
||||
|
|
@ -81,150 +84,73 @@ class OnboardingViewModel extends ReactiveViewModel
|
|||
String? get selectedGender => _selectedGender;
|
||||
|
||||
// Age group
|
||||
final List<Map<String, dynamic>> _ageGroups = [
|
||||
{
|
||||
'Under 13': 'UNDER_13',
|
||||
},
|
||||
{
|
||||
'13-17': '13_17',
|
||||
},
|
||||
{
|
||||
'18-24': '18_24',
|
||||
},
|
||||
{
|
||||
'25-34': '25_34',
|
||||
},
|
||||
{
|
||||
'35-44': '35_44',
|
||||
},
|
||||
{
|
||||
'45-54': '45_54',
|
||||
},
|
||||
{'55-Plus': '55_PLUS'},
|
||||
];
|
||||
List<FieldOption> get _ageGroups => _onboardingService.ageGroups;
|
||||
|
||||
List<Map<String, dynamic>> get ageGroups => _ageGroups;
|
||||
List<FieldOption> get ageGroups => _ageGroups;
|
||||
|
||||
Map<String, dynamic>? _selectedAgeGroup;
|
||||
FieldOption? _selectedAgeGroup;
|
||||
|
||||
Map<String, dynamic>? get selectedAgeGroup => _selectedAgeGroup;
|
||||
FieldOption? get selectedAgeGroup => _selectedAgeGroup;
|
||||
|
||||
// Occupation
|
||||
String _selectedOccupation = 'Students (High school & University)';
|
||||
FieldOption? _selectedOccupation;
|
||||
|
||||
String get selectedOccupation => _selectedOccupation;
|
||||
FieldOption? get selectedOccupation => _selectedOccupation;
|
||||
|
||||
// Country
|
||||
String _selectedCountry = 'Ethiopia';
|
||||
FieldOption? _selectedCountry;
|
||||
|
||||
String get selectedCountry => _selectedCountry;
|
||||
FieldOption? get selectedCountry => _selectedCountry;
|
||||
|
||||
// Region
|
||||
bool _focusRegion = false;
|
||||
|
||||
bool get focusRegion => _focusRegion;
|
||||
|
||||
bool _dropdownRegion = true;
|
||||
bool _dropdownRegion = false;
|
||||
|
||||
bool get dropdownRegion => _dropdownRegion;
|
||||
|
||||
String _selectedRegion = 'Addis Ababa';
|
||||
FieldOption? _selectedRegion;
|
||||
|
||||
String get selectedRegion => _selectedRegion;
|
||||
FieldOption? get selectedRegion => _selectedRegion;
|
||||
|
||||
// Learning goal
|
||||
String? _selectedLearningGoal;
|
||||
FieldOption? _selectedLearningGoal;
|
||||
|
||||
String? get selectedLearningGoal => _selectedLearningGoal;
|
||||
FieldOption? get selectedLearningGoal => _selectedLearningGoal;
|
||||
|
||||
final List<Map<String, dynamic>> _learningGoals = [
|
||||
{
|
||||
'icon': 0,
|
||||
'title': 'Learn to Speak English',
|
||||
'subtitle': 'I know some English, but i want to learn to speak it',
|
||||
},
|
||||
{
|
||||
'icon': 1,
|
||||
'title': 'Practice Speaking English',
|
||||
'subtitle': 'I already speak English, but I want more practice.',
|
||||
},
|
||||
{
|
||||
'icon': 2,
|
||||
'title': 'Skill-based Courses',
|
||||
'subtitle': 'I want courses for IELTS, TOEFL, Duolingo, or work.',
|
||||
},
|
||||
];
|
||||
List<FieldOption> get _learningGoals => _onboardingService.learningGoals;
|
||||
|
||||
List<Map<String, dynamic>> get learningGoals => _learningGoals;
|
||||
List<FieldOption> get learningGoals => _learningGoals;
|
||||
|
||||
// Learning reason
|
||||
bool _showLanguageGoalTextBox = false;
|
||||
|
||||
bool get showLanguageGoalTextBox => _showLanguageGoalTextBox;
|
||||
FieldOption? _selectedLanguageGoal;
|
||||
|
||||
bool _focusLanguageGoal = false;
|
||||
FieldOption? get selectedLanguageGoal => _selectedLanguageGoal;
|
||||
|
||||
bool get focusLanguageGoal => _focusLanguageGoal;
|
||||
List<FieldOption> get _languageGoals => _onboardingService.languageGoals;
|
||||
|
||||
String? _selectedLanguageGoal;
|
||||
|
||||
String? get selectedLanguageGoal => _selectedLanguageGoal;
|
||||
|
||||
final List<String> _languageGoals = [
|
||||
'Speak confidently at work or school',
|
||||
'Travel or handle daily situations',
|
||||
'Connect with family or friends',
|
||||
'General skills expansion',
|
||||
'Other'
|
||||
];
|
||||
|
||||
List<String> get languageGoals => _languageGoals;
|
||||
List<FieldOption> get languageGoals => _languageGoals;
|
||||
|
||||
// Challenges
|
||||
bool _showChallengeTextBox = false;
|
||||
FieldOption? _selectedChallenge;
|
||||
|
||||
bool get showChallengeTextBox => _showChallengeTextBox;
|
||||
FieldOption? get selectedChallenge => _selectedChallenge;
|
||||
|
||||
bool _focusChallenge = false;
|
||||
List<FieldOption> get _challenges => _onboardingService.challenges;
|
||||
|
||||
bool get focusChallenge => _focusChallenge;
|
||||
|
||||
String? _selectedChallenge;
|
||||
|
||||
String? get selectedChallenge => _selectedChallenge;
|
||||
|
||||
final List<String> _challenges = [
|
||||
'Pronunciation',
|
||||
'Finding words or grammar quickly',
|
||||
'Feeling nervous or lacking confidence',
|
||||
'Understanding accents or fast speech',
|
||||
'Other'
|
||||
];
|
||||
|
||||
List<String> get challenges => _challenges;
|
||||
List<FieldOption> get challenges => _challenges;
|
||||
|
||||
// Topic
|
||||
bool _showTopicTextBox = false;
|
||||
FieldOption? _selectedTopic;
|
||||
|
||||
bool get showTopicTextBox => _showTopicTextBox;
|
||||
FieldOption? get selectedTopic => _selectedTopic;
|
||||
|
||||
bool _focusTopic = false;
|
||||
List<FieldOption> get _topics => _onboardingService.topics;
|
||||
|
||||
bool get focusTopic => _focusTopic;
|
||||
|
||||
String? _selectedTopic;
|
||||
|
||||
String? get selectedTopic => _selectedTopic;
|
||||
|
||||
final List<String> _topics = [
|
||||
'Food & Cooking',
|
||||
'Hobbies, Sports, Music',
|
||||
'Tech, News, Business',
|
||||
'Travel, Places, Culture',
|
||||
'Other'
|
||||
];
|
||||
|
||||
List<String> get topics => _topics;
|
||||
List<FieldOption> get topics => _topics;
|
||||
|
||||
// User data
|
||||
final Map<String, dynamic> _userData = {};
|
||||
|
|
@ -238,12 +164,12 @@ class OnboardingViewModel extends ReactiveViewModel
|
|||
}
|
||||
|
||||
// Education background
|
||||
void setSelectedEducationalBackground(String value) {
|
||||
void setSelectedEducationalBackground(FieldOption value) {
|
||||
_selectedEducationalBackground = value;
|
||||
rebuildUi();
|
||||
}
|
||||
|
||||
bool isSelectedEducationalBackground(String value) =>
|
||||
bool isSelectedEducationalBackground(FieldOption value) =>
|
||||
_selectedEducationalBackground == value;
|
||||
|
||||
// Gender
|
||||
|
|
@ -255,192 +181,35 @@ class OnboardingViewModel extends ReactiveViewModel
|
|||
bool isSelectedGender(String value) => _selectedGender == value;
|
||||
|
||||
// Age group
|
||||
void setSelectedAgeGroup(Map<String, dynamic> value) {
|
||||
void setSelectedAgeGroup(FieldOption value) {
|
||||
_selectedAgeGroup = value;
|
||||
|
||||
rebuildUi();
|
||||
}
|
||||
|
||||
bool isSelectedAgeGroup(Map<String, dynamic> value) =>
|
||||
_selectedAgeGroup == value;
|
||||
bool isSelectedAgeGroup(FieldOption value) => _selectedAgeGroup == value;
|
||||
|
||||
// Occupation
|
||||
List<String> 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)'
|
||||
];
|
||||
List<FieldOption> get _occupations => _onboardingService.occupations;
|
||||
|
||||
void setSelectedOccupation(String value) {
|
||||
List<FieldOption> get occupations => _occupations;
|
||||
|
||||
void setSelectedOccupation(FieldOption? value) {
|
||||
_selectedOccupation = value;
|
||||
rebuildUi();
|
||||
}
|
||||
|
||||
// Country
|
||||
List<String> getCountries() => [
|
||||
"Afghanistan",
|
||||
"Albania",
|
||||
"Algeria",
|
||||
"Andorra",
|
||||
"Angola",
|
||||
"Argentina",
|
||||
"Armenia",
|
||||
"Australia",
|
||||
"Austria",
|
||||
"Azerbaijan",
|
||||
"Bahrain",
|
||||
"Bangladesh",
|
||||
"Belarus",
|
||||
"Belgium",
|
||||
"Belize",
|
||||
"Benin",
|
||||
"Bhutan",
|
||||
"Bolivia",
|
||||
"Bosnia and Herzegovina",
|
||||
"Botswana",
|
||||
"Brazil",
|
||||
"Brunei",
|
||||
"Bulgaria",
|
||||
"Burkina Faso",
|
||||
"Burundi",
|
||||
"Cambodia",
|
||||
"Cameroon",
|
||||
"Canada",
|
||||
"Chad",
|
||||
"Chile",
|
||||
"China",
|
||||
"Colombia",
|
||||
"Comoros",
|
||||
"Congo",
|
||||
"Costa Rica",
|
||||
"Croatia",
|
||||
"Cuba",
|
||||
"Cyprus",
|
||||
"Czech Republic",
|
||||
"Denmark",
|
||||
"Djibouti",
|
||||
"Dominican Republic",
|
||||
"Ecuador",
|
||||
"Egypt",
|
||||
"El Salvador",
|
||||
"Eritrea",
|
||||
"Estonia",
|
||||
"Eswatini",
|
||||
"Ethiopia",
|
||||
"Finland",
|
||||
"France",
|
||||
"Gabon",
|
||||
"Gambia",
|
||||
"Georgia",
|
||||
"Germany",
|
||||
"Ghana",
|
||||
"Greece",
|
||||
"Guatemala",
|
||||
"Guinea",
|
||||
"Haiti",
|
||||
"Honduras",
|
||||
"Hungary",
|
||||
"Iceland",
|
||||
"India",
|
||||
"Indonesia",
|
||||
"Iran",
|
||||
"Iraq",
|
||||
"Ireland",
|
||||
"Israel",
|
||||
"Italy",
|
||||
"Jamaica",
|
||||
"Japan",
|
||||
"Jordan",
|
||||
"Kazakhstan",
|
||||
"Kenya",
|
||||
"Kuwait",
|
||||
"Kyrgyzstan",
|
||||
"Laos",
|
||||
"Latvia",
|
||||
"Lebanon",
|
||||
"Liberia",
|
||||
"Libya",
|
||||
"Lithuania",
|
||||
"Luxembourg",
|
||||
"Madagascar",
|
||||
"Malawi",
|
||||
"Malaysia",
|
||||
"Maldives",
|
||||
"Mali",
|
||||
"Malta",
|
||||
"Mexico",
|
||||
"Moldova",
|
||||
"Monaco",
|
||||
"Mongolia",
|
||||
"Morocco",
|
||||
"Mozambique",
|
||||
"Myanmar",
|
||||
"Namibia",
|
||||
"Nepal",
|
||||
"Netherlands",
|
||||
"New Zealand",
|
||||
"Nicaragua",
|
||||
"Niger",
|
||||
"Nigeria",
|
||||
"North Korea",
|
||||
"Norway",
|
||||
"Oman",
|
||||
"Pakistan",
|
||||
"Panama",
|
||||
"Paraguay",
|
||||
"Peru",
|
||||
"Philippines",
|
||||
"Poland",
|
||||
"Portugal",
|
||||
"Qatar",
|
||||
"Romania",
|
||||
"Russia",
|
||||
"Rwanda",
|
||||
"Saudi Arabia",
|
||||
"Senegal",
|
||||
"Serbia",
|
||||
"Singapore",
|
||||
"Slovakia",
|
||||
"Slovenia",
|
||||
"Somalia",
|
||||
"South Africa",
|
||||
"South Korea",
|
||||
"Spain",
|
||||
"Sri Lanka",
|
||||
"Sudan",
|
||||
"Sweden",
|
||||
"Switzerland",
|
||||
"Syria",
|
||||
"Taiwan",
|
||||
"Tajikistan",
|
||||
"Tanzania",
|
||||
"Thailand",
|
||||
"Tunisia",
|
||||
"Turkey",
|
||||
"Uganda",
|
||||
"Ukraine",
|
||||
"United Arab Emirates",
|
||||
"United Kingdom",
|
||||
"United States",
|
||||
"Uruguay",
|
||||
"Uzbekistan",
|
||||
"Venezuela",
|
||||
"Vietnam",
|
||||
"Yemen",
|
||||
"Zambia",
|
||||
"Zimbabwe"
|
||||
];
|
||||
List<FieldOption> get _countries => _onboardingService.countries;
|
||||
|
||||
void setSelectedCountry(String value) {
|
||||
List<FieldOption> get countries => _countries;
|
||||
|
||||
void setSelectedCountry(FieldOption? value) {
|
||||
_selectedCountry = value;
|
||||
|
||||
if (value == 'Ethiopia') {
|
||||
if (value?.label?.toLowerCase().trim() == 'ethiopia') {
|
||||
_dropdownRegion = true;
|
||||
_selectedRegion = 'Addis Ababa';
|
||||
_selectedRegion = _regions
|
||||
.firstWhere((e) => e.label?.toLowerCase().trim() == 'addis ababa');
|
||||
} else {
|
||||
_dropdownRegion = false;
|
||||
}
|
||||
|
|
@ -449,24 +218,11 @@ class OnboardingViewModel extends ReactiveViewModel
|
|||
}
|
||||
|
||||
// Region
|
||||
List<String> getRegions() => [
|
||||
'Addis Ababa',
|
||||
'Afar',
|
||||
'Amhara',
|
||||
'Benishangul-Gumuz',
|
||||
'Central Ethiopia',
|
||||
'Dire Dawa',
|
||||
'Gambela',
|
||||
'Harari',
|
||||
'Oromia',
|
||||
'Sidama',
|
||||
'Somali',
|
||||
'South Ethiopia',
|
||||
'South West Ethiopia Peoples',
|
||||
'Tigray',
|
||||
];
|
||||
List<FieldOption> get _regions => _onboardingService.regions;
|
||||
|
||||
void setSelectedRegion(String value) {
|
||||
List<FieldOption> get regions => _regions;
|
||||
|
||||
void setSelectedRegion(FieldOption? value) {
|
||||
_selectedRegion = value;
|
||||
rebuildUi();
|
||||
}
|
||||
|
|
@ -482,75 +238,42 @@ class OnboardingViewModel extends ReactiveViewModel
|
|||
}
|
||||
|
||||
// Learning goal
|
||||
void setSelectedLearningGoal(String value) {
|
||||
void setSelectedLearningGoal(FieldOption value) {
|
||||
_selectedLearningGoal = value;
|
||||
rebuildUi();
|
||||
}
|
||||
|
||||
bool isSelectedLearningGoal(String value) => _selectedLearningGoal == value;
|
||||
bool isSelectedLearningGoal(FieldOption value) =>
|
||||
_selectedLearningGoal == value;
|
||||
|
||||
// Learning reason
|
||||
void setLanguageGoalFocus() {
|
||||
_focusLanguageGoal = true;
|
||||
rebuildUi();
|
||||
}
|
||||
|
||||
void setSelectedLanguageGoal(String value) {
|
||||
void setSelectedLanguageGoal(FieldOption value) {
|
||||
_selectedLanguageGoal = value;
|
||||
if (value.toLowerCase() == 'other') {
|
||||
_showLanguageGoalTextBox = true;
|
||||
} else {
|
||||
if (_showLanguageGoalTextBox) {
|
||||
_showLanguageGoalTextBox = false;
|
||||
_focusLanguageGoal = false;
|
||||
}
|
||||
}
|
||||
|
||||
rebuildUi();
|
||||
}
|
||||
|
||||
bool isSelectedLanguageGoal(String value) => _selectedLanguageGoal == value;
|
||||
bool isSelectedLanguageGoal(FieldOption value) =>
|
||||
_selectedLanguageGoal == value;
|
||||
|
||||
// Challenges
|
||||
void setChallengesFocus() {
|
||||
_focusChallenge = true;
|
||||
rebuildUi();
|
||||
}
|
||||
|
||||
void setSelectedChallenge(String value) {
|
||||
void setSelectedChallenge(FieldOption value) {
|
||||
_selectedChallenge = value;
|
||||
if (value.toLowerCase() == 'other') {
|
||||
_showChallengeTextBox = true;
|
||||
} else {
|
||||
if (_showChallengeTextBox) {
|
||||
_showChallengeTextBox = false;
|
||||
_focusChallenge = false;
|
||||
}
|
||||
}
|
||||
rebuildUi();
|
||||
}
|
||||
|
||||
bool isSelectedChallenge(String value) => _selectedChallenge == value;
|
||||
bool isSelectedChallenge(FieldOption value) => _selectedChallenge == value;
|
||||
|
||||
// Topics
|
||||
void setTopicsFocus() {
|
||||
_focusTopic = true;
|
||||
rebuildUi();
|
||||
}
|
||||
|
||||
void setSelectedTopic(String value) {
|
||||
void setSelectedTopic(FieldOption value) {
|
||||
_selectedTopic = value;
|
||||
if (value.toLowerCase() == 'other') {
|
||||
_showTopicTextBox = true;
|
||||
} else {
|
||||
if (_showTopicTextBox) {
|
||||
_showTopicTextBox = false;
|
||||
_focusTopic = false;
|
||||
}
|
||||
}
|
||||
rebuildUi();
|
||||
}
|
||||
|
||||
bool isSelectedTopic(String value) => _selectedTopic == value;
|
||||
bool isSelectedTopic(FieldOption value) => _selectedTopic == value;
|
||||
|
||||
// Add user data
|
||||
void addUserData(Map<String, dynamic> data) {
|
||||
|
|
@ -589,15 +312,15 @@ class OnboardingViewModel extends ReactiveViewModel
|
|||
|
||||
// Reset occupation form screen
|
||||
void resetOccupationFormScreen() {
|
||||
_selectedOccupation = 'Students (High school & University)';
|
||||
_selectedOccupation = null;
|
||||
rebuildUi();
|
||||
}
|
||||
|
||||
// Reset country region form screen
|
||||
void resetCountryRegionFormScreen() {
|
||||
_focusRegion = false;
|
||||
_selectedCountry = 'Ethiopia';
|
||||
_selectedRegion = 'Addis Ababa';
|
||||
_selectedRegion = null;
|
||||
_selectedCountry = null;
|
||||
rebuildUi();
|
||||
}
|
||||
|
||||
|
|
@ -609,26 +332,20 @@ class OnboardingViewModel extends ReactiveViewModel
|
|||
|
||||
// Reset language goal form screen
|
||||
void resetLanguageGoalFormScreen() {
|
||||
_focusLanguageGoal = false;
|
||||
_selectedLanguageGoal = null;
|
||||
_showLanguageGoalTextBox = false;
|
||||
rebuildUi();
|
||||
}
|
||||
|
||||
// Reset challenge form screen
|
||||
void resetChallengeFormScreen() {
|
||||
_focusChallenge = false;
|
||||
_selectedChallenge = null;
|
||||
_showChallengeTextBox = false;
|
||||
|
||||
rebuildUi();
|
||||
}
|
||||
|
||||
// Reset topic form screen
|
||||
void resetTopicFormScreen() {
|
||||
_focusTopic = false;
|
||||
_selectedTopic = null;
|
||||
_showTopicTextBox = false;
|
||||
rebuildUi();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import 'package:easy_localization/easy_localization.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/translations/locale_keys.g.dart';
|
||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
||||
|
|
@ -19,9 +21,7 @@ class AgeGroupFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
Future<void> _next(OnboardingViewModel viewModel) async {
|
||||
FocusManager.instance.primaryFocus?.unfocus();
|
||||
|
||||
Map<String, dynamic> data = {
|
||||
'age_group': viewModel.selectedAgeGroup?.values.first
|
||||
};
|
||||
Map<String, dynamic> data = {'age_group': viewModel.selectedAgeGroup?.code};
|
||||
viewModel.addUserData(data);
|
||||
|
||||
viewModel.next();
|
||||
|
|
@ -88,17 +88,19 @@ class AgeGroupFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
showBackButton: true,
|
||||
showLanguageSelection: true,
|
||||
onPop: () => _pop(viewModel),
|
||||
language: viewModel.selectedLanguage['code'],
|
||||
onLanguage: () async => await viewModel.navigateToLanguage(),
|
||||
language: viewModel.selectedLanguage['code'] == 'am'
|
||||
? 'አማ'
|
||||
: viewModel.selectedLanguage['code'],
|
||||
);
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
'Which age range are you in?',
|
||||
LocaleKeys.age_range.tr(),
|
||||
style: style25DG600,
|
||||
);
|
||||
|
||||
Widget _buildSubtitle() => Text(
|
||||
'We’ll personalize your learning experience based on your age.',
|
||||
LocaleKeys.age_for_personalization.tr(),
|
||||
style: style14DG400,
|
||||
);
|
||||
|
||||
|
|
@ -107,7 +109,7 @@ class AgeGroupFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
itemCount: viewModel.ageGroups.length,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) => _buildAgeGroup(
|
||||
title: viewModel.ageGroups[index].keys.first,
|
||||
title: viewModel.ageGroups[index].label ?? '',
|
||||
selected: viewModel.isSelectedAgeGroup(viewModel.ageGroups[index]),
|
||||
onTap: () =>
|
||||
viewModel.setSelectedAgeGroup(viewModel.ageGroups[index]),
|
||||
|
|
@ -132,9 +134,9 @@ class AgeGroupFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
Widget _buildContinueButton(OnboardingViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
text: 'Continue',
|
||||
borderRadius: 12,
|
||||
foregroundColor: kcWhite,
|
||||
text: LocaleKeys.cont.tr(),
|
||||
backgroundColor: viewModel.selectedAgeGroup != null
|
||||
? kcPrimaryColor
|
||||
: kcPrimaryColor.withOpacity(0.1),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import 'package:easy_localization/easy_localization.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/translations/locale_keys.g.dart';
|
||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||
import 'package:yimaru_app/ui/views/onboarding/onboarding_view.form.dart';
|
||||
|
|
@ -9,12 +11,9 @@ import 'package:yimaru_app/ui/widgets/custom_small_radio_button.dart';
|
|||
import 'package:yimaru_app/ui/widgets/large_app_bar.dart';
|
||||
|
||||
class ChallengeFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
||||
final TextEditingController challengeController;
|
||||
|
||||
const ChallengeFormScreen({super.key, required this.challengeController});
|
||||
const ChallengeFormScreen({super.key});
|
||||
|
||||
void _pop(OnboardingViewModel viewModel) {
|
||||
challengeController.clear();
|
||||
viewModel.resetChallengeFormScreen();
|
||||
viewModel.goBack();
|
||||
}
|
||||
|
|
@ -23,8 +22,7 @@ class ChallengeFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
FocusManager.instance.primaryFocus?.unfocus();
|
||||
|
||||
Map<String, dynamic> data = {
|
||||
'language_challange':
|
||||
viewModel.selectedChallenge ?? challengeController.text,
|
||||
'language_challange': viewModel.selectedChallenge?.code,
|
||||
};
|
||||
viewModel.addUserData(data);
|
||||
|
||||
|
|
@ -86,15 +84,6 @@ class ChallengeFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
_buildSubtitle(),
|
||||
verticalSpaceMedium,
|
||||
_buildChallenges(viewModel),
|
||||
if (viewModel.showChallengeTextBox) _buildChallengeFormField(viewModel),
|
||||
if (viewModel.showChallengeTextBox &&
|
||||
viewModel.hasChallengeValidationMessage &&
|
||||
viewModel.focusChallenge)
|
||||
verticalSpaceTiny,
|
||||
if (viewModel.showChallengeTextBox &&
|
||||
viewModel.hasChallengeValidationMessage &&
|
||||
viewModel.focusChallenge)
|
||||
_buildChallengeValidatorWrapper(viewModel),
|
||||
verticalSpaceMedium,
|
||||
];
|
||||
|
||||
|
|
@ -102,17 +91,19 @@ class ChallengeFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
showBackButton: true,
|
||||
showLanguageSelection: true,
|
||||
onPop: () => _pop(viewModel),
|
||||
language: viewModel.selectedLanguage['code'],
|
||||
onLanguage: () async => await viewModel.navigateToLanguage(),
|
||||
language: viewModel.selectedLanguage['code'] == 'am'
|
||||
? 'አማ'
|
||||
: viewModel.selectedLanguage['code'],
|
||||
);
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
'What challenge do you face most with English?',
|
||||
LocaleKeys.challenge_you_face.tr(),
|
||||
style: style25DG600,
|
||||
);
|
||||
|
||||
Widget _buildSubtitle() => Text(
|
||||
'Everyone has struggles, let’s start fixing yours 😊',
|
||||
'${LocaleKeys.evey_one_has_strugle.tr()} 😊',
|
||||
style: style14MG400,
|
||||
);
|
||||
|
||||
|
|
@ -122,7 +113,7 @@ class ChallengeFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
itemCount: viewModel.challenges.length,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) => _buildChallenge(
|
||||
title: viewModel.challenges[index],
|
||||
title: viewModel.challenges[index].label ?? '',
|
||||
onTap: () =>
|
||||
viewModel.setSelectedChallenge(viewModel.challenges[index]),
|
||||
selected: viewModel.isSelectedChallenge(viewModel.challenges[index]),
|
||||
|
|
@ -139,27 +130,6 @@ class ChallengeFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
selected: selected,
|
||||
);
|
||||
|
||||
Widget _buildChallengeFormField(OnboardingViewModel viewModel) =>
|
||||
TextFormField(
|
||||
maxLines: 3,
|
||||
controller: challengeController,
|
||||
onTap: viewModel.setChallengesFocus,
|
||||
decoration: inputDecoration(
|
||||
focus: true,
|
||||
hint: 'Write your challenge…',
|
||||
filled: challengeController.text.isNotEmpty),
|
||||
);
|
||||
|
||||
Widget _buildChallengeValidatorWrapper(OnboardingViewModel viewModel) =>
|
||||
viewModel.hasChallengeValidationMessage
|
||||
? _buildChallengeValidator(viewModel)
|
||||
: Container();
|
||||
|
||||
Widget _buildChallengeValidator(OnboardingViewModel viewModel) => Text(
|
||||
viewModel.challengeValidationMessage!,
|
||||
style: style12R700,
|
||||
);
|
||||
|
||||
Widget _buildContinueButtonWrapper(OnboardingViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.only(bottom: 50),
|
||||
child: _buildContinueButton(viewModel),
|
||||
|
|
@ -168,21 +138,13 @@ class ChallengeFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
Widget _buildContinueButton(OnboardingViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
text: 'Continue',
|
||||
borderRadius: 12,
|
||||
foregroundColor: kcWhite,
|
||||
text: LocaleKeys.cont.tr(),
|
||||
onTap: viewModel.selectedChallenge != null
|
||||
? viewModel.selectedChallenge?.toLowerCase() == 'other'
|
||||
? challengeController.text.isNotEmpty
|
||||
? () => _next(viewModel)
|
||||
: null
|
||||
: () => _next(viewModel)
|
||||
: null,
|
||||
backgroundColor: viewModel.selectedChallenge != null
|
||||
? viewModel.selectedChallenge?.toLowerCase() == 'other'
|
||||
? challengeController.text.isNotEmpty
|
||||
? kcPrimaryColor
|
||||
: kcPrimaryColor.withOpacity(0.1)
|
||||
: kcPrimaryColor
|
||||
: kcPrimaryColor.withOpacity(0.1));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
import 'package:easy_localization/easy_localization.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/translations/locale_keys.g.dart';
|
||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
||||
import 'package:yimaru_app/ui/widgets/custom_dropdown.dart';
|
||||
import 'package:yimaru_app/ui/widgets/large_app_bar.dart';
|
||||
import '../../../../models/field_option.dart';
|
||||
import '../onboarding_view.form.dart';
|
||||
|
||||
class CountryRegionFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
||||
|
|
@ -13,10 +16,10 @@ class CountryRegionFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
const CountryRegionFormScreen({super.key, required this.regionController});
|
||||
|
||||
void _setSelectedCountry(
|
||||
{String? value, required OnboardingViewModel viewModel}) {
|
||||
viewModel.setSelectedCountry(value ?? 'Ethiopia');
|
||||
{FieldOption? value, required OnboardingViewModel viewModel}) {
|
||||
viewModel.setSelectedCountry(value);
|
||||
|
||||
if (viewModel.selectedCountry != 'Ethiopia') {
|
||||
if (viewModel.selectedCountry?.label?.toLowerCase().tr() != 'ethiopia') {
|
||||
regionController.clear();
|
||||
viewModel.unsetRegionFocus();
|
||||
}
|
||||
|
|
@ -32,9 +35,9 @@ class CountryRegionFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
|
||||
Map<String, dynamic> data = {
|
||||
'region': viewModel.dropdownRegion
|
||||
? viewModel.selectedRegion
|
||||
? viewModel.selectedRegion?.code
|
||||
: regionController.text,
|
||||
'country': viewModel.selectedCountry,
|
||||
'country': viewModel.selectedCountry?.code,
|
||||
};
|
||||
viewModel.addUserData(data);
|
||||
|
||||
|
|
@ -112,26 +115,28 @@ class CountryRegionFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
showBackButton: true,
|
||||
showLanguageSelection: true,
|
||||
onPop: () => _pop(viewModel),
|
||||
language: viewModel.selectedLanguage['code'],
|
||||
onLanguage: () async => await viewModel.navigateToLanguage(),
|
||||
language: viewModel.selectedLanguage['code'] == 'am'
|
||||
? 'አማ'
|
||||
: viewModel.selectedLanguage['code'],
|
||||
);
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
'Where are you from?',
|
||||
LocaleKeys.location.tr(),
|
||||
style: style25DG600,
|
||||
);
|
||||
|
||||
Widget _buildSubtitle() => Text(
|
||||
'Select your country and region from the dropdown',
|
||||
LocaleKeys.select_country_region.tr(),
|
||||
style: style14MG400,
|
||||
);
|
||||
|
||||
Widget _buildCountryDropDown(OnboardingViewModel viewModel) =>
|
||||
CustomDropdownPicker(
|
||||
hint: 'Select country',
|
||||
icon: _buildSearchIcon(),
|
||||
hint: LocaleKeys.select_country.tr(),
|
||||
selectedItem: viewModel.selectedCountry,
|
||||
items: (value, props) => viewModel.getCountries(),
|
||||
items: (value, props) => viewModel.countries,
|
||||
onChanged: (value) =>
|
||||
_setSelectedCountry(value: value, viewModel: viewModel));
|
||||
|
||||
|
|
@ -142,19 +147,18 @@ class CountryRegionFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
|
||||
Widget _buildRegionDropDown(OnboardingViewModel viewModel) =>
|
||||
CustomDropdownPicker(
|
||||
hint: 'Select region',
|
||||
icon: _buildSearchIcon(),
|
||||
hint: LocaleKeys.select_region.tr(),
|
||||
selectedItem: viewModel.selectedRegion,
|
||||
items: (value, props) => viewModel.getRegions(),
|
||||
onChanged: (value) =>
|
||||
viewModel.setSelectedRegion(value ?? 'Addis Ababa'));
|
||||
items: (value, props) => viewModel.regions,
|
||||
onChanged: (value) => viewModel.setSelectedRegion(value));
|
||||
|
||||
Widget _buildRegionFormField(OnboardingViewModel viewModel) => TextFormField(
|
||||
controller: regionController,
|
||||
onTap: viewModel.setRegionFocus,
|
||||
decoration: inputDecoration(
|
||||
hint: 'Enter Your City',
|
||||
focus: viewModel.focusRegion,
|
||||
hint: LocaleKeys.enter_your_city.tr(),
|
||||
filled: regionController.text.isNotEmpty),
|
||||
);
|
||||
|
||||
|
|
@ -181,18 +185,26 @@ class CountryRegionFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
Widget _buildContinueButton(OnboardingViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
text: 'Continue',
|
||||
borderRadius: 12,
|
||||
foregroundColor: kcWhite,
|
||||
onTap: !viewModel.dropdownRegion
|
||||
text: LocaleKeys.cont.tr(),
|
||||
onTap: viewModel.selectedCountry != null
|
||||
? !viewModel.dropdownRegion
|
||||
? regionController.text.isNotEmpty
|
||||
? () => _next(viewModel)
|
||||
: null
|
||||
: () => _next(viewModel),
|
||||
backgroundColor: !viewModel.dropdownRegion
|
||||
: viewModel.selectedRegion != null
|
||||
? () => _next(viewModel)
|
||||
: null
|
||||
: null,
|
||||
backgroundColor: viewModel.selectedCountry != null
|
||||
? !viewModel.dropdownRegion
|
||||
? regionController.text.isNotEmpty
|
||||
? kcPrimaryColor
|
||||
: kcPrimaryColor.withOpacity(0.1)
|
||||
: kcPrimaryColor,
|
||||
: viewModel.selectedRegion != null
|
||||
? kcPrimaryColor
|
||||
: kcPrimaryColor.withOpacity(0.1)
|
||||
: kcPrimaryColor.withOpacity(0.1),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import 'package:easy_localization/easy_localization.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/translations/locale_keys.g.dart';
|
||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
||||
|
|
@ -20,7 +22,7 @@ class EducationalBackgroundFormScreen
|
|||
FocusManager.instance.primaryFocus?.unfocus();
|
||||
|
||||
Map<String, dynamic> data = {
|
||||
'education_level': viewModel.selectedEducationalBackground
|
||||
'education_level': viewModel.selectedEducationalBackground?.code
|
||||
};
|
||||
viewModel.addUserData(data);
|
||||
|
||||
|
|
@ -88,22 +90,20 @@ class EducationalBackgroundFormScreen
|
|||
showBackButton: true,
|
||||
showLanguageSelection: true,
|
||||
onPop: () => _pop(viewModel),
|
||||
language: viewModel.selectedLanguage['code'],
|
||||
onLanguage: () async => await viewModel.navigateToLanguage(),
|
||||
language: viewModel.selectedLanguage['code'] == 'am'
|
||||
? 'አማ'
|
||||
: viewModel.selectedLanguage['code'],
|
||||
);
|
||||
|
||||
Widget _buildTitle() => const Text(
|
||||
'What’s your current educational level?',
|
||||
style: TextStyle(
|
||||
fontSize: 25,
|
||||
color: kcDarkGrey,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
Widget _buildTitle() => Text(
|
||||
LocaleKeys.educational_background.tr(),
|
||||
style: style25DG600,
|
||||
);
|
||||
|
||||
Widget _buildSubtitle() => const Text(
|
||||
'This helps us tailor your lessons to your experience.',
|
||||
style: TextStyle(color: kcMediumGrey),
|
||||
Widget _buildSubtitle() => Text(
|
||||
LocaleKeys.education_for_personalization.tr(),
|
||||
style: style14MG400,
|
||||
);
|
||||
|
||||
Widget _buildEducationalLevels(OnboardingViewModel viewModel) =>
|
||||
|
|
@ -112,7 +112,7 @@ class EducationalBackgroundFormScreen
|
|||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: viewModel.educationalBackgrounds.length,
|
||||
itemBuilder: (context, index) => _buildEducationalLevel(
|
||||
title: viewModel.educationalBackgrounds[index],
|
||||
title: viewModel.educationalBackgrounds[index].label ?? '',
|
||||
selected: viewModel.isSelectedEducationalBackground(
|
||||
viewModel.educationalBackgrounds[index]),
|
||||
onTap: () => viewModel.setSelectedEducationalBackground(
|
||||
|
|
@ -138,9 +138,9 @@ class EducationalBackgroundFormScreen
|
|||
Widget _buildContinueButton(OnboardingViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
text: 'Continue',
|
||||
borderRadius: 12,
|
||||
foregroundColor: kcWhite,
|
||||
text: LocaleKeys.cont.tr(),
|
||||
onTap: viewModel.selectedEducationalBackground != null
|
||||
? () => _next(viewModel)
|
||||
: null,
|
||||
|
|
|
|||
|
|
@ -90,18 +90,20 @@ class FullNameFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
Widget _buildAppBar(OnboardingViewModel viewModel) => LargeAppBar(
|
||||
showBackButton: false,
|
||||
showLanguageSelection: true,
|
||||
language: viewModel.selectedLanguage['code'],
|
||||
onLanguage: () async => await viewModel.navigateToLanguage(),
|
||||
language: viewModel.selectedLanguage['code'] == 'am'
|
||||
? 'አማ'
|
||||
: viewModel.selectedLanguage['code'],
|
||||
);
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
'${LocaleKeys.what_should_we_call_you.tr()} 😊',
|
||||
style:style25DG600,
|
||||
style: style25DG600,
|
||||
);
|
||||
|
||||
Widget _buildSubtitle() => Text(
|
||||
LocaleKeys.name_for_personalization.tr(),
|
||||
style:style14MG400,
|
||||
style: style14MG400,
|
||||
);
|
||||
|
||||
Widget _buildFullNameFormField(OnboardingViewModel viewModel) =>
|
||||
|
|
|
|||
|
|
@ -82,8 +82,10 @@ class GenderFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
showBackButton: true,
|
||||
showLanguageSelection: true,
|
||||
onPop: () => _pop(viewModel),
|
||||
language: viewModel.selectedLanguage['code'],
|
||||
onLanguage: () async => await viewModel.navigateToLanguage(),
|
||||
language: viewModel.selectedLanguage['code'] == 'am'
|
||||
? 'አማ'
|
||||
: viewModel.selectedLanguage['code'],
|
||||
);
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
|
|
@ -98,14 +100,22 @@ class GenderFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
|
||||
Widget _buildAgeGroups(OnboardingViewModel viewModel) => ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: viewModel.selectedLanguage['code'] == 'አማ'
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: viewModel.selectedLanguage['code'] == 'am'
|
||||
? viewModel.gendersAm.length
|
||||
: viewModel.gendersEn.length,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) => _buildAgeGroup(
|
||||
title:viewModel.selectedLanguage['code'] == 'አማ' ? viewModel.gendersAm[index]:viewModel.gendersEn[index],
|
||||
selected: viewModel.isSelectedGender(viewModel.selectedLanguage['code'] == 'አማ' ? viewModel.gendersAm[index]: viewModel.gendersEn[index]),
|
||||
onTap: () => viewModel.setSelectedGender(viewModel.selectedLanguage['code'] == 'አማ' ? viewModel.gendersAm[index]: viewModel.gendersEn[index]),
|
||||
selected: viewModel.isSelectedGender(
|
||||
viewModel.selectedLanguage['code'] == 'am'
|
||||
? viewModel.gendersAm[index]
|
||||
: viewModel.gendersEn[index]),
|
||||
onTap: () => viewModel.setSelectedGender(
|
||||
viewModel.selectedLanguage['code'] == 'am'
|
||||
? viewModel.gendersAm[index]
|
||||
: viewModel.gendersEn[index]),
|
||||
title: viewModel.selectedLanguage['code'] == 'am'
|
||||
? viewModel.gendersAm[index]
|
||||
: viewModel.gendersEn[index],
|
||||
),
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import 'package:easy_localization/easy_localization.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/translations/locale_keys.g.dart';
|
||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||
import 'package:yimaru_app/ui/views/onboarding/onboarding_view.form.dart';
|
||||
|
|
@ -9,13 +11,9 @@ import 'package:yimaru_app/ui/widgets/custom_small_radio_button.dart';
|
|||
import 'package:yimaru_app/ui/widgets/large_app_bar.dart';
|
||||
|
||||
class LanguageGoalFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
||||
final TextEditingController languageGoalController;
|
||||
|
||||
const LanguageGoalFormScreen(
|
||||
{super.key, required this.languageGoalController});
|
||||
const LanguageGoalFormScreen({super.key});
|
||||
|
||||
void _pop(OnboardingViewModel viewModel) {
|
||||
languageGoalController.clear();
|
||||
viewModel.resetLanguageGoalFormScreen();
|
||||
viewModel.goBack();
|
||||
}
|
||||
|
|
@ -24,8 +22,7 @@ class LanguageGoalFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
FocusManager.instance.primaryFocus?.unfocus();
|
||||
|
||||
Map<String, dynamic> data = {
|
||||
'language_goal':
|
||||
viewModel.selectedLanguageGoal ?? languageGoalController.text,
|
||||
'language_goal': viewModel.selectedLanguageGoal?.code,
|
||||
};
|
||||
viewModel.addUserData(data);
|
||||
|
||||
|
|
@ -87,15 +84,6 @@ class LanguageGoalFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
_buildSubtitle(),
|
||||
verticalSpaceMedium,
|
||||
_buildReasons(viewModel),
|
||||
if (viewModel.showLanguageGoalTextBox) _buildReasonFormField(viewModel),
|
||||
if (viewModel.showLanguageGoalTextBox &&
|
||||
viewModel.hasLanguageGoalValidationMessage &&
|
||||
viewModel.focusLanguageGoal)
|
||||
verticalSpaceTiny,
|
||||
if (viewModel.showLanguageGoalTextBox &&
|
||||
viewModel.hasLanguageGoalValidationMessage &&
|
||||
viewModel.focusLanguageGoal)
|
||||
_buildReasonValidatorWrapper(viewModel),
|
||||
verticalSpaceMedium,
|
||||
];
|
||||
|
||||
|
|
@ -103,17 +91,19 @@ class LanguageGoalFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
showBackButton: true,
|
||||
showLanguageSelection: true,
|
||||
onPop: () => _pop(viewModel),
|
||||
language: viewModel.selectedLanguage['code'],
|
||||
onLanguage: () async => await viewModel.navigateToLanguage(),
|
||||
language: viewModel.selectedLanguage['code'] == 'am'
|
||||
? 'አማ'
|
||||
: viewModel.selectedLanguage['code'],
|
||||
);
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
'What’s your main goal for improving your English?',
|
||||
LocaleKeys.language_goal.tr(),
|
||||
style: style25DG600,
|
||||
);
|
||||
|
||||
Widget _buildSubtitle() => Text(
|
||||
'Your goal helps us tailor your learning journey.',
|
||||
LocaleKeys.your_goal.tr(),
|
||||
style: style14MG400,
|
||||
);
|
||||
|
||||
|
|
@ -123,7 +113,7 @@ class LanguageGoalFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
itemCount: viewModel.languageGoals.length,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) => _buildLanguageGoal(
|
||||
title: viewModel.languageGoals[index],
|
||||
title: viewModel.languageGoals[index].label ?? '',
|
||||
selected:
|
||||
viewModel.isSelectedLanguageGoal(viewModel.languageGoals[index]),
|
||||
onTap: () =>
|
||||
|
|
@ -141,26 +131,6 @@ class LanguageGoalFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
selected: selected,
|
||||
);
|
||||
|
||||
Widget _buildReasonFormField(OnboardingViewModel viewModel) => TextFormField(
|
||||
maxLines: 3,
|
||||
controller: languageGoalController,
|
||||
onTap: viewModel.setLanguageGoalFocus,
|
||||
decoration: inputDecoration(
|
||||
focus: true,
|
||||
hint: 'Write your goal…',
|
||||
filled: languageGoalController.text.isNotEmpty),
|
||||
);
|
||||
|
||||
Widget _buildReasonValidatorWrapper(OnboardingViewModel viewModel) =>
|
||||
viewModel.hasLanguageGoalValidationMessage
|
||||
? _buildReasonValidator(viewModel)
|
||||
: Container();
|
||||
|
||||
Widget _buildReasonValidator(OnboardingViewModel viewModel) => Text(
|
||||
viewModel.languageGoalValidationMessage!,
|
||||
style: style12R700,
|
||||
);
|
||||
|
||||
Widget _buildContinueButtonWrapper(OnboardingViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.only(bottom: 50),
|
||||
child: _buildContinueButton(viewModel),
|
||||
|
|
@ -169,21 +139,13 @@ class LanguageGoalFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
Widget _buildContinueButton(OnboardingViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
text: 'Continue',
|
||||
borderRadius: 12,
|
||||
foregroundColor: kcWhite,
|
||||
text: LocaleKeys.cont.tr(),
|
||||
onTap: viewModel.selectedLanguageGoal != null
|
||||
? viewModel.selectedLanguageGoal?.toLowerCase() == 'other'
|
||||
? languageGoalController.text.isNotEmpty
|
||||
? () => _next(viewModel)
|
||||
: null
|
||||
: () => _next(viewModel)
|
||||
: null,
|
||||
backgroundColor: viewModel.selectedLanguageGoal != null
|
||||
? viewModel.selectedLanguageGoal?.toLowerCase() == 'other'
|
||||
? languageGoalController.text.isNotEmpty
|
||||
? kcPrimaryColor
|
||||
: kcPrimaryColor.withOpacity(0.1)
|
||||
: kcPrimaryColor
|
||||
: kcPrimaryColor.withOpacity(0.1));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:iconsax/iconsax.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||
import 'package:yimaru_app/ui/common/translations/locale_keys.g.dart';
|
||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
||||
|
|
@ -32,7 +34,7 @@ class LearningGoalFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
FocusManager.instance.primaryFocus?.unfocus();
|
||||
|
||||
Map<String, dynamic> data = {
|
||||
'learning_goal': viewModel.selectedLearningGoal,
|
||||
'learning_goal': viewModel.selectedLearningGoal?.code,
|
||||
};
|
||||
viewModel.addUserData(data);
|
||||
|
||||
|
|
@ -98,17 +100,20 @@ class LearningGoalFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
showBackButton: true,
|
||||
showLanguageSelection: true,
|
||||
onPop: () => _pop(viewModel),
|
||||
language: viewModel.selectedLanguage['code'],
|
||||
onLanguage: () async => await viewModel.navigateToLanguage(),
|
||||
language: viewModel.selectedLanguage['code'] == 'am'
|
||||
? 'አማ'
|
||||
: viewModel.selectedLanguage['code'],
|
||||
);
|
||||
|
||||
Widget _buildTitle(OnboardingViewModel viewModel) => Text.rich(
|
||||
TextSpan(
|
||||
text: 'Hi ${viewModel.userData['first_name']},',
|
||||
text:
|
||||
'${LocaleKeys.hello.tr()} ${viewModel.userData['first_name']},',
|
||||
style: style18P600.copyWith(fontSize: 22),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: ' Choose your learning goal.',
|
||||
text: ' ${LocaleKeys.learning_goal.tr()}',
|
||||
style: style16DG600.copyWith(fontSize: 22),
|
||||
)
|
||||
]),
|
||||
|
|
@ -119,27 +124,21 @@ class LearningGoalFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
itemCount: viewModel.learningGoals.length,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) => _buildLearningGoal(
|
||||
title: viewModel.learningGoals[index]['title'],
|
||||
icon: getIcon(viewModel.learningGoals[index]['icon']),
|
||||
subtitle: viewModel.learningGoals[index]['subtitle'],
|
||||
selected: viewModel
|
||||
.isSelectedLearningGoal(viewModel.learningGoals[index]['title']),
|
||||
onTap: () => viewModel
|
||||
.setSelectedLearningGoal(viewModel.learningGoals[index]['title']),
|
||||
title: viewModel.learningGoals[index].label ?? '',
|
||||
selected:
|
||||
viewModel.isSelectedLearningGoal(viewModel.learningGoals[index]),
|
||||
onTap: () =>
|
||||
viewModel.setSelectedLearningGoal(viewModel.learningGoals[index]),
|
||||
),
|
||||
);
|
||||
|
||||
Widget _buildLearningGoal(
|
||||
{required String title,
|
||||
required bool selected,
|
||||
required IconData icon,
|
||||
required String subtitle,
|
||||
required GestureTapCallback onTap}) =>
|
||||
CustomLargeRadioButton(
|
||||
icon: icon,
|
||||
title: title,
|
||||
onTap: onTap,
|
||||
subtitle: subtitle,
|
||||
selected: selected,
|
||||
);
|
||||
|
||||
|
|
@ -151,9 +150,9 @@ class LearningGoalFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
Widget _buildContinueButton(OnboardingViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
text: 'Continue',
|
||||
borderRadius: 12,
|
||||
foregroundColor: kcWhite,
|
||||
text: LocaleKeys.cont.tr(),
|
||||
onTap: viewModel.selectedLearningGoal != null
|
||||
? () => _next(viewModel)
|
||||
: null,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import 'package:easy_localization/easy_localization.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/translations/locale_keys.g.dart';
|
||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||
import 'package:yimaru_app/ui/views/onboarding/onboarding_viewmodel.dart';
|
||||
|
|
@ -19,7 +21,9 @@ class OccupationFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
Future<void> _next(OnboardingViewModel viewModel) async {
|
||||
FocusManager.instance.primaryFocus?.unfocus();
|
||||
|
||||
Map<String, dynamic> data = {'occupation': viewModel.selectedOccupation};
|
||||
Map<String, dynamic> data = {
|
||||
'occupation': viewModel.selectedOccupation?.code
|
||||
};
|
||||
viewModel.addUserData(data);
|
||||
|
||||
viewModel.next();
|
||||
|
|
@ -86,28 +90,30 @@ class OccupationFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
showBackButton: true,
|
||||
showLanguageSelection: true,
|
||||
onPop: () => _pop(viewModel),
|
||||
language: viewModel.selectedLanguage['code'],
|
||||
onLanguage: () async => await viewModel.navigateToLanguage(),
|
||||
language: viewModel.selectedLanguage['code'] == 'am'
|
||||
? 'አማ'
|
||||
: viewModel.selectedLanguage['code'],
|
||||
);
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
'What’s your occupation?',
|
||||
LocaleKeys.your_occupation.tr(),
|
||||
style: style25DG600,
|
||||
);
|
||||
|
||||
Widget _buildSubtitle() => Text(
|
||||
'We’ll personalize your learning experience based on your occupation.',
|
||||
LocaleKeys.occupation_for_personalization.tr(),
|
||||
style: style14MG400,
|
||||
);
|
||||
|
||||
Widget _buildOccupationDropdown(OnboardingViewModel viewModel) =>
|
||||
CustomDropdownPicker(
|
||||
hint: 'Select occupation',
|
||||
icon: _buildSearchIcon(),
|
||||
hint: LocaleKeys.select_occupation.tr(),
|
||||
selectedItem: viewModel.selectedOccupation,
|
||||
items: (value, props) => viewModel.getOccupations(),
|
||||
onChanged: (value) => viewModel.setSelectedOccupation(
|
||||
value ?? 'Students (High school & University)'));
|
||||
items: (value, props) => viewModel.occupations,
|
||||
onChanged: (value) => viewModel.setSelectedOccupation(value));
|
||||
|
||||
Icon _buildSearchIcon() => const Icon(
|
||||
Icons.search,
|
||||
color: kcPrimaryColor,
|
||||
|
|
@ -121,9 +127,14 @@ class OccupationFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
Widget _buildContinueButton(OnboardingViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
text: 'Continue',
|
||||
borderRadius: 12,
|
||||
foregroundColor: kcWhite,
|
||||
onTap: () => _next(viewModel),
|
||||
backgroundColor: kcPrimaryColor);
|
||||
text: LocaleKeys.cont.tr(),
|
||||
onTap: viewModel.selectedOccupation != null
|
||||
? () => _next(viewModel)
|
||||
: null,
|
||||
backgroundColor: viewModel.selectedOccupation != null
|
||||
? kcPrimaryColor
|
||||
: kcPrimaryColor.withOpacity(0.1),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||
import 'package:yimaru_app/ui/common/translations/locale_keys.g.dart';
|
||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||
import 'package:yimaru_app/ui/views/onboarding/onboarding_view.form.dart';
|
||||
|
|
@ -10,12 +11,9 @@ import 'package:yimaru_app/ui/widgets/custom_small_radio_button.dart';
|
|||
import 'package:yimaru_app/ui/widgets/large_app_bar.dart';
|
||||
|
||||
class TopicFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
||||
final TextEditingController topicController;
|
||||
|
||||
const TopicFormScreen({super.key, required this.topicController});
|
||||
const TopicFormScreen({super.key});
|
||||
|
||||
void _pop(OnboardingViewModel viewModel) {
|
||||
topicController.clear();
|
||||
viewModel.resetTopicFormScreen();
|
||||
viewModel.goBack();
|
||||
}
|
||||
|
|
@ -26,8 +24,8 @@ class TopicFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
Map<String, dynamic> data = {
|
||||
'profile_completed': true,
|
||||
'preferred_language': 'en',
|
||||
'favoutite_topic': viewModel.selectedTopic?.code,
|
||||
'birth_day': DateFormat('yyyy-MM-dd').format(DateTime.now()),
|
||||
'favoutite_topic': viewModel.selectedTopic ?? topicController.text,
|
||||
};
|
||||
viewModel.addUserData(data);
|
||||
|
||||
|
|
@ -58,8 +56,10 @@ class TopicFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
showBackButton: true,
|
||||
showLanguageSelection: true,
|
||||
onPop: () => _pop(viewModel),
|
||||
language: viewModel.selectedLanguage['code'],
|
||||
onLanguage: () async => await viewModel.navigateToLanguage(),
|
||||
language: viewModel.selectedLanguage['code'] == 'am'
|
||||
? 'አማ'
|
||||
: viewModel.selectedLanguage['code'],
|
||||
);
|
||||
|
||||
Widget _buildExpandedBody(OnboardingViewModel viewModel) =>
|
||||
|
|
@ -97,25 +97,16 @@ class TopicFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
_buildSubtitle(),
|
||||
verticalSpaceMedium,
|
||||
_buildTopics(viewModel),
|
||||
if (viewModel.showTopicTextBox) _buildTopicFormField(viewModel),
|
||||
if (viewModel.showTopicTextBox &&
|
||||
viewModel.hasTopicValidationMessage &&
|
||||
viewModel.focusTopic)
|
||||
verticalSpaceTiny,
|
||||
if (viewModel.showTopicTextBox &&
|
||||
viewModel.hasTopicValidationMessage &&
|
||||
viewModel.focusTopic)
|
||||
_buildTopicWrapper(viewModel),
|
||||
verticalSpaceMedium,
|
||||
];
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
'Which topics interest you most?',
|
||||
LocaleKeys.topic_interest.tr(),
|
||||
style: style25DG600,
|
||||
);
|
||||
|
||||
Widget _buildSubtitle() => Text(
|
||||
'Your favorite topics help us create fun, relatable lessons.',
|
||||
LocaleKeys.favourite_topic.tr(),
|
||||
style: style14MG400,
|
||||
);
|
||||
|
||||
|
|
@ -125,7 +116,7 @@ class TopicFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
itemCount: viewModel.topics.length,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) => _buildTopic(
|
||||
title: viewModel.topics[index],
|
||||
title: viewModel.topics[index].label ?? '',
|
||||
selected: viewModel.isSelectedTopic(viewModel.topics[index]),
|
||||
onTap: () => viewModel.setSelectedTopic(viewModel.topics[index]),
|
||||
),
|
||||
|
|
@ -141,26 +132,6 @@ class TopicFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
selected: selected,
|
||||
);
|
||||
|
||||
Widget _buildTopicFormField(OnboardingViewModel viewModel) => TextFormField(
|
||||
maxLines: 3,
|
||||
controller: topicController,
|
||||
onTap: viewModel.setTopicsFocus,
|
||||
decoration: inputDecoration(
|
||||
focus: true,
|
||||
hint: 'Write you interest…',
|
||||
filled: topicController.text.isNotEmpty),
|
||||
);
|
||||
|
||||
Widget _buildTopicWrapper(OnboardingViewModel viewModel) =>
|
||||
viewModel.hasTopicValidationMessage
|
||||
? _buildTopicValidator(viewModel)
|
||||
: Container();
|
||||
|
||||
Widget _buildTopicValidator(OnboardingViewModel viewModel) => Text(
|
||||
viewModel.topicValidationMessage!,
|
||||
style: style12R700,
|
||||
);
|
||||
|
||||
Widget _buildContinueButtonWrapper(OnboardingViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.only(bottom: 50),
|
||||
child: _buildContinueButton(viewModel),
|
||||
|
|
@ -169,21 +140,13 @@ class TopicFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
|||
Widget _buildContinueButton(OnboardingViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
text: 'Continue',
|
||||
borderRadius: 12,
|
||||
foregroundColor: kcWhite,
|
||||
text: LocaleKeys.cont.tr(),
|
||||
onTap: viewModel.selectedTopic != null
|
||||
? viewModel.selectedTopic?.toLowerCase() == 'other'
|
||||
? topicController.text.isNotEmpty
|
||||
? () async => await _next(viewModel)
|
||||
: null
|
||||
: () async => await _next(viewModel)
|
||||
: null,
|
||||
backgroundColor: viewModel.selectedTopic != null
|
||||
? viewModel.selectedTopic?.toLowerCase() == 'other'
|
||||
? topicController.text.isNotEmpty
|
||||
? kcPrimaryColor
|
||||
: kcPrimaryColor.withOpacity(0.1)
|
||||
: kcPrimaryColor
|
||||
: kcPrimaryColor.withOpacity(0.1));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import '../../common/app_colors.dart';
|
|||
import '../../common/enmus.dart';
|
||||
import '../../common/ui_helpers.dart';
|
||||
import '../../common/validators/form_validator.dart';
|
||||
import '../../widgets/custom_dropdown.dart';
|
||||
import '../../widgets/custom_elevated_button.dart';
|
||||
import '../../widgets/image_picker_option.dart';
|
||||
import '../../widgets/page_loading_indicator.dart';
|
||||
|
|
@ -202,7 +201,7 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
|
|||
verticalSpaceMedium,
|
||||
_buildCountryDropdownLabel(),
|
||||
verticalSpaceSmall,
|
||||
_buildCountryDropdown(viewModel),
|
||||
// _buildCountryDropdown(viewModel),
|
||||
verticalSpaceMedium,
|
||||
_buildRegionFormFieldWrapper(viewModel),
|
||||
verticalSpaceMedium,
|
||||
|
|
@ -527,13 +526,13 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
|
|||
label: LocaleKeys.country.tr(),
|
||||
);
|
||||
|
||||
Widget _buildCountryDropdown(ProfileDetailViewModel viewModel) =>
|
||||
CustomDropdownPicker(
|
||||
hint: 'Select country',
|
||||
selectedItem: viewModel.selectedCountry,
|
||||
items: (value, props) => viewModel.getCountries(),
|
||||
onChanged: (value) => viewModel.setSelectedCountry(value ?? 'Ethiopia'),
|
||||
);
|
||||
// Widget _buildCountryDropdown(ProfileDetailViewModel viewModel) =>
|
||||
// CustomDropdownPicker(
|
||||
// hint: 'Select country',
|
||||
// selectedItem: viewModel.selectedCountry,
|
||||
// items: (value, props) => viewModel.getCountries(),
|
||||
// onChanged: (value) => viewModel.setSelectedCountry(value ?? 'Ethiopia'),
|
||||
// );
|
||||
|
||||
Widget _buildRegionFormFieldWrapper(ProfileDetailViewModel viewModel) =>
|
||||
Column(
|
||||
|
|
@ -565,18 +564,19 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
|
|||
);
|
||||
|
||||
Widget _buildRegionFormState(ProfileDetailViewModel viewModel) =>
|
||||
viewModel.dropdownRegion
|
||||
? _buildRegionDropDown(viewModel)
|
||||
: _buildRegionFormField(viewModel);
|
||||
|
||||
Widget _buildRegionDropDown(ProfileDetailViewModel viewModel) =>
|
||||
CustomDropdownPicker(
|
||||
icon: _buildSearchIcon(),
|
||||
hint:LocaleKeys.select_region.tr(),
|
||||
selectedItem: viewModel.selectedRegion,
|
||||
items: (value, props) => viewModel.getRegions(),
|
||||
onChanged: (value) =>
|
||||
viewModel.setSelectedRegion(value ?? 'Addis Ababa'));
|
||||
// viewModel.dropdownRegion
|
||||
// ? _buildRegionDropDown(viewModel)
|
||||
// :
|
||||
_buildRegionFormField(viewModel);
|
||||
//
|
||||
// Widget _buildRegionDropDown(ProfileDetailViewModel viewModel) =>
|
||||
// CustomDropdownPicker(
|
||||
// icon: _buildSearchIcon(),
|
||||
// hint:LocaleKeys.select_region.tr(),
|
||||
// selectedItem: viewModel.selectedRegion,
|
||||
// items: (value, props) => viewModel.getRegions(),
|
||||
// onChanged: (value) =>
|
||||
// viewModel.setSelectedRegion(value ?? 'Addis Ababa'));
|
||||
|
||||
Widget _buildRegionFormField(ProfileDetailViewModel viewModel) =>
|
||||
TextFormField(
|
||||
|
|
@ -584,7 +584,7 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
|
|||
onTap: viewModel.setRegionFocus,
|
||||
decoration: inputDecoration(
|
||||
focus: viewModel.focusRegion,
|
||||
hint:LocaleKeys.enter_your_city.tr(),
|
||||
hint: LocaleKeys.enter_your_city.tr(),
|
||||
filled: regionController.text.isNotEmpty),
|
||||
);
|
||||
|
||||
|
|
@ -611,7 +611,7 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
|
|||
[
|
||||
_buildOccupationDropdownLabel(),
|
||||
verticalSpaceSmall,
|
||||
_buildOccupationDropdown(viewModel)
|
||||
// _buildOccupationDropdown(viewModel)
|
||||
];
|
||||
|
||||
Widget _buildOccupationDropdownLabel() => CustomFormLabel(
|
||||
|
|
@ -619,14 +619,14 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
|
|||
label: LocaleKeys.occupation.tr(),
|
||||
);
|
||||
|
||||
Widget _buildOccupationDropdown(ProfileDetailViewModel viewModel) =>
|
||||
CustomDropdownPicker(
|
||||
icon: _buildSearchIcon(),
|
||||
hint:LocaleKeys.select_occupation.tr(),
|
||||
selectedItem: viewModel.selectedOccupation,
|
||||
items: (value, props) => viewModel.getOccupations(),
|
||||
onChanged: (value) => viewModel.setSelectedOccupation(
|
||||
value ?? 'Students (High school & University)'));
|
||||
// Widget _buildOccupationDropdown(ProfileDetailViewModel viewModel) =>
|
||||
// CustomDropdownPicker(
|
||||
// icon: _buildSearchIcon(),
|
||||
// hint:LocaleKeys.select_occupation.tr(),
|
||||
// selectedItem: viewModel.selectedOccupation,
|
||||
// items: (value, props) => viewModel.getOccupations(),
|
||||
// onChanged: (value) => viewModel.setSelectedOccupation(
|
||||
// value ?? 'Students (High school & University)'));
|
||||
Icon _buildSearchIcon() => const Icon(
|
||||
Icons.search,
|
||||
color: kcPrimaryColor,
|
||||
|
|
@ -663,7 +663,7 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
|
|||
borderRadius: 12,
|
||||
onTap: viewModel.pop,
|
||||
backgroundColor: kcWhite,
|
||||
text:LocaleKeys.cancel.tr(),
|
||||
text: LocaleKeys.cancel.tr(),
|
||||
borderColor: kcPrimaryColor,
|
||||
foregroundColor: kcPrimaryColor,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -75,8 +75,10 @@ class CreatePasswordScreen extends ViewModelWidget<RegisterViewModel> {
|
|||
showBackButton: true,
|
||||
onPop: viewModel.goBack,
|
||||
showLanguageSelection: true,
|
||||
language: viewModel.selectedLanguage['code'],
|
||||
onLanguage: () async => await viewModel.navigateToLanguage(),
|
||||
language: viewModel.selectedLanguage['code'] == 'am'
|
||||
? 'አማ'
|
||||
: viewModel.selectedLanguage['code'],
|
||||
);
|
||||
|
||||
Widget _buildExpandedBody(RegisterViewModel viewModel) =>
|
||||
|
|
|
|||
|
|
@ -91,8 +91,10 @@ class RegisterWithEmailScreen extends ViewModelWidget<RegisterViewModel> {
|
|||
showBackButton: true,
|
||||
onPop: viewModel.goBack,
|
||||
showLanguageSelection: true,
|
||||
language: viewModel.selectedLanguage['code'],
|
||||
onLanguage: () async => await viewModel.navigateToLanguage(),
|
||||
language: viewModel.selectedLanguage['code'] == 'am'
|
||||
? 'አማ'
|
||||
: viewModel.selectedLanguage['code'],
|
||||
);
|
||||
|
||||
Widget _buildExpandedBody(
|
||||
|
|
|
|||
|
|
@ -94,8 +94,10 @@ class RegisterWithPhoneNumberScreen extends ViewModelWidget<RegisterViewModel> {
|
|||
showBackButton: true,
|
||||
onPop: viewModel.goBack,
|
||||
showLanguageSelection: true,
|
||||
language: viewModel.selectedLanguage['code'],
|
||||
onLanguage: () async => await viewModel.navigateToLanguage(),
|
||||
language: viewModel.selectedLanguage['code'] == 'am'
|
||||
? 'አማ'
|
||||
: viewModel.selectedLanguage['code'],
|
||||
);
|
||||
|
||||
Widget _buildExpandedBody(
|
||||
|
|
|
|||
|
|
@ -105,8 +105,10 @@ class RegistrationOtpScreen extends ViewModelWidget<RegisterViewModel> {
|
|||
showBackButton: true,
|
||||
onPop: viewModel.goBack,
|
||||
showLanguageSelection: true,
|
||||
language: viewModel.selectedLanguage['code'],
|
||||
onLanguage: () async => await viewModel.navigateToLanguage(),
|
||||
language: viewModel.selectedLanguage['code'] == 'am'
|
||||
? 'አማ'
|
||||
: viewModel.selectedLanguage['code'],
|
||||
);
|
||||
|
||||
Widget _buildExpandedBody(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:stacked/stacked.dart';
|
||||
import 'package:stacked_services/stacked_services.dart';
|
||||
import 'package:yimaru_app/services/authentication_service.dart';
|
||||
import 'package:yimaru_app/services/onboarding_service.dart';
|
||||
|
||||
import '../../../app/app.locator.dart';
|
||||
import '../../../app/app.router.dart';
|
||||
|
|
@ -14,15 +15,22 @@ import '../../common/enmus.dart';
|
|||
class StartupViewModel extends ReactiveViewModel {
|
||||
// Dependency injection
|
||||
final _apiService = locator<ApiService>();
|
||||
|
||||
final _statusChecker = locator<StatusCheckerService>();
|
||||
|
||||
final _navigationService = locator<NavigationService>();
|
||||
|
||||
final _onboardingService = locator<OnboardingService>();
|
||||
|
||||
final _localizationService = locator<LocalizationService>();
|
||||
|
||||
final _authenticationService = locator<AuthenticationService>();
|
||||
|
||||
final _imageDownloaderService = locator<ImageDownloaderService>();
|
||||
|
||||
@override
|
||||
List<ListenableServiceMixin> get listenableServices =>
|
||||
[_authenticationService];
|
||||
[_onboardingService, _authenticationService];
|
||||
|
||||
// Current user
|
||||
User? get _user => _authenticationService.user;
|
||||
|
|
@ -81,7 +89,7 @@ class StartupViewModel extends ReactiveViewModel {
|
|||
response = {'data': true, 'status': ResponseStatus.success};
|
||||
}
|
||||
if (response['status'] == ResponseStatus.success && !response['data']) {
|
||||
await replaceWithOnboarding();
|
||||
await etOnboardingFields();
|
||||
} else if (response['status'] == ResponseStatus.success &&
|
||||
response['data']) {
|
||||
await saveProfileStatus(response['data']);
|
||||
|
|
@ -122,4 +130,16 @@ class StartupViewModel extends ReactiveViewModel {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remote api call
|
||||
|
||||
// Onboarding fields
|
||||
Future<void> etOnboardingFields() async {
|
||||
bool response = await _onboardingService.getOnboardingFields();
|
||||
if (response) {
|
||||
await replaceWithOnboarding();
|
||||
} else {
|
||||
await replaceWithFailure();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ class SupportView extends StackedView<SupportViewModel> {
|
|||
Widget _buildAppbar(SupportViewModel viewModel) => SmallAppBar(
|
||||
showBackButton: true,
|
||||
onPop: viewModel.pop,
|
||||
title:LocaleKeys.need_help.tr(),
|
||||
title: LocaleKeys.need_help.tr(),
|
||||
);
|
||||
|
||||
Widget _buildContentWrapper(SupportViewModel viewModel) =>
|
||||
|
|
@ -87,7 +87,7 @@ class SupportView extends StackedView<SupportViewModel> {
|
|||
Widget _buildCallSupport(SupportViewModel viewModel) => SupportCard(
|
||||
icon: Icons.call,
|
||||
color: kcPrimaryColor,
|
||||
title:LocaleKeys.call_support.tr(),
|
||||
title: LocaleKeys.call_support.tr(),
|
||||
subtitle: LocaleKeys.talk_with_support.tr(),
|
||||
onTap: () async => await viewModel.navigateToCallSupport(),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -135,7 +135,10 @@ class TelegramSupportView extends StackedView<TelegramSupportViewModel> {
|
|||
Widget _buildOptionTextDivider() => const OptionTextDivider();
|
||||
|
||||
Widget _buildSearchText() => Text.rich(
|
||||
TextSpan(text: LocaleKeys.search_for.tr(), style: style14DG500, children: [
|
||||
TextSpan(
|
||||
text: LocaleKeys.search_for.tr(),
|
||||
style: style14DG500,
|
||||
children: [
|
||||
TextSpan(
|
||||
style: style14P600,
|
||||
text: ' $kTelegramSupport',
|
||||
|
|
|
|||
|
|
@ -5,12 +5,15 @@ import 'package:flutter/material.dart';
|
|||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||
|
||||
import '../../models/field_option.dart';
|
||||
|
||||
class CustomDropdownPicker extends StatelessWidget {
|
||||
final Icon? icon;
|
||||
final String hint;
|
||||
final String selectedItem;
|
||||
final void Function(String?)? onChanged;
|
||||
final FutureOr<List<String>> Function(String value, LoadProps? props)? items;
|
||||
final FieldOption? selectedItem;
|
||||
final void Function(FieldOption?)? onChanged;
|
||||
final FutureOr<List<FieldOption>> Function(String value, LoadProps? props)?
|
||||
items;
|
||||
|
||||
const CustomDropdownPicker(
|
||||
{super.key,
|
||||
|
|
@ -28,16 +31,18 @@ class CustomDropdownPicker extends StatelessWidget {
|
|||
child: _buildDropDownSearch(),
|
||||
);
|
||||
|
||||
Widget _buildDropDownSearch() => DropdownSearch<String>(
|
||||
Widget _buildDropDownSearch() => DropdownSearch<FieldOption>(
|
||||
onChanged: onChanged,
|
||||
popupProps: _popupProps(),
|
||||
selectedItem: selectedItem,
|
||||
onChanged: (value) => onChanged!(value),
|
||||
itemAsString: (item) => item.label ?? '',
|
||||
decoratorProps: _dropDownDecoratorProps(),
|
||||
compareFn: (item1, item2) => item1.label == item2.label,
|
||||
items: (value, properties) => items!(value, properties),
|
||||
dropdownBuilder: (context, value) => _buildDropdownBuilder(value),
|
||||
items: (value, properties) async => await items!(value, properties),
|
||||
);
|
||||
|
||||
PopupProps<String> _popupProps() => PopupProps.menu(
|
||||
PopupProps<FieldOption> _popupProps() => PopupProps<FieldOption>.menu(
|
||||
showSearchBox: true,
|
||||
showSelectedItems: true,
|
||||
searchFieldProps: _searchFieldProps(),
|
||||
|
|
@ -57,25 +62,25 @@ class CustomDropdownPicker extends StatelessWidget {
|
|||
InputDecoration _popUpDecoration() => InputDecoration(
|
||||
filled: true,
|
||||
hintStyle: style14DG400,
|
||||
fillColor: kcTransparent,
|
||||
errorBorder: searchBorder,
|
||||
focusedBorder: searchBorder,
|
||||
enabledBorder: searchBorder,
|
||||
disabledBorder: searchBorder,
|
||||
focusedErrorBorder: searchBorder,
|
||||
fillColor: const Color(0xfff5e9f4),
|
||||
contentPadding: const EdgeInsets.only(top: 12),
|
||||
prefixIcon: icon != null ? _buildPrefixIcon() : null,
|
||||
);
|
||||
|
||||
Widget _buildPopupProsBuilderWrapper(String value) => Padding(
|
||||
Widget _buildPopupProsBuilderWrapper(FieldOption value) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
child: _buildPopupProsBuilder(value),
|
||||
);
|
||||
|
||||
Widget _buildPopupProsBuilder(String value) => Text(
|
||||
value,
|
||||
Widget _buildPopupProsBuilder(FieldOption value) => Text(
|
||||
value.label ?? '',
|
||||
maxLines: 1,
|
||||
style: const TextStyle(color: kcDarkGrey, fontSize: 14),
|
||||
style: style14DG400,
|
||||
);
|
||||
|
||||
DropDownDecoratorProps _dropDownDecoratorProps() => DropDownDecoratorProps(
|
||||
|
|
@ -91,7 +96,7 @@ class CustomDropdownPicker extends StatelessWidget {
|
|||
focusedBorder: border,
|
||||
enabledBorder: border,
|
||||
disabledBorder: border,
|
||||
hintStyle: style14LG400,
|
||||
hintStyle: style14MG400,
|
||||
fillColor: kcPrimaryColor.withOpacity(0.1),
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
|
||||
|
|
@ -102,8 +107,8 @@ class CustomDropdownPicker extends StatelessWidget {
|
|||
child: icon,
|
||||
);
|
||||
|
||||
Widget _buildDropdownBuilder(String? value) => Text(
|
||||
value ?? hint,
|
||||
Widget _buildDropdownBuilder(FieldOption? value) => Text(
|
||||
value?.label ?? '',
|
||||
maxLines: 1,
|
||||
style: style14DG400,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -5,23 +5,20 @@ import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
|||
class CustomLargeRadioButton extends StatelessWidget {
|
||||
final String title;
|
||||
final bool selected;
|
||||
final IconData icon;
|
||||
final String subtitle;
|
||||
final GestureTapCallback? onTap;
|
||||
|
||||
const CustomLargeRadioButton(
|
||||
{super.key,
|
||||
const CustomLargeRadioButton({
|
||||
super.key,
|
||||
this.onTap,
|
||||
required this.title,
|
||||
required this.icon,
|
||||
required this.selected,
|
||||
required this.subtitle});
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => _buildButtonWrapper();
|
||||
|
||||
Widget _buildButtonWrapper() => Container(
|
||||
height: 125,
|
||||
height: 75,
|
||||
width: double.maxFinite,
|
||||
margin: const EdgeInsets.only(bottom: 15),
|
||||
child: _buildContainerWrapper(),
|
||||
|
|
@ -41,39 +38,24 @@ class CustomLargeRadioButton extends StatelessWidget {
|
|||
color: selected ? kcPrimaryColor : kcPrimaryColor.withOpacity(0.75),
|
||||
),
|
||||
),
|
||||
child: _buildButtonColumnWrapper(),
|
||||
child: _buildButtonRowWrapper(),
|
||||
);
|
||||
|
||||
Widget _buildButtonColumnWrapper() => Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
Widget _buildButtonRowWrapper() => Row(
|
||||
children: _buildButtonRowChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildButtonRowChildren() =>
|
||||
[_buildIconSectionWrapper(), _buildTitle(), _buildSubtitle()];
|
||||
[_buildTitleWrapper(), _buildSelectedCheckBox()];
|
||||
|
||||
Widget _buildIconSectionWrapper() => Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: _buildIconSectionChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildIconSectionChildren() =>
|
||||
[_buildLeadingIcon(), _buildSelectedCheckBox()];
|
||||
|
||||
Widget _buildLeadingIcon() => Icon(
|
||||
icon,
|
||||
size: 25,
|
||||
color: kcPrimaryColor,
|
||||
Widget _buildTitleWrapper() => Expanded(
|
||||
child: _buildTitle(),
|
||||
);
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
title,
|
||||
style: style18DG700,
|
||||
);
|
||||
|
||||
Widget _buildSubtitle() => Text(
|
||||
subtitle,
|
||||
style: const TextStyle(color: kcMediumGrey),
|
||||
maxLines: 1,
|
||||
style: style16DG400,
|
||||
);
|
||||
|
||||
Widget _buildSelectedCheckBox() => Checkbox(
|
||||
|
|
|
|||
|
|
@ -13,18 +13,26 @@ import 'custom_elevated_button.dart';
|
|||
class LearnCourseTile extends ViewModelWidget<LearnCourseViewModel> {
|
||||
final LearnCourse course;
|
||||
final GestureTapCallback? onViewTap;
|
||||
final GestureTapCallback? onLockTap;
|
||||
final GestureTapCallback? onPracticeTap;
|
||||
|
||||
const LearnCourseTile({
|
||||
super.key,
|
||||
this.onViewTap,
|
||||
this.onLockTap,
|
||||
this.onPracticeTap,
|
||||
required this.course,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, LearnCourseViewModel viewModel) =>
|
||||
_buildExpansionTileCard(viewModel);
|
||||
_buildExpansionTileCardWrapper(viewModel);
|
||||
|
||||
Widget _buildExpansionTileCardWrapper(LearnCourseViewModel viewModel) =>
|
||||
GestureDetector(
|
||||
onTap: !(course.access?.isAccessible ?? false) ? onLockTap : null,
|
||||
child: _buildExpansionTileCard(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildExpansionTileCard(LearnCourseViewModel viewModel) => Container(
|
||||
margin: const EdgeInsets.only(bottom: 15),
|
||||
|
|
@ -147,7 +155,7 @@ class LearnCourseTile extends ViewModelWidget<LearnCourseViewModel> {
|
|||
onTap: onViewTap,
|
||||
foregroundColor: kcWhite,
|
||||
backgroundColor: kcPrimaryColor,
|
||||
text:LocaleKeys.view_course.tr(),
|
||||
text: LocaleKeys.view_course.tr(),
|
||||
);
|
||||
|
||||
Widget _buildPracticeButtonWrapper(LearnCourseViewModel viewModel) =>
|
||||
|
|
@ -163,6 +171,6 @@ class LearnCourseTile extends ViewModelWidget<LearnCourseViewModel> {
|
|||
backgroundColor: kcWhite,
|
||||
borderColor: kcPrimaryColor,
|
||||
foregroundColor: kcPrimaryColor,
|
||||
text:LocaleKeys.take_practice.tr() ,
|
||||
text: LocaleKeys.take_practice.tr(),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,7 +130,9 @@ class LearnLessonTile extends ViewModelWidget<LearnLessonViewModel> {
|
|||
);
|
||||
|
||||
Widget _buildProgressText() => Text(
|
||||
(lesson.access?.isCompleted ?? false) ?LocaleKeys.completed.tr() : LocaleKeys.in_progress.tr() ,
|
||||
(lesson.access?.isCompleted ?? false)
|
||||
? LocaleKeys.completed.tr()
|
||||
: LocaleKeys.in_progress.tr(),
|
||||
style: style14P600,
|
||||
);
|
||||
|
||||
|
|
@ -166,7 +168,7 @@ class LearnLessonTile extends ViewModelWidget<LearnLessonViewModel> {
|
|||
width: double.maxFinite,
|
||||
backgroundColor: kcWhite,
|
||||
borderColor: kcPrimaryColor,
|
||||
text:LocaleKeys.practice.tr() ,
|
||||
text: LocaleKeys.practice.tr(),
|
||||
foregroundColor: kcPrimaryColor,
|
||||
);
|
||||
|
||||
|
|
@ -182,7 +184,7 @@ class LearnLessonTile extends ViewModelWidget<LearnLessonViewModel> {
|
|||
onTap: onLessonTap,
|
||||
width: double.maxFinite,
|
||||
foregroundColor: kcWhite,
|
||||
text:LocaleKeys.start.tr() ,
|
||||
text: LocaleKeys.start.tr(),
|
||||
trailingIcon: Icons.play_arrow,
|
||||
backgroundColor: kcPrimaryColor,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -18,10 +18,7 @@ class LearnModuleTile extends ViewModelWidget<LearnModuleViewModel> {
|
|||
final GestureTapCallback? onPracticeTap;
|
||||
|
||||
const LearnModuleTile(
|
||||
{super.key,
|
||||
this.onModuleTap,
|
||||
this.onPracticeTap,
|
||||
required this.module});
|
||||
{super.key, this.onModuleTap, this.onPracticeTap, required this.module});
|
||||
|
||||
Future<void> _showSheet(
|
||||
{required BuildContext context,
|
||||
|
|
@ -54,7 +51,7 @@ class LearnModuleTile extends ViewModelWidget<LearnModuleViewModel> {
|
|||
Stack(
|
||||
children: [
|
||||
_buildExpansionTile(context: context, viewModel: viewModel),
|
||||
_buildContainerShaderState()
|
||||
// _buildContainerShaderState()
|
||||
],
|
||||
);
|
||||
|
||||
|
|
@ -73,14 +70,14 @@ class LearnModuleTile extends ViewModelWidget<LearnModuleViewModel> {
|
|||
shape: Border.all(color: kcTransparent),
|
||||
expandedAlignment: Alignment.centerLeft,
|
||||
collapsedBackgroundColor: kcBackgroundColor,
|
||||
enabled: (module.access?.isAccessible ?? false),
|
||||
//enabled: (module.access?.isAccessible ?? false),
|
||||
controlAffinity: ListTileControlAffinity.trailing,
|
||||
expandedCrossAxisAlignment: CrossAxisAlignment.start,
|
||||
tilePadding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
childrenPadding: const EdgeInsets.fromLTRB(70, 0, 15, 15),
|
||||
initiallyExpanded: (module.access?.isAccessible ?? false),
|
||||
showTrailingIcon:
|
||||
!(module.access?.isAccessible ?? false) ? true : false,
|
||||
//initiallyExpanded: (module.access?.isAccessible ?? false),
|
||||
// showTrailingIcon:
|
||||
// !(module.access?.isAccessible ?? false) ? true : false,
|
||||
children:
|
||||
_buildExpansionTileChildren(context: context, viewModel: viewModel),
|
||||
);
|
||||
|
|
@ -211,9 +208,7 @@ class LearnModuleTile extends ViewModelWidget<LearnModuleViewModel> {
|
|||
backgroundColor: kcWhite,
|
||||
borderColor: kcPrimaryColor,
|
||||
foregroundColor: kcPrimaryColor,
|
||||
text:LocaleKeys.take_practice.tr(),
|
||||
|
||||
// onTap: () async => await viewModel.navigateToLearnPractice(practices),
|
||||
text: LocaleKeys.take_practice.tr(),
|
||||
);
|
||||
|
||||
Widget _buildSheet(LearnModuleViewModel viewModel) => FinishPracticeSheet(
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ class LearnPracticeResultCard extends ViewModelWidget<LearnPracticeViewModel> {
|
|||
Widget _buildSampleResponse() => LearnPracticeAnswerCard(
|
||||
answer: answer,
|
||||
voice: Voice.sample,
|
||||
title:LocaleKeys.sample_answer.tr() ,
|
||||
title: LocaleKeys.sample_answer.tr(),
|
||||
);
|
||||
|
||||
Widget _buildActualResponseWrapper() =>
|
||||
|
|
@ -63,6 +63,5 @@ class LearnPracticeResultCard extends ViewModelWidget<LearnPracticeViewModel> {
|
|||
answer: answer,
|
||||
voice: Voice.recorded,
|
||||
title: LocaleKeys.your_answer.tr(),
|
||||
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,20 +13,12 @@ import 'custom_elevated_button.dart';
|
|||
class LearnProgramTile extends ViewModelWidget<LearnProgramViewModel> {
|
||||
final LearnProgram program;
|
||||
final GestureTapCallback? onTap;
|
||||
final GestureTapCallback? onLockTap;
|
||||
|
||||
const LearnProgramTile(
|
||||
{super.key, this.onTap, this.onLockTap, required this.program});
|
||||
const LearnProgramTile({super.key, this.onTap, required this.program});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, LearnProgramViewModel viewModel) =>
|
||||
_buildExpansionTileCardWrapper(viewModel);
|
||||
|
||||
Widget _buildExpansionTileCardWrapper(LearnProgramViewModel viewModel) =>
|
||||
GestureDetector(
|
||||
// onTap: !(program.access?.isAccessible ?? false) ? onLockTap : null,
|
||||
child: _buildExpansionTileCard(viewModel),
|
||||
);
|
||||
_buildExpansionTileCard(viewModel);
|
||||
|
||||
Widget _buildExpansionTileCard(LearnProgramViewModel viewModel) => Container(
|
||||
margin: const EdgeInsets.only(bottom: 15),
|
||||
|
|
@ -109,7 +101,7 @@ class LearnProgramTile extends ViewModelWidget<LearnProgramViewModel> {
|
|||
Widget _buildProgressStatus() => ProgressStatus(
|
||||
color: kcPrimaryColor,
|
||||
status: (program.access?.isCompleted ?? false)
|
||||
?LocaleKeys.completed.tr()
|
||||
? LocaleKeys.completed.tr()
|
||||
: LocaleKeys.in_progress.tr(),
|
||||
);
|
||||
|
||||
|
|
@ -134,6 +126,6 @@ class LearnProgramTile extends ViewModelWidget<LearnProgramViewModel> {
|
|||
backgroundColor: kcPrimaryColor,
|
||||
text: program.access?.progressPercent == 0
|
||||
? LocaleKeys.start_learning.tr()
|
||||
:LocaleKeys.continue_learning.tr() ,
|
||||
: LocaleKeys.continue_learning.tr(),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,7 +74,10 @@ class ProfileAppBar extends StatelessWidget {
|
|||
[_buildGreetingTitle(), _buildSubtitle()];
|
||||
|
||||
Widget _buildGreetingTitle() => Text.rich(
|
||||
TextSpan(text: '${LocaleKeys.hello.tr()},', style: style14DG600, children: [
|
||||
TextSpan(
|
||||
text: '${LocaleKeys.hello.tr()},',
|
||||
style: style14DG600,
|
||||
children: [
|
||||
TextSpan(
|
||||
text: ' $name!',
|
||||
style: style14P600,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import FlutterMacOS
|
|||
import Foundation
|
||||
|
||||
import audioplayers_darwin
|
||||
import battery_plus
|
||||
import connectivity_plus
|
||||
import file_selector_macos
|
||||
import firebase_core
|
||||
|
|
@ -25,7 +24,6 @@ import wakelock_plus
|
|||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
|
||||
BatteryPlusMacosPlugin.register(with: registry.registrar(forPlugin: "BatteryPlusMacosPlugin"))
|
||||
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
|
||||
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
||||
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
|
||||
|
|
|
|||
74
pubspec.lock
74
pubspec.lock
|
|
@ -113,22 +113,6 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.3.0"
|
||||
battery_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: battery_plus
|
||||
sha256: ad16fcb55b7384be6b4bbc763d5e2031ac7ea62b2d9b6b661490c7b9741155bf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.0"
|
||||
battery_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: battery_plus_platform_interface
|
||||
sha256: e8342c0f32de4b1dfd0223114b6785e48e579bfc398da9471c9179b907fa4910
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
bloc:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -349,10 +333,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: dbus
|
||||
sha256: "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c"
|
||||
sha256: d0c98dcd4f5169878b6cf8f6e0a52403a9dff371a3e2f019697accbf6f44a270
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.11"
|
||||
version: "0.7.12"
|
||||
dio:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -421,10 +405,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: d07d37192dbf97461359c1518788f203b0c9102cfd2c35a716b823741219542c
|
||||
sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
version: "2.2.0"
|
||||
ffi_leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi_leak_tracker
|
||||
sha256: "4093d4ef9ca06ffe2786e73bfb25e22aa92112b9bb4ec941f11e3e6b61489a97"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.2"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -743,10 +735,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: flutter_secure_storage_windows
|
||||
sha256: "3b7c8e068875dfd46719ff57c90d8c459c87f2302ed6b00ff006b3c9fcad1613"
|
||||
sha256: "471951813a97006d899db4948acc654a4f28c440083ea08178935ce20b173ec1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.0"
|
||||
version: "4.2.2"
|
||||
flutter_spinkit:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -1206,21 +1198,21 @@ packages:
|
|||
source: hosted
|
||||
version: "2.2.0"
|
||||
package_info_plus:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: package_info_plus
|
||||
sha256: f69da0d3189a4b4ceaeb1a3defb0f329b3b352517f52bed4290f83d4f06bc08d
|
||||
sha256: "4bf625947f6c7713ee242296a682e23e44823c09cf9d79e4f1238923c92db852"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.0.0"
|
||||
version: "10.1.0"
|
||||
package_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_info_plus_platform_interface
|
||||
sha256: "202a487f08836a592a6bd4f901ac69b3a8f146af552bbd14407b6b41e1c3f086"
|
||||
sha256: db762cb2f4f25ee60fb6359773861b0f199e00b90d237bd85a76a1e806b46ef4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.1"
|
||||
version: "4.1.0"
|
||||
path:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -1682,14 +1674,6 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.2"
|
||||
storage_info:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: storage_info
|
||||
sha256: adbf5fd1a7c2ca977dd828573820db0a0a16f4aa317e0ab72e9b3282eb5bbe42
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1770,14 +1754,6 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
upower:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: upower
|
||||
sha256: cf042403154751180affa1d15614db7fa50234bc2373cd21c3db666c38543ebf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.0"
|
||||
url_launcher:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -1942,18 +1918,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: wakelock_plus
|
||||
sha256: "9296d40c9adbedaba95d1e704f4e0b434be446e2792948d0e4aa977048104228"
|
||||
sha256: "824c5bba0f800e86d32e57d3d1843c531f090005cc89d9a837933e6601093d53"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
version: "1.6.1"
|
||||
wakelock_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: wakelock_plus_platform_interface
|
||||
sha256: "036deb14cd62f558ca3b73006d52ce049fabcdcb2eddfe0bf0fe4e8a943b5cf2"
|
||||
sha256: b13f99e992e7ae6a152e16c5559d3c07ff445b13330192662494e614ca3e7d7b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
version: "1.5.1"
|
||||
watcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -2006,10 +1982,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e
|
||||
sha256: ba6f4bba816c8d7e3c1580e170f3786d216951cc6b94babc3b814c08d2cb2738
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.15.0"
|
||||
version: "6.3.0"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
name: yimaru_app
|
||||
version: 0.1.21+23
|
||||
version: 0.1.22+24
|
||||
publish_to: 'none'
|
||||
description: A new Flutter project.
|
||||
|
||||
|
|
@ -22,8 +22,6 @@ dependencies:
|
|||
flutter_svg: ^2.2.3
|
||||
stacked_shared: any
|
||||
image_picker: ^1.2.1
|
||||
battery_plus: ^7.0.0
|
||||
storage_info: ^1.0.0
|
||||
flutter_html: ^3.0.0
|
||||
email_validator: any
|
||||
audioplayers: ^6.6.0
|
||||
|
|
@ -43,6 +41,7 @@ dependencies:
|
|||
json_serializable: ^6.8.0
|
||||
waveform_recorder: ^1.8.0
|
||||
vimeo_video_player: ^1.0.3
|
||||
package_info_plus: ^10.1.0
|
||||
permission_handler: ^12.0.1
|
||||
firebase_messaging: ^16.1.1
|
||||
cached_network_image: ^3.4.1
|
||||
|
|
|
|||
|
|
@ -22,10 +22,9 @@ import 'package:yimaru_app/services/url_launcher_service.dart';
|
|||
import 'package:yimaru_app/services/phone_caller_service.dart';
|
||||
import 'package:yimaru_app/services/learn_service.dart';
|
||||
import 'package:yimaru_app/services/localization_service.dart';
|
||||
import 'package:yimaru_app/services/onboarding_service.dart';
|
||||
// @stacked-import
|
||||
|
||||
import 'test_helpers.mocks.dart';
|
||||
|
||||
@GenerateMocks(
|
||||
[],
|
||||
customMocks: [
|
||||
|
|
@ -54,10 +53,10 @@ import 'test_helpers.mocks.dart';
|
|||
MockSpec<UrlLauncherService>(onMissingStub: OnMissingStub.returnDefault),
|
||||
MockSpec<UrlLauncherService>(onMissingStub: OnMissingStub.returnDefault),
|
||||
MockSpec<PhoneCallerService>(onMissingStub: OnMissingStub.returnDefault),
|
||||
MockSpec<LearnLessonService>(onMissingStub: OnMissingStub.returnDefault),
|
||||
MockSpec<LearnService>(onMissingStub: OnMissingStub.returnDefault),
|
||||
MockSpec<LearnService>(onMissingStub: OnMissingStub.returnDefault),
|
||||
MockSpec<LocalizationService>(onMissingStub: OnMissingStub.returnDefault),
|
||||
MockSpec<OnboardingService>(onMissingStub: OnMissingStub.returnDefault),
|
||||
// @stacked-mock-spec
|
||||
],
|
||||
)
|
||||
|
|
@ -85,10 +84,10 @@ void registerServices() {
|
|||
getAndRegisterUrlLauncherService();
|
||||
getAndRegisterUrlLauncherService();
|
||||
getAndRegisterPhoneCallerService();
|
||||
getAndRegisterLearnLessonService();
|
||||
getAndRegisterLearnService();
|
||||
getAndRegisterLearnService();
|
||||
getAndRegisterLocalizationService();
|
||||
getAndRegisterOnboardingService();
|
||||
// @stacked-mock-register
|
||||
}
|
||||
|
||||
|
|
@ -285,6 +284,13 @@ MockLocalizationService getAndRegisterLocalizationService() {
|
|||
locator.registerSingleton<LocalizationService>(service);
|
||||
return service;
|
||||
}
|
||||
|
||||
MockOnboardingService getAndRegisterOnboardingService() {
|
||||
_removeRegistrationIfExists<OnboardingService>();
|
||||
final service = MockOnboardingService();
|
||||
locator.registerSingleton<OnboardingService>(service);
|
||||
return service;
|
||||
}
|
||||
// @stacked-mock-create
|
||||
|
||||
void _removeRegistrationIfExists<T extends Object>() {
|
||||
|
|
|
|||
11
test/services/onboarding_service_test.dart
Normal file
11
test/services/onboarding_service_test.dart
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:yimaru_app/app/app.locator.dart';
|
||||
|
||||
import '../helpers/test_helpers.dart';
|
||||
|
||||
void main() {
|
||||
group('OnboardingServiceTest -', () {
|
||||
setUp(() => registerServices());
|
||||
tearDown(() => locator.reset());
|
||||
});
|
||||
}
|
||||
|
|
@ -7,7 +7,6 @@
|
|||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <audioplayers_windows/audioplayers_windows_plugin.h>
|
||||
#include <battery_plus/battery_plus_windows_plugin.h>
|
||||
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
|
||||
#include <file_selector_windows/file_selector_windows.h>
|
||||
#include <firebase_core/firebase_core_plugin_c_api.h>
|
||||
|
|
@ -20,8 +19,6 @@
|
|||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
AudioplayersWindowsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin"));
|
||||
BatteryPlusWindowsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("BatteryPlusWindowsPlugin"));
|
||||
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
|
||||
FileSelectorWindowsRegisterWithRegistrar(
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
audioplayers_windows
|
||||
battery_plus
|
||||
connectivity_plus
|
||||
file_selector_windows
|
||||
firebase_core
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user