diff --git a/lib/services/voice_recorder_service.dart b/lib/services/voice_recorder_service.dart index 64bfca6..c0a7444 100644 --- a/lib/services/voice_recorder_service.dart +++ b/lib/services/voice_recorder_service.dart @@ -14,11 +14,9 @@ class VoiceRecorderService with ListenableServiceMixin { WaveformRecorderController get waveController => _waveController; - bool _isRecording = false; - bool get isRecording => _isRecording; - + bool get isRecording => _isRecording; // Start voice recording Future startRecording() async { @@ -41,5 +39,4 @@ class VoiceRecorderService with ListenableServiceMixin { if (file == null) return null; return file.path; } - } diff --git a/lib/ui/views/learn_practice/learn_practice_view.dart b/lib/ui/views/learn_practice/learn_practice_view.dart index 040b35a..32edfa0 100644 --- a/lib/ui/views/learn_practice/learn_practice_view.dart +++ b/lib/ui/views/learn_practice/learn_practice_view.dart @@ -60,8 +60,8 @@ class LearnPracticeView extends StackedView { required LearnPracticeViewModel viewModel}) => PopScope( canPop: false, - onPopInvokedWithResult: (value, data) - async=>await _showSheet(context: context, viewModel: viewModel), + onPopInvokedWithResult: (value, data) async => + await _showSheet(context: context, viewModel: viewModel), child: _buildScaffoldWrapper(viewModel)); Widget _buildSheet(LearnPracticeViewModel viewModel) => diff --git a/lib/ui/views/learn_practice/learn_practice_viewmodel.dart b/lib/ui/views/learn_practice/learn_practice_viewmodel.dart index eb4934c..46d8304 100644 --- a/lib/ui/views/learn_practice/learn_practice_viewmodel.dart +++ b/lib/ui/views/learn_practice/learn_practice_viewmodel.dart @@ -83,7 +83,7 @@ class LearnPracticeViewModel extends ReactiveViewModel { Voice? _playing; - Voice? get playing =>_playing; + Voice? get playing => _playing; // Learn practices List _practices = []; @@ -126,7 +126,6 @@ class LearnPracticeViewModel extends ReactiveViewModel { Future _startRecording() async => await _voiceRecorderService.startRecording(); - // Play practice audio void _listenToAudio() { _audioPlayerService.durationStream.listen((dur) { @@ -151,45 +150,37 @@ class LearnPracticeViewModel extends ReactiveViewModel { await _audioPlayerService.playUrl(question.voicePrompt ?? ''); } - Future playResult({required Map answer,required Voice voice})async{ - setBusyObject( - playing: voice, - object: answer['busy_object']); - await playAudio(voice: voice,answer: answer); + Future playResult( + {required Map answer, required Voice voice}) async { + setBusyObject(playing: voice, object: answer['busy_object']); + await playAudio(voice: voice, answer: answer); } - Future playAudio({required Map answer,required Voice voice}) async => - await runBusyFuture(_playAudio(voice:voice,answer:answer), + Future playAudio( + {required Map answer, required Voice voice}) async => + await runBusyFuture(_playAudio(voice: voice, answer: answer), busyObject: answer['busy_object']); - Future _playAudio({required Map answer,required Voice voice}) async { - - - if(voice == Voice.recorded){ - await _audioPlayerService - .playLocal(answer['recorded_voice_answer']); - }else{ + Future _playAudio( + {required Map answer, required Voice voice}) async { + if (voice == Voice.recorded) { + await _audioPlayerService.playLocal(answer['recorded_voice_answer']); + } else { await _audioPlayerService.playUrl(answer['sample_voice_answer']); - } - - } - Future pauseAudio() async { await _audioPlayerService.pause(); } // Set busy object - void setBusyObject({required String object,required Voice playing}) { + void setBusyObject({required String object, required Voice playing}) { _playing = playing; _busyObject = object; rebuildUi(); } - - // Dialogue Future showAbortDialog() async { DialogResponse? response = await _dialogService.showDialog( @@ -219,13 +210,14 @@ class LearnPracticeViewModel extends ReactiveViewModel { } } - Future nextQuestion({required int index,required LearnQuestion question}) async { + Future nextQuestion( + {required int index, required LearnQuestion question}) async { await stopRecording(); _answers.add({ 'busy_object': question.id.toString(), 'sample_text_answer': question.audioCorrectAnswerText, 'sample_voice_answer': question.sampleAnswerVoicePrompt, - 'recorded_voice_answer' : _voiceRecorderService.getRecordedAudio() , + 'recorded_voice_answer': _voiceRecorderService.getRecordedAudio(), }); if (index != _questions.length) { _questionSetController.nextPage( @@ -266,6 +258,5 @@ class LearnPracticeViewModel extends ReactiveViewModel { Future _getLearnPracticeQuestions(int id) async { _questions = await _apiService.getLearnQuestions(id); - } } diff --git a/lib/ui/views/learn_practice/screens/interact_learn_practice_screen.dart b/lib/ui/views/learn_practice/screens/interact_learn_practice_screen.dart index 1d11264..ba8b00b 100644 --- a/lib/ui/views/learn_practice/screens/interact_learn_practice_screen.dart +++ b/lib/ui/views/learn_practice/screens/interact_learn_practice_screen.dart @@ -31,11 +31,10 @@ class InteractLearnPracticeScreen } void _start(LearnPracticeViewModel viewModel) => - viewModel.playVoicePrompt(question); - + viewModel.playVoicePrompt(question); Future _stop(LearnPracticeViewModel viewModel) async => - await viewModel.nextQuestion(index: index,question: question); + await viewModel.nextQuestion(index: index, question: question); Future _showSheet( {required BuildContext context, @@ -96,25 +95,30 @@ class InteractLearnPracticeScreen {required BuildContext context, required LearnPracticeViewModel viewModel}) => [ - _buildAppBarWrapper(context: context,viewModel: viewModel), + _buildAppBarWrapper(context: context, viewModel: viewModel), _buildSpeakingIndicatorWrapper(viewModel), _buildLowerButtonsSectionWrapper(context: context, viewModel: viewModel) ]; - Widget _buildAppBarWrapper( {required BuildContext context, - required LearnPracticeViewModel viewModel}) => Column( + Widget _buildAppBarWrapper( + {required BuildContext context, + required LearnPracticeViewModel viewModel}) => + Column( children: [ verticalSpaceMedium, - _buildAppBar(context: context,viewModel: viewModel), + _buildAppBar(context: context, viewModel: viewModel), verticalSpaceMedium, ], ); - Widget _buildAppBar( {required BuildContext context, - required LearnPracticeViewModel viewModel}) => SmallAppBar( - showBackButton: true, - onTap: () async => await _showSheet(context: context,viewModel: viewModel), - title: 'Practice Speaking ($index/${viewModel.questions.length})'); + Widget _buildAppBar( + {required BuildContext context, + required LearnPracticeViewModel viewModel}) => + SmallAppBar( + showBackButton: true, + onTap: () async => + await _showSheet(context: context, viewModel: viewModel), + title: 'Practice Speaking ($index/${viewModel.questions.length})'); Widget _buildSpeakingIndicatorWrapper(LearnPracticeViewModel viewModel) => Column( diff --git a/lib/ui/views/learn_practice/screens/learn_practice_intro_screen.dart b/lib/ui/views/learn_practice/screens/learn_practice_intro_screen.dart index 4a82299..f831db8 100644 --- a/lib/ui/views/learn_practice/screens/learn_practice_intro_screen.dart +++ b/lib/ui/views/learn_practice/screens/learn_practice_intro_screen.dart @@ -17,12 +17,11 @@ class LearnPracticeIntroScreen extends ViewModelWidget { viewModel.pop(); viewModel.pop(); viewModel.stopRecording(); - } Future _showSheet( - {required BuildContext context, - required LearnPracticeViewModel viewModel}) async => + {required BuildContext context, + required LearnPracticeViewModel viewModel}) async => await showModalBottomSheet( context: context, isScrollControlled: true, @@ -32,41 +31,51 @@ class LearnPracticeIntroScreen extends ViewModelWidget { @override Widget build(BuildContext context, LearnPracticeViewModel viewModel) => - _buildScaffoldWrapper(context: context,viewModel: viewModel); + _buildScaffoldWrapper(context: context, viewModel: viewModel); - Widget _buildScaffoldWrapper( {required BuildContext context, - required LearnPracticeViewModel viewModel}) => Scaffold( + Widget _buildScaffoldWrapper( + {required BuildContext context, + required LearnPracticeViewModel viewModel}) => + Scaffold( backgroundColor: kcBackgroundColor, - body: _buildScaffold(context: context,viewModel: viewModel), + body: _buildScaffold(context: context, viewModel: viewModel), ); - Widget _buildScaffold( {required BuildContext context, - required LearnPracticeViewModel viewModel}) => - SafeArea(child: _buildColumnWrapper(context: context,viewModel: viewModel)); + Widget _buildScaffold( + {required BuildContext context, + required LearnPracticeViewModel viewModel}) => + SafeArea( + child: _buildColumnWrapper(context: context, viewModel: viewModel)); - Widget _buildColumnWrapper( {required BuildContext context, - required LearnPracticeViewModel viewModel}) => Padding( + Widget _buildColumnWrapper( + {required BuildContext context, + required LearnPracticeViewModel viewModel}) => + Padding( padding: const EdgeInsets.symmetric(horizontal: 15), - child: _buildColumn(context: context,viewModel: viewModel), + child: _buildColumn(context: context, viewModel: viewModel), ); - Widget _buildColumn( {required BuildContext context, - required LearnPracticeViewModel viewModel}) => Column( + Widget _buildColumn( + {required BuildContext context, + required LearnPracticeViewModel viewModel}) => + Column( children: [ verticalSpaceMedium, - _buildAppBar(context: context,viewModel: viewModel), + _buildAppBar(context: context, viewModel: viewModel), verticalSpaceMedium, _buildBodyColumnWrapper(viewModel), ], ); - Widget _buildAppBar( {required BuildContext context, - required LearnPracticeViewModel viewModel}) => SmallAppBar( + Widget _buildAppBar( + {required BuildContext context, + required LearnPracticeViewModel viewModel}) => + SmallAppBar( showBackButton: true, title: 'Practice Speaking', - onTap: () async => await _showSheet(context: context,viewModel: viewModel), - - ); + onTap: () async => + await _showSheet(context: context, viewModel: viewModel), + ); Widget _buildSheet(LearnPracticeViewModel viewModel) => CancelLearnPracticeSheet( diff --git a/lib/ui/views/learn_practice/screens/learn_practice_questions_screen.dart b/lib/ui/views/learn_practice/screens/learn_practice_questions_screen.dart index 425f71a..52b798a 100644 --- a/lib/ui/views/learn_practice/screens/learn_practice_questions_screen.dart +++ b/lib/ui/views/learn_practice/screens/learn_practice_questions_screen.dart @@ -38,7 +38,7 @@ class LearnPracticeQuestionsScreen PageView( controller: viewModel.questionController, physics: const NeverScrollableScrollPhysics(), - children: _buildScreens(index:index,question: question), + children: _buildScreens(index: index, question: question), ); List _buildScreens({ @@ -46,20 +46,25 @@ class LearnPracticeQuestionsScreen required LearnQuestion question, }) => [ - _buildStartLearnPracticeScreen(index:index,question: question), - _buildInteractLearnPracticeScreen(index:index,question: question) + _buildStartLearnPracticeScreen(index: index, question: question), + _buildInteractLearnPracticeScreen(index: index, question: question) ]; Widget _buildStartLearnPracticeScreen({ - required int index, - required LearnQuestion question,} - ) => StartLearnPracticeScreen( + required int index, + required LearnQuestion question, + }) => + StartLearnPracticeScreen( index: index, - question: question, + question: question, ); Widget _buildInteractLearnPracticeScreen({ required int index, - required LearnQuestion question,}) => - InteractLearnPracticeScreen(index: index,question: question,); + required LearnQuestion question, + }) => + InteractLearnPracticeScreen( + index: index, + question: question, + ); } diff --git a/lib/ui/views/learn_practice/screens/learn_practice_result_screen.dart b/lib/ui/views/learn_practice/screens/learn_practice_result_screen.dart index 68c1c64..1e27b26 100644 --- a/lib/ui/views/learn_practice/screens/learn_practice_result_screen.dart +++ b/lib/ui/views/learn_practice/screens/learn_practice_result_screen.dart @@ -12,11 +12,9 @@ import '../../../widgets/small_app_bar.dart'; class LearnPracticeResultScreen extends ViewModelWidget { - const LearnPracticeResultScreen({super.key}); - - void _navigate(LearnPracticeViewModel viewModel){ + void _navigate(LearnPracticeViewModel viewModel) { viewModel.questionSetController.jumpToPage(0); viewModel.goTo(0); } @@ -25,12 +23,11 @@ class LearnPracticeResultScreen viewModel.pop(); viewModel.pop(); viewModel.stopRecording(); - } Future _showSheet( - {required BuildContext context, - required LearnPracticeViewModel viewModel}) async => + {required BuildContext context, + required LearnPracticeViewModel viewModel}) async => await showModalBottomSheet( context: context, isScrollControlled: true, @@ -40,42 +37,57 @@ class LearnPracticeResultScreen @override Widget build(BuildContext context, LearnPracticeViewModel viewModel) => - _buildScaffoldWrapper(context: context,viewModel: viewModel); + _buildScaffoldWrapper(context: context, viewModel: viewModel); - Widget _buildScaffoldWrapper({required BuildContext context, - required LearnPracticeViewModel viewModel}) => Scaffold( + Widget _buildScaffoldWrapper( + {required BuildContext context, + required LearnPracticeViewModel viewModel}) => + Scaffold( backgroundColor: kcBackgroundColor, - body: _buildScaffold(context: context,viewModel: viewModel), + body: _buildScaffold(context: context, viewModel: viewModel), ); - Widget _buildScaffold({required BuildContext context, - required LearnPracticeViewModel viewModel}) => - SafeArea(child: _buildBodyColumnWrapper(context: context,viewModel: viewModel)); + Widget _buildScaffold( + {required BuildContext context, + required LearnPracticeViewModel viewModel}) => + SafeArea( + child: + _buildBodyColumnWrapper(context: context, viewModel: viewModel)); - Widget _buildBodyColumnWrapper({required BuildContext context, - required LearnPracticeViewModel viewModel}) => Padding( + Widget _buildBodyColumnWrapper( + {required BuildContext context, + required LearnPracticeViewModel viewModel}) => + Padding( padding: const EdgeInsets.symmetric(horizontal: 15), - child: _buildBodyColumn(context: context,viewModel: viewModel), + child: _buildBodyColumn(context: context, viewModel: viewModel), ); - Widget _buildBodyColumn({required BuildContext context, - required LearnPracticeViewModel viewModel}) => Column( - children: _buildBodyColumnChildren(context: context,viewModel: viewModel), + Widget _buildBodyColumn( + {required BuildContext context, + required LearnPracticeViewModel viewModel}) => + Column( + children: + _buildBodyColumnChildren(context: context, viewModel: viewModel), ); - List _buildBodyColumnChildren({required BuildContext context, - required LearnPracticeViewModel viewModel}) => [ + List _buildBodyColumnChildren( + {required BuildContext context, + required LearnPracticeViewModel viewModel}) => + [ verticalSpaceMedium, - _buildAppBar(context: context,viewModel: viewModel), + _buildAppBar(context: context, viewModel: viewModel), verticalSpaceMedium, _buildBodyWrapper(viewModel) ]; - Widget _buildAppBar({required BuildContext context, - required LearnPracticeViewModel viewModel}) => SmallAppBar( + Widget _buildAppBar( + {required BuildContext context, + required LearnPracticeViewModel viewModel}) => + SmallAppBar( title: 'Result', showBackButton: true, - onTap: () async => await _showSheet(context: context,viewModel: viewModel), + onTap: () async => + await _showSheet(context: context, viewModel: viewModel), ); Widget _buildSheet(LearnPracticeViewModel viewModel) => @@ -112,7 +124,7 @@ class LearnPracticeResultScreen Widget _buildResultsSection(LearnPracticeViewModel viewModel) => const LearnPracticeResultsWrapper(); - Widget _buildLearnPracticeTipSection() => const LearnPracticeTipSection(); + Widget _buildLearnPracticeTipSection() => const LearnPracticeTipSection(); Widget _buildLowerButtonsSection(LearnPracticeViewModel viewModel) => Column( mainAxisSize: MainAxisSize.min, @@ -148,6 +160,5 @@ class LearnPracticeResultScreen borderColor: kcPrimaryColor, foregroundColor: kcPrimaryColor, onTap: () => _navigate(viewModel), - ); } diff --git a/lib/ui/views/learn_practice/screens/start_learn_practice_screen.dart b/lib/ui/views/learn_practice/screens/start_learn_practice_screen.dart index 32e8963..50ccce6 100644 --- a/lib/ui/views/learn_practice/screens/start_learn_practice_screen.dart +++ b/lib/ui/views/learn_practice/screens/start_learn_practice_screen.dart @@ -13,7 +13,8 @@ class StartLearnPracticeScreen extends ViewModelWidget { final int index; final LearnQuestion question; - const StartLearnPracticeScreen({super.key,required this.index,required this.question}); + const StartLearnPracticeScreen( + {super.key, required this.index, required this.question}); Future _cancel(LearnPracticeViewModel viewModel) async { viewModel.pop(); @@ -26,8 +27,8 @@ class StartLearnPracticeScreen extends ViewModelWidget { } Future _showSheet( - {required BuildContext context, - required LearnPracticeViewModel viewModel}) async => + {required BuildContext context, + required LearnPracticeViewModel viewModel}) async => await showModalBottomSheet( context: context, isScrollControlled: true, @@ -37,51 +38,68 @@ class StartLearnPracticeScreen extends ViewModelWidget { @override Widget build(BuildContext context, LearnPracticeViewModel viewModel) => - _buildScaffoldWrapper(context: context,viewModel: viewModel); + _buildScaffoldWrapper(context: context, viewModel: viewModel); - Widget _buildScaffoldWrapper( {required BuildContext context, - required LearnPracticeViewModel viewModel}) => Scaffold( + Widget _buildScaffoldWrapper( + {required BuildContext context, + required LearnPracticeViewModel viewModel}) => + Scaffold( backgroundColor: kcBackgroundColor, - body: _buildScaffold(context: context,viewModel: viewModel), + body: _buildScaffold(context: context, viewModel: viewModel), ); - Widget _buildScaffold( {required BuildContext context, - required LearnPracticeViewModel viewModel}) => - SafeArea(child: _buildBodyColumnWrapper(context: context,viewModel: viewModel)); + Widget _buildScaffold( + {required BuildContext context, + required LearnPracticeViewModel viewModel}) => + SafeArea( + child: + _buildBodyColumnWrapper(context: context, viewModel: viewModel)); - Widget _buildBodyColumnWrapper( {required BuildContext context, - required LearnPracticeViewModel viewModel}) => Padding( + Widget _buildBodyColumnWrapper( + {required BuildContext context, + required LearnPracticeViewModel viewModel}) => + Padding( padding: const EdgeInsets.symmetric(horizontal: 15), - child: _buildBodyColumn(context: context,viewModel: viewModel), + child: _buildBodyColumn(context: context, viewModel: viewModel), ); - Widget _buildBodyColumn( {required BuildContext context, - required LearnPracticeViewModel viewModel}) => Column( + Widget _buildBodyColumn( + {required BuildContext context, + required LearnPracticeViewModel viewModel}) => + Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: _buildBodyColumnChildren(context: context,viewModel: viewModel), + children: + _buildBodyColumnChildren(context: context, viewModel: viewModel), ); - List _buildBodyColumnChildren( {required BuildContext context, - required LearnPracticeViewModel viewModel}) => [ - _buildAppBarWrapper(context: context,viewModel: viewModel), + List _buildBodyColumnChildren( + {required BuildContext context, + required LearnPracticeViewModel viewModel}) => + [ + _buildAppBarWrapper(context: context, viewModel: viewModel), _buildStartButtonWrapper(viewModel), _buildLowerButtonsSectionWrapper(viewModel) ]; - Widget _buildAppBarWrapper( {required BuildContext context, - required LearnPracticeViewModel viewModel}) => Column( + Widget _buildAppBarWrapper( + {required BuildContext context, + required LearnPracticeViewModel viewModel}) => + Column( children: [ verticalSpaceMedium, - _buildAppBar(context: context,viewModel: viewModel), + _buildAppBar(context: context, viewModel: viewModel), verticalSpaceMedium, ], ); - Widget _buildAppBar( {required BuildContext context, - required LearnPracticeViewModel viewModel}) => SmallAppBar( - showBackButton: true, - onTap: () async => await _showSheet(context: context,viewModel: viewModel), - title: 'Practice Speaking ($index/${viewModel.questions.length})'); + Widget _buildAppBar( + {required BuildContext context, + required LearnPracticeViewModel viewModel}) => + SmallAppBar( + showBackButton: true, + onTap: () async => + await _showSheet(context: context, viewModel: viewModel), + title: 'Practice Speaking ($index/${viewModel.questions.length})'); Widget _buildSheet(LearnPracticeViewModel viewModel) => CancelLearnPracticeSheet( diff --git a/lib/ui/views/onboarding/onboarding_view.dart b/lib/ui/views/onboarding/onboarding_view.dart index 2a982f7..142576b 100644 --- a/lib/ui/views/onboarding/onboarding_view.dart +++ b/lib/ui/views/onboarding/onboarding_view.dart @@ -3,7 +3,6 @@ import 'package:stacked/stacked.dart'; import 'package:stacked/stacked_annotations.dart'; import 'package:yimaru_app/ui/views/onboarding/screens/age_group_form_screen.dart'; -import 'package:yimaru_app/ui/views/onboarding/screens/birthday_form_screen.dart'; import 'package:yimaru_app/ui/views/onboarding/screens/challenge_form_screen.dart'; import 'package:yimaru_app/ui/views/onboarding/screens/country_region_form_screen.dart'; import 'package:yimaru_app/ui/views/onboarding/screens/educational_background_form_screen.dart'; @@ -22,6 +21,7 @@ import 'onboarding_view.form.dart'; 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: 'occupation', validator: FormValidator.validateForm), FormTextField(name: 'languageGoal', validator: FormValidator.validateForm), @@ -50,25 +50,23 @@ class OnboardingView extends StackedView } else if (viewModel.currentPage == 1) { viewModel.resetGenderFormScreen(); } else if (viewModel.currentPage == 2) { - viewModel.resetBirthdayFormScreen(); - } else if (viewModel.currentPage == 3) { viewModel.resetAgeGroupFormScreen(); - } else if (viewModel.currentPage == 4) { + } else if (viewModel.currentPage == 3) { viewModel.resetEducationalBackgroundFormScreen(); - } else if (viewModel.currentPage == 5) { + } else if (viewModel.currentPage == 4) { occupationController.clear(); viewModel.resetOccupationFormScreen(); - } else if (viewModel.currentPage == 6) { + } else if (viewModel.currentPage == 5) { viewModel.resetCountryRegionFormScreen(); - } else if (viewModel.currentPage == 7) { + } else if (viewModel.currentPage == 6) { viewModel.resetLearningGoalFormScreen(); - } else if (viewModel.currentPage == 8) { + } else if (viewModel.currentPage == 7) { languageGoalController.clear(); viewModel.resetLanguageGoalFormScreen(); - } else if (viewModel.currentPage == 9) { + } else if (viewModel.currentPage == 8) { challengeController.clear(); viewModel.resetChallengeFormScreen(); - } else if (viewModel.currentPage == 10) { + } else if (viewModel.currentPage == 9) { topicController.clear(); viewModel.resetTopicFormScreen(); } @@ -117,7 +115,6 @@ class OnboardingView extends StackedView List _buildScreens() => [ _buildFullNameForm(), _buildGenderForm(), - _buildBirthdayForm(), _buildAgeGroupForm(), _buildEducationalBackgroundForm(), _buildOccupationForm(), @@ -133,8 +130,6 @@ class OnboardingView extends StackedView Widget _buildGenderForm() => const GenderFormScreen(); - Widget _buildBirthdayForm() => const BirthdayFormScreen(); - Widget _buildAgeGroupForm() => const AgeGroupFormScreen(); Widget _buildEducationalBackgroundForm() => @@ -143,7 +138,9 @@ class OnboardingView extends StackedView Widget _buildOccupationForm() => OccupationFormScreen(occupationController: occupationController); - Widget _buildCountryRegionForm() => const CountryRegionFormScreen(); + Widget _buildCountryRegionForm() => CountryRegionFormScreen( + regionController: regionController, + ); Widget _buildLearningGoalForm() => const LearningGoalFormScreen(); diff --git a/lib/ui/views/onboarding/onboarding_view.form.dart b/lib/ui/views/onboarding/onboarding_view.form.dart index df81d46..6ae05a8 100644 --- a/lib/ui/views/onboarding/onboarding_view.form.dart +++ b/lib/ui/views/onboarding/onboarding_view.form.dart @@ -15,6 +15,7 @@ const bool _autoTextFieldValidation = true; const String TopicValueKey = 'topic'; const String FullNameValueKey = 'fullName'; +const String RegionValueKey = 'region'; const String ChallengeValueKey = 'challenge'; const String OccupationValueKey = 'occupation'; const String LanguageGoalValueKey = 'languageGoal'; @@ -27,6 +28,7 @@ final Map _OnboardingViewFocusNodes = {}; final Map _OnboardingViewTextValidations = { TopicValueKey: FormValidator.validateForm, FullNameValueKey: FormValidator.validateFullNameForm, + RegionValueKey: FormValidator.validateForm, ChallengeValueKey: FormValidator.validateForm, OccupationValueKey: FormValidator.validateForm, LanguageGoalValueKey: FormValidator.validateForm, @@ -37,6 +39,8 @@ mixin $OnboardingView { _getFormTextEditingController(TopicValueKey); TextEditingController get fullNameController => _getFormTextEditingController(FullNameValueKey); + TextEditingController get regionController => + _getFormTextEditingController(RegionValueKey); TextEditingController get challengeController => _getFormTextEditingController(ChallengeValueKey); TextEditingController get occupationController => @@ -46,6 +50,7 @@ mixin $OnboardingView { FocusNode get topicFocusNode => _getFormFocusNode(TopicValueKey); FocusNode get fullNameFocusNode => _getFormFocusNode(FullNameValueKey); + FocusNode get regionFocusNode => _getFormFocusNode(RegionValueKey); FocusNode get challengeFocusNode => _getFormFocusNode(ChallengeValueKey); FocusNode get occupationFocusNode => _getFormFocusNode(OccupationValueKey); FocusNode get languageGoalFocusNode => @@ -77,6 +82,7 @@ mixin $OnboardingView { void syncFormWithViewModel(FormStateHelper model) { topicController.addListener(() => _updateFormData(model)); fullNameController.addListener(() => _updateFormData(model)); + regionController.addListener(() => _updateFormData(model)); challengeController.addListener(() => _updateFormData(model)); occupationController.addListener(() => _updateFormData(model)); languageGoalController.addListener(() => _updateFormData(model)); @@ -93,6 +99,7 @@ mixin $OnboardingView { void listenToFormUpdated(FormViewModel model) { topicController.addListener(() => _updateFormData(model)); fullNameController.addListener(() => _updateFormData(model)); + regionController.addListener(() => _updateFormData(model)); challengeController.addListener(() => _updateFormData(model)); occupationController.addListener(() => _updateFormData(model)); languageGoalController.addListener(() => _updateFormData(model)); @@ -107,6 +114,7 @@ mixin $OnboardingView { ..addAll({ TopicValueKey: topicController.text, FullNameValueKey: fullNameController.text, + RegionValueKey: regionController.text, ChallengeValueKey: challengeController.text, OccupationValueKey: occupationController.text, LanguageGoalValueKey: languageGoalController.text, @@ -153,6 +161,7 @@ extension ValueProperties on FormStateHelper { 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 occupationValue => this.formValueMap[OccupationValueKey] as String?; @@ -180,6 +189,16 @@ extension ValueProperties on FormStateHelper { } } + set regionValue(String? value) { + this.setData( + this.formValueMap..addAll({RegionValueKey: value}), + ); + + if (_OnboardingViewTextEditingControllers.containsKey(RegionValueKey)) { + _OnboardingViewTextEditingControllers[RegionValueKey]?.text = value ?? ''; + } + } + set challengeValue(String? value) { this.setData( this.formValueMap..addAll({ChallengeValueKey: value}), @@ -220,6 +239,9 @@ extension ValueProperties on FormStateHelper { 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); @@ -234,6 +256,8 @@ extension ValueProperties on FormStateHelper { 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 hasOccupationValidationMessage => @@ -245,6 +269,8 @@ extension ValueProperties on FormStateHelper { this.fieldsValidationMessages[TopicValueKey]; String? get fullNameValidationMessage => this.fieldsValidationMessages[FullNameValueKey]; + String? get regionValidationMessage => + this.fieldsValidationMessages[RegionValueKey]; String? get challengeValidationMessage => this.fieldsValidationMessages[ChallengeValueKey]; String? get occupationValidationMessage => @@ -258,6 +284,8 @@ extension Methods on FormStateHelper { 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 setOccupationValidationMessage(String? validationMessage) => @@ -269,6 +297,7 @@ extension Methods on FormStateHelper { void clearForm() { topicValue = ''; fullNameValue = ''; + regionValue = ''; challengeValue = ''; occupationValue = ''; languageGoalValue = ''; @@ -279,6 +308,7 @@ extension Methods on FormStateHelper { this.setValidationMessages({ TopicValueKey: getValidationMessage(TopicValueKey), FullNameValueKey: getValidationMessage(FullNameValueKey), + RegionValueKey: getValidationMessage(RegionValueKey), ChallengeValueKey: getValidationMessage(ChallengeValueKey), OccupationValueKey: getValidationMessage(OccupationValueKey), LanguageGoalValueKey: getValidationMessage(LanguageGoalValueKey), @@ -303,6 +333,7 @@ void updateValidationData(FormStateHelper model) => model.setValidationMessages({ TopicValueKey: getValidationMessage(TopicValueKey), FullNameValueKey: getValidationMessage(FullNameValueKey), + RegionValueKey: getValidationMessage(RegionValueKey), ChallengeValueKey: getValidationMessage(ChallengeValueKey), OccupationValueKey: getValidationMessage(OccupationValueKey), LanguageGoalValueKey: getValidationMessage(LanguageGoalValueKey), diff --git a/lib/ui/views/onboarding/onboarding_viewmodel.dart b/lib/ui/views/onboarding/onboarding_viewmodel.dart index ab3184c..c7bc537 100644 --- a/lib/ui/views/onboarding/onboarding_viewmodel.dart +++ b/lib/ui/views/onboarding/onboarding_viewmodel.dart @@ -63,11 +63,6 @@ class OnboardingViewModel extends ReactiveViewModel String? get selectedGender => _selectedGender; - // Birthday - String? _selectedBirthday; - - String? get selectedBirthday => _selectedBirthday; - // Age group final List> _ageGroups = [ { @@ -105,10 +100,10 @@ class OnboardingViewModel extends ReactiveViewModel String get selectedCountry => _selectedCountry; - // Country - String _selectedRegion = 'Addis Ababa'; + // Region + bool _focusRegion = false; - String get selectedRegion => _selectedRegion; + bool get focusRegion => _focusRegion; // Learning goal String? _selectedLearningGoal; @@ -247,12 +242,6 @@ class OnboardingViewModel extends ReactiveViewModel bool isSelectedGender(String value) => _selectedGender == value; - // Birthday - void setBirthday(String value) { - _selectedBirthday = value; - rebuildUi(); - } - // Age group void setSelectedAgeGroup(Map value) { _selectedAgeGroup = value; @@ -270,44 +259,168 @@ class OnboardingViewModel extends ReactiveViewModel } // Country - List getCountries() => ['Ethiopia', 'Other']; + List getCountries() => [ + "Afghanistan", + "Albania", + "Algeria", + "Andorra", + "Angola", + "Argentina", + "Armenia", + "Australia", + "Austria", + "Azerbaijan", + "Bahrain", + "Bangladesh", + "Belarus", + "Belgium", + "Belize", + "Benin", + "Bhutan", + "Bolivia", + "Bosnia and Herzegovina", + "Botswana", + "Brazil", + "Brunei", + "Bulgaria", + "Burkina Faso", + "Burundi", + "Cambodia", + "Cameroon", + "Canada", + "Chad", + "Chile", + "China", + "Colombia", + "Comoros", + "Congo", + "Costa Rica", + "Croatia", + "Cuba", + "Cyprus", + "Czech Republic", + "Denmark", + "Djibouti", + "Dominican Republic", + "Ecuador", + "Egypt", + "El Salvador", + "Eritrea", + "Estonia", + "Eswatini", + "Ethiopia", + "Finland", + "France", + "Gabon", + "Gambia", + "Georgia", + "Germany", + "Ghana", + "Greece", + "Guatemala", + "Guinea", + "Haiti", + "Honduras", + "Hungary", + "Iceland", + "India", + "Indonesia", + "Iran", + "Iraq", + "Ireland", + "Israel", + "Italy", + "Jamaica", + "Japan", + "Jordan", + "Kazakhstan", + "Kenya", + "Kuwait", + "Kyrgyzstan", + "Laos", + "Latvia", + "Lebanon", + "Liberia", + "Libya", + "Lithuania", + "Luxembourg", + "Madagascar", + "Malawi", + "Malaysia", + "Maldives", + "Mali", + "Malta", + "Mexico", + "Moldova", + "Monaco", + "Mongolia", + "Morocco", + "Mozambique", + "Myanmar", + "Namibia", + "Nepal", + "Netherlands", + "New Zealand", + "Nicaragua", + "Niger", + "Nigeria", + "North Korea", + "Norway", + "Oman", + "Pakistan", + "Panama", + "Paraguay", + "Peru", + "Philippines", + "Poland", + "Portugal", + "Qatar", + "Romania", + "Russia", + "Rwanda", + "Saudi Arabia", + "Senegal", + "Serbia", + "Singapore", + "Slovakia", + "Slovenia", + "Somalia", + "South Africa", + "South Korea", + "Spain", + "Sri Lanka", + "Sudan", + "Sweden", + "Switzerland", + "Syria", + "Taiwan", + "Tajikistan", + "Tanzania", + "Thailand", + "Tunisia", + "Turkey", + "Uganda", + "Ukraine", + "United Arab Emirates", + "United Kingdom", + "United States", + "Uruguay", + "Uzbekistan", + "Venezuela", + "Vietnam", + "Yemen", + "Zambia", + "Zimbabwe" + ]; void setSelectedCountry(String value) { _selectedCountry = value; - if (selectedCountry != 'Ethiopia') { - _selectedRegion = 'Other'; - } else { - _selectedRegion = 'Addis Ababa'; - } rebuildUi(); } // Region - List getRegions(String country) { - if (country == 'Ethiopia') { - return [ - 'Afar', - 'SNNPR', - 'Amhara', - 'Harari', - 'Oromia', - 'Sidama', - 'Somali', - 'Tigray', - 'Gambela', - 'Dire Dawa', - 'Addis Ababa', - 'Central Ethiopia', - 'Benishangul-Gumuz', - 'South West Ethiopia', - ]; - } else { - return ['Other']; - } - } - - void setSelectedRegion(String value) { - _selectedRegion = value; + void setRegionFocus() { + _focusRegion = true; rebuildUi(); } @@ -414,12 +527,6 @@ class OnboardingViewModel extends ReactiveViewModel rebuildUi(); } - // Reset birthday form screen - void resetBirthdayFormScreen() { - _selectedBirthday = null; - rebuildUi(); - } - // Reset age group form screen void resetAgeGroupFormScreen() { _selectedAgeGroup = null; @@ -441,7 +548,6 @@ class OnboardingViewModel extends ReactiveViewModel // Reset country region form screen void resetCountryRegionFormScreen() { _selectedCountry = 'Ethiopia'; - _selectedRegion = 'Addis Ababa'; rebuildUi(); } diff --git a/lib/ui/views/onboarding/screens/birthday_form_screen.dart b/lib/ui/views/onboarding/screens/birthday_form_screen.dart deleted file mode 100644 index 57b31ec..0000000 --- a/lib/ui/views/onboarding/screens/birthday_form_screen.dart +++ /dev/null @@ -1,122 +0,0 @@ -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/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/large_app_bar.dart'; - -import '../../../widgets/birthday_selector.dart'; - -class BirthdayFormScreen extends ViewModelWidget { - const BirthdayFormScreen({super.key}); - - void _pop(OnboardingViewModel viewModel) { - viewModel.resetBirthdayFormScreen(); - - viewModel.goBack(); - } - - Future _next(OnboardingViewModel viewModel) async { - FocusManager.instance.primaryFocus?.unfocus(); - - Map data = {'birth_day': viewModel.selectedBirthday}; - viewModel.addUserData(data); - viewModel.next(); - } - - @override - Widget build(BuildContext context, OnboardingViewModel viewModel) => - _buildScaffoldWrapper(viewModel); - - Widget _buildScaffoldWrapper(OnboardingViewModel viewModel) => Scaffold( - backgroundColor: kcBackgroundColor, - body: _buildScaffold(viewModel), - ); - - Widget _buildScaffold(OnboardingViewModel viewModel) => Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: _buildScaffoldChildren(viewModel), - ); - - List _buildScaffoldChildren(OnboardingViewModel viewModel) => [ - _buildAppBar(viewModel), - verticalSpaceMedium, - _buildExpandedBody(viewModel) - ]; - - Widget _buildExpandedBody(OnboardingViewModel viewModel) => - Expanded(child: _buildBodyWrapper(viewModel)); - - Widget _buildBodyWrapper(OnboardingViewModel viewModel) => Padding( - padding: const EdgeInsets.symmetric(horizontal: 15), - child: _buildBody(viewModel), - ); - - Widget _buildBody(OnboardingViewModel viewModel) => Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: _buildBodyChildren(viewModel), - ); - - List _buildBodyChildren(OnboardingViewModel viewModel) => - [_buildUpperColumn(viewModel), _buildContinueButtonWrapper(viewModel)]; - - Widget _buildUpperColumn(OnboardingViewModel viewModel) => Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: _buildUpperColumnChildren(viewModel), - ); - - List _buildUpperColumnChildren(OnboardingViewModel viewModel) => [ - verticalSpaceMedium, - _buildTitle(), - verticalSpaceSmall, - _buildSubtitle(), - verticalSpaceMedium, - _buildBirthdayFormField(viewModel) - ]; - - Widget _buildAppBar(OnboardingViewModel viewModel) => LargeAppBar( - showBackButton: true, - showLanguageSelection: true, - onPop: () => _pop(viewModel), - onLanguage: () async => await viewModel.navigateToLanguage(), - ); - - Widget _buildTitle() => Text( - 'Pick your birthday?', - style: style25DG600, - ); - - Widget _buildSubtitle() => Text( - 'We’ll personalize your learning experience based on your birthday.', - style: style14MG400, - ); - - Widget _buildBirthdayFormField(OnboardingViewModel viewModel) => - BirthdaySelector( - birthday: viewModel.selectedBirthday, - onSelected: (value) => - viewModel.setBirthday(DateFormat('yyyy-MM-dd').format(value)), - ); - - Widget _buildContinueButtonWrapper(OnboardingViewModel viewModel) => Padding( - padding: const EdgeInsets.only(bottom: 50), - child: _buildContinueButton(viewModel), - ); - - Widget _buildContinueButton(OnboardingViewModel viewModel) => - CustomElevatedButton( - height: 55, - text: 'Continue', - borderRadius: 12, - foregroundColor: kcWhite, - backgroundColor: viewModel.selectedBirthday != null - ? kcPrimaryColor - : kcPrimaryColor.withOpacity(0.1), - onTap: - viewModel.selectedBirthday != null ? () => _next(viewModel) : null, - ); -} diff --git a/lib/ui/views/onboarding/screens/country_region_form_screen.dart b/lib/ui/views/onboarding/screens/country_region_form_screen.dart index 0e907eb..4a08c34 100644 --- a/lib/ui/views/onboarding/screens/country_region_form_screen.dart +++ b/lib/ui/views/onboarding/screens/country_region_form_screen.dart @@ -6,9 +6,11 @@ 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 '../onboarding_view.form.dart'; class CountryRegionFormScreen extends ViewModelWidget { - const CountryRegionFormScreen({super.key}); + final TextEditingController regionController; + const CountryRegionFormScreen({super.key, required this.regionController}); void _pop(OnboardingViewModel viewModel) { viewModel.resetCountryRegionFormScreen(); @@ -19,8 +21,8 @@ class CountryRegionFormScreen extends ViewModelWidget { FocusManager.instance.primaryFocus?.unfocus(); Map data = { + 'region': regionController.text, 'country': viewModel.selectedCountry, - 'region': viewModel.selectedRegion }; viewModel.addUserData(data); @@ -83,8 +85,11 @@ class CountryRegionFormScreen extends ViewModelWidget { verticalSpaceMedium, _buildCountryDropDown(viewModel), verticalSpaceMedium, - _buildRegionDropDown(viewModel), - verticalSpaceMedium, + _buildRegionFormField(viewModel), + if (viewModel.hasRegionValidationMessage && viewModel.focusRegion) + verticalSpaceTiny, + if (viewModel.hasRegionValidationMessage && viewModel.focusRegion) + _buildRegionValidatorWrapper(viewModel) ]; Widget _buildAppBar(OnboardingViewModel viewModel) => LargeAppBar( @@ -112,16 +117,23 @@ class CountryRegionFormScreen extends ViewModelWidget { items: (value, props) => viewModel.getCountries(), onChanged: (value) => viewModel.setSelectedCountry(value ?? 'Ethiopia')); + Widget _buildRegionFormField(OnboardingViewModel viewModel) => TextFormField( + controller: regionController, + onTap: viewModel.setRegionFocus, + decoration: inputDecoration( + hint: 'Enter Your City', + focus: viewModel.focusRegion, + filled: regionController.text.isNotEmpty), + ); - Widget _buildRegionDropDown(OnboardingViewModel viewModel) => - CustomDropdownPicker( - hint: 'Select region', - icon: _buildSearchIcon(), - selectedItem: viewModel.selectedRegion, - items: (value, props) => - viewModel.getRegions(viewModel.selectedCountry), - onChanged: (value) => - viewModel.setSelectedRegion(value ?? 'Addis Ababa'), + Widget _buildRegionValidatorWrapper(OnboardingViewModel viewModel) => + viewModel.hasRegionValidationMessage + ? _buildRegionValidator(viewModel) + : Container(); + + Widget _buildRegionValidator(OnboardingViewModel viewModel) => Text( + viewModel.regionValidationMessage!, + style: style12R700, ); Icon _buildSearchIcon() => const Icon( @@ -140,7 +152,9 @@ class CountryRegionFormScreen extends ViewModelWidget { text: 'Continue', borderRadius: 12, foregroundColor: kcWhite, - onTap: () => _next(viewModel), - backgroundColor: kcPrimaryColor, + onTap: regionController.text.isNotEmpty ? () => _next(viewModel) : null, + backgroundColor: regionController.text.isNotEmpty + ? kcPrimaryColor + : kcPrimaryColor.withOpacity(0.1), ); } diff --git a/lib/ui/views/onboarding/screens/topic_form_screen.dart b/lib/ui/views/onboarding/screens/topic_form_screen.dart index ba19fea..4b7c167 100644 --- a/lib/ui/views/onboarding/screens/topic_form_screen.dart +++ b/lib/ui/views/onboarding/screens/topic_form_screen.dart @@ -1,4 +1,5 @@ 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/ui_helpers.dart'; @@ -25,6 +26,7 @@ class TopicFormScreen extends ViewModelWidget { Map data = { 'profile_completed': true, 'preferred_language': 'en', + 'birth_day': DateFormat('yyyy-MM-dd').format(DateTime.now()), 'favoutite_topic': viewModel.selectedTopic ?? topicController.text, }; viewModel.addUserData(data); diff --git a/lib/ui/views/profile_detail/profile_detail_view.dart b/lib/ui/views/profile_detail/profile_detail_view.dart index 2e7fa12..3fb13e7 100644 --- a/lib/ui/views/profile_detail/profile_detail_view.dart +++ b/lib/ui/views/profile_detail/profile_detail_view.dart @@ -3,7 +3,6 @@ import 'package:flutter/services.dart'; import 'package:intl/intl.dart'; import 'package:stacked/stacked.dart'; import 'package:stacked/stacked_annotations.dart'; -import 'package:yimaru_app/ui/widgets/birthday_selector.dart'; import 'package:yimaru_app/ui/widgets/custom_form_label.dart'; import 'package:yimaru_app/ui/widgets/small_app_bar.dart'; @@ -22,6 +21,7 @@ import 'profile_detail_view.form.dart'; @FormView(fields: [ FormTextField(name: 'email', validator: FormValidator.validateForm), + FormTextField(name: 'region', validator: FormValidator.validateForm), FormTextField( name: 'phoneNumber', validator: FormValidator.validatePhoneNumberForm), FormTextField(name: 'lastName', validator: FormValidator.validateForm), @@ -34,13 +34,13 @@ class ProfileDetailView extends StackedView Future _update(ProfileDetailViewModel viewModel) async { Map data = { - 'region': viewModel.selectedRegion, + 'region':regionController.text, 'gender': viewModel.selectedGender, 'last_name': lastNameController.text, 'country': viewModel.selectedCountry, 'first_name': firstNameController.text, 'occupation': occupationController.text, - 'birth_day': viewModel.selectedBirthday, + 'birth_day': DateFormat('d MMM, yyyy').format(DateTime.now()), }; viewModel.addUserData(data); @@ -68,15 +68,14 @@ class ProfileDetailView extends StackedView void _onModelReady(ProfileDetailViewModel viewModel) { phoneNumberController.text = '251900000000'; emailController.text = viewModel.user?.email ?? ''; + regionController.text = viewModel.user?.region ?? ''; lastNameController.text = viewModel.user?.lastName ?? ''; firstNameController.text = viewModel.user?.firstName ?? ''; occupationController.text = viewModel.user?.occupation ?? ''; viewModel.clearUserData(); viewModel.setGender(viewModel.user?.gender ?? ''); viewModel.setSelectedCountry(viewModel.user?.country ?? 'Ethiopia'); - viewModel.setSelectedRegion(viewModel.user?.region ?? 'Addis Ababa'); - viewModel.setBirthday(viewModel.user?.birthday ?? - DateFormat('d MMM, yyyy').format(DateTime.now())); + } @override @@ -185,14 +184,17 @@ class ProfileDetailView extends StackedView verticalSpaceMedium, _buildGenderFormFieldWrapper(viewModel), verticalSpaceSmall, - _buildBirthdayColumn(viewModel), - verticalSpaceSmall, _buildPhoneNumberFormFieldSection(viewModel), verticalSpaceTiny, _buildEmailFormFieldSection(viewModel), verticalSpaceMedium, - _buildCountryRegionSection(viewModel), + _buildCountryDropdownLabel(), + verticalSpaceSmall, + _buildCountryDropdown(viewModel), verticalSpaceMedium, + _buildRegionFormFieldWrapper(viewModel), + verticalSpaceMedium, + _buildOccupationDropdownWrapper(viewModel), verticalSpaceLarge, _buildLowerColumn(viewModel) @@ -415,30 +417,6 @@ class ProfileDetailView extends StackedView ), ); - Widget _buildBirthdayColumn(ProfileDetailViewModel viewModel) => Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: _buildBirthdayChildren(viewModel), - ); - - List _buildBirthdayChildren(ProfileDetailViewModel viewModel) => [ - _buildBirthdayLabel(), - verticalSpaceSmall, - _buildBirthdayFormField(viewModel), - ]; - - Widget _buildBirthdayLabel() => CustomFormLabel( - label: 'Birthday', - style: style16DG600, - ); - - Widget _buildBirthdayFormField(ProfileDetailViewModel viewModel) => - BirthdaySelector( - birthday: viewModel.selectedBirthday, - onSelected: (value) => - viewModel.setBirthday(DateFormat('d MMM, yyyy').format(value)), - ); - Widget _buildPhoneNumberFormFieldSection(ProfileDetailViewModel viewModel) => Column( mainAxisSize: MainAxisSize.min, @@ -533,39 +511,6 @@ class ProfileDetailView extends StackedView style: validationStyle, ); - Widget _buildCountryRegionSection(ProfileDetailViewModel viewModel) => Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: _buildCountryRegionChildren(viewModel), - ); - - List _buildCountryRegionChildren(ProfileDetailViewModel viewModel) => - [ - _buildCountryDropdownColumnWrapper(viewModel), - const SizedBox(width: 20), - _buildRegionDropdownColumnWrapper(viewModel) - ]; - - Widget _buildCountryDropdownColumnWrapper(ProfileDetailViewModel viewModel) => - Expanded( - child: _buildCountryDropdownColumn(viewModel), - ); - - Widget _buildCountryDropdownColumn(ProfileDetailViewModel viewModel) => - Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: _buildCountryDropdownChildren(viewModel), - ); - - List _buildCountryDropdownChildren( - ProfileDetailViewModel viewModel) => - [ - _buildCountryDropdownLabel(), - verticalSpaceSmall, - _buildCountryDropdown(viewModel) - ]; - Widget _buildCountryDropdownLabel() => CustomFormLabel( label: 'Country', style: style16DG600, @@ -579,38 +524,48 @@ class ProfileDetailView extends StackedView onChanged: (value) => viewModel.setSelectedCountry(value ?? 'Ethiopia'), ); - Widget _buildRegionDropdownColumnWrapper(ProfileDetailViewModel viewModel) => - Expanded( - child: _buildRegionDropdownColumn(viewModel), - ); - - Widget _buildRegionDropdownColumn(ProfileDetailViewModel viewModel) => Column( + Widget _buildRegionFormFieldWrapper(ProfileDetailViewModel viewModel) => + Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, - children: _buildRegionDropdownChildren(viewModel), + children: _buildRegionFormFieldChildren(viewModel), ); - List _buildRegionDropdownChildren(ProfileDetailViewModel viewModel) => + List _buildRegionFormFieldChildren( + ProfileDetailViewModel viewModel) => [ - _buildRegionDropdownLabel(), - verticalSpaceSmall, - _buildRegionDropdown(viewModel) + _buildRegionFormFieldLabel(),verticalSpaceSmall, + _buildRegionFormField(viewModel), + if (viewModel.hasRegionValidationMessage && viewModel.focusRegion) + verticalSpaceTiny, + if (viewModel.hasRegionValidationMessage && viewModel.focusRegion) + _buildRegionValidatorWrapper(viewModel), ]; - Widget _buildRegionDropdownLabel() => CustomFormLabel( + Widget _buildRegionFormFieldLabel() => CustomFormLabel( label: 'Region', style: style16DG600, ); - Widget _buildRegionDropdown(ProfileDetailViewModel viewModel) => - CustomDropdownPicker( - hint: 'Select region', - selectedItem: viewModel.selectedRegion, - items: (value, props) => - viewModel.getRegions(viewModel.selectedCountry), - onChanged: (value) => - viewModel.setSelectedRegion(value ?? 'Addis Ababa'), + Widget _buildRegionFormField(ProfileDetailViewModel viewModel) => + TextFormField( + controller: regionController, + onTap: viewModel.setRegionFocus, + decoration: inputDecoration( + hint: 'Enter Your City', + focus: viewModel.focusRegion, + filled: regionController.text.isNotEmpty), + ); + + Widget _buildRegionValidatorWrapper(ProfileDetailViewModel viewModel) => + viewModel.hasRegionValidationMessage + ? _buildRegionValidator(viewModel) + : Container(); + + Widget _buildRegionValidator(ProfileDetailViewModel viewModel) => Text( + viewModel.regionValidationMessage!, + style: style12R700, ); Widget _buildOccupationDropdownWrapper(ProfileDetailViewModel viewModel) => diff --git a/lib/ui/views/profile_detail/profile_detail_view.form.dart b/lib/ui/views/profile_detail/profile_detail_view.form.dart index 3c52f19..23b9060 100644 --- a/lib/ui/views/profile_detail/profile_detail_view.form.dart +++ b/lib/ui/views/profile_detail/profile_detail_view.form.dart @@ -14,6 +14,7 @@ import 'package:yimaru_app/ui/common/validators/form_validator.dart'; const bool _autoTextFieldValidation = true; const String EmailValueKey = 'email'; +const String RegionValueKey = 'region'; const String PhoneNumberValueKey = 'phoneNumber'; const String LastNameValueKey = 'lastName'; const String FirstNameValueKey = 'firstName'; @@ -27,6 +28,7 @@ final Map _ProfileDetailViewFocusNodes = {}; final Map _ProfileDetailViewTextValidations = { EmailValueKey: FormValidator.validateForm, + RegionValueKey: FormValidator.validateForm, PhoneNumberValueKey: FormValidator.validatePhoneNumberForm, LastNameValueKey: FormValidator.validateForm, FirstNameValueKey: FormValidator.validateForm, @@ -36,6 +38,8 @@ final Map mixin $ProfileDetailView { TextEditingController get emailController => _getFormTextEditingController(EmailValueKey); + TextEditingController get regionController => + _getFormTextEditingController(RegionValueKey); TextEditingController get phoneNumberController => _getFormTextEditingController(PhoneNumberValueKey); TextEditingController get lastNameController => @@ -46,6 +50,7 @@ mixin $ProfileDetailView { _getFormTextEditingController(OccupationValueKey); FocusNode get emailFocusNode => _getFormFocusNode(EmailValueKey); + FocusNode get regionFocusNode => _getFormFocusNode(RegionValueKey); FocusNode get phoneNumberFocusNode => _getFormFocusNode(PhoneNumberValueKey); FocusNode get lastNameFocusNode => _getFormFocusNode(LastNameValueKey); FocusNode get firstNameFocusNode => _getFormFocusNode(FirstNameValueKey); @@ -76,6 +81,7 @@ mixin $ProfileDetailView { /// with the latest textController values void syncFormWithViewModel(FormStateHelper model) { emailController.addListener(() => _updateFormData(model)); + regionController.addListener(() => _updateFormData(model)); phoneNumberController.addListener(() => _updateFormData(model)); lastNameController.addListener(() => _updateFormData(model)); firstNameController.addListener(() => _updateFormData(model)); @@ -92,6 +98,7 @@ mixin $ProfileDetailView { ) void listenToFormUpdated(FormViewModel model) { emailController.addListener(() => _updateFormData(model)); + regionController.addListener(() => _updateFormData(model)); phoneNumberController.addListener(() => _updateFormData(model)); lastNameController.addListener(() => _updateFormData(model)); firstNameController.addListener(() => _updateFormData(model)); @@ -106,6 +113,7 @@ mixin $ProfileDetailView { model.formValueMap ..addAll({ EmailValueKey: emailController.text, + RegionValueKey: regionController.text, PhoneNumberValueKey: phoneNumberController.text, LastNameValueKey: lastNameController.text, FirstNameValueKey: firstNameController.text, @@ -152,6 +160,7 @@ extension ValueProperties on FormStateHelper { } String? get emailValue => this.formValueMap[EmailValueKey] as String?; + String? get regionValue => this.formValueMap[RegionValueKey] as String?; String? get phoneNumberValue => this.formValueMap[PhoneNumberValueKey] as String?; String? get lastNameValue => this.formValueMap[LastNameValueKey] as String?; @@ -170,6 +179,17 @@ extension ValueProperties on FormStateHelper { } } + set regionValue(String? value) { + this.setData( + this.formValueMap..addAll({RegionValueKey: value}), + ); + + if (_ProfileDetailViewTextEditingControllers.containsKey(RegionValueKey)) { + _ProfileDetailViewTextEditingControllers[RegionValueKey]?.text = + value ?? ''; + } + } + set phoneNumberValue(String? value) { this.setData( this.formValueMap..addAll({PhoneNumberValueKey: value}), @@ -221,6 +241,9 @@ extension ValueProperties on FormStateHelper { bool get hasEmail => this.formValueMap.containsKey(EmailValueKey) && (emailValue?.isNotEmpty ?? false); + bool get hasRegion => + this.formValueMap.containsKey(RegionValueKey) && + (regionValue?.isNotEmpty ?? false); bool get hasPhoneNumber => this.formValueMap.containsKey(PhoneNumberValueKey) && (phoneNumberValue?.isNotEmpty ?? false); @@ -236,6 +259,8 @@ extension ValueProperties on FormStateHelper { bool get hasEmailValidationMessage => this.fieldsValidationMessages[EmailValueKey]?.isNotEmpty ?? false; + bool get hasRegionValidationMessage => + this.fieldsValidationMessages[RegionValueKey]?.isNotEmpty ?? false; bool get hasPhoneNumberValidationMessage => this.fieldsValidationMessages[PhoneNumberValueKey]?.isNotEmpty ?? false; bool get hasLastNameValidationMessage => @@ -247,6 +272,8 @@ extension ValueProperties on FormStateHelper { String? get emailValidationMessage => this.fieldsValidationMessages[EmailValueKey]; + String? get regionValidationMessage => + this.fieldsValidationMessages[RegionValueKey]; String? get phoneNumberValidationMessage => this.fieldsValidationMessages[PhoneNumberValueKey]; String? get lastNameValidationMessage => @@ -260,6 +287,8 @@ extension ValueProperties on FormStateHelper { extension Methods on FormStateHelper { void setEmailValidationMessage(String? validationMessage) => this.fieldsValidationMessages[EmailValueKey] = validationMessage; + void setRegionValidationMessage(String? validationMessage) => + this.fieldsValidationMessages[RegionValueKey] = validationMessage; void setPhoneNumberValidationMessage(String? validationMessage) => this.fieldsValidationMessages[PhoneNumberValueKey] = validationMessage; void setLastNameValidationMessage(String? validationMessage) => @@ -272,6 +301,7 @@ extension Methods on FormStateHelper { /// Clears text input fields on the Form void clearForm() { emailValue = ''; + regionValue = ''; phoneNumberValue = ''; lastNameValue = ''; firstNameValue = ''; @@ -282,6 +312,7 @@ extension Methods on FormStateHelper { void validateForm() { this.setValidationMessages({ EmailValueKey: getValidationMessage(EmailValueKey), + RegionValueKey: getValidationMessage(RegionValueKey), PhoneNumberValueKey: getValidationMessage(PhoneNumberValueKey), LastNameValueKey: getValidationMessage(LastNameValueKey), FirstNameValueKey: getValidationMessage(FirstNameValueKey), @@ -306,6 +337,7 @@ String? getValidationMessage(String key) { void updateValidationData(FormStateHelper model) => model.setValidationMessages({ EmailValueKey: getValidationMessage(EmailValueKey), + RegionValueKey: getValidationMessage(RegionValueKey), PhoneNumberValueKey: getValidationMessage(PhoneNumberValueKey), LastNameValueKey: getValidationMessage(LastNameValueKey), FirstNameValueKey: getValidationMessage(FirstNameValueKey), diff --git a/lib/ui/views/profile_detail/profile_detail_viewmodel.dart b/lib/ui/views/profile_detail/profile_detail_viewmodel.dart index a1a7592..a1eae51 100644 --- a/lib/ui/views/profile_detail/profile_detail_viewmodel.dart +++ b/lib/ui/views/profile_detail/profile_detail_viewmodel.dart @@ -47,10 +47,6 @@ class ProfileDetailViewModel extends ReactiveViewModel String? get selectedGender => _selectedGender; - // Birthday - String? _selectedBirthday; - - String? get selectedBirthday => _selectedBirthday; // First name bool _focusPhoneNumber = false; @@ -67,16 +63,17 @@ class ProfileDetailViewModel extends ReactiveViewModel String get selectedCountry => _selectedCountry; - // Region - String _selectedRegion = 'Addis Ababa'; - - String get selectedRegion => _selectedRegion; // Occupation bool _focusOccupation = false; bool get focusOccupation => _focusOccupation; + // Region + bool _focusRegion = false; + + bool get focusRegion => _focusRegion; + // User data final Map _userData = {}; @@ -100,11 +97,6 @@ class ProfileDetailViewModel extends ReactiveViewModel rebuildUi(); } - // Birthday - void setBirthday(String value) { - _selectedBirthday = value; - rebuildUi(); - } // Phone number void setPhoneNumberFocus() { @@ -119,47 +111,168 @@ class ProfileDetailViewModel extends ReactiveViewModel } // Country - List getCountries() => ['Ethiopia', 'Other']; + // Country + List getCountries() => [ + "Afghanistan", + "Albania", + "Algeria", + "Andorra", + "Angola", + "Argentina", + "Armenia", + "Australia", + "Austria", + "Azerbaijan", + "Bahrain", + "Bangladesh", + "Belarus", + "Belgium", + "Belize", + "Benin", + "Bhutan", + "Bolivia", + "Bosnia and Herzegovina", + "Botswana", + "Brazil", + "Brunei", + "Bulgaria", + "Burkina Faso", + "Burundi", + "Cambodia", + "Cameroon", + "Canada", + "Chad", + "Chile", + "China", + "Colombia", + "Comoros", + "Congo", + "Costa Rica", + "Croatia", + "Cuba", + "Cyprus", + "Czech Republic", + "Denmark", + "Djibouti", + "Dominican Republic", + "Ecuador", + "Egypt", + "El Salvador", + "Eritrea", + "Estonia", + "Eswatini", + "Ethiopia", + "Finland", + "France", + "Gabon", + "Gambia", + "Georgia", + "Germany", + "Ghana", + "Greece", + "Guatemala", + "Guinea", + "Haiti", + "Honduras", + "Hungary", + "Iceland", + "India", + "Indonesia", + "Iran", + "Iraq", + "Ireland", + "Israel", + "Italy", + "Jamaica", + "Japan", + "Jordan", + "Kazakhstan", + "Kenya", + "Kuwait", + "Kyrgyzstan", + "Laos", + "Latvia", + "Lebanon", + "Liberia", + "Libya", + "Lithuania", + "Luxembourg", + "Madagascar", + "Malawi", + "Malaysia", + "Maldives", + "Mali", + "Malta", + "Mexico", + "Moldova", + "Monaco", + "Mongolia", + "Morocco", + "Mozambique", + "Myanmar", + "Namibia", + "Nepal", + "Netherlands", + "New Zealand", + "Nicaragua", + "Niger", + "Nigeria", + "North Korea", + "Norway", + "Oman", + "Pakistan", + "Panama", + "Paraguay", + "Peru", + "Philippines", + "Poland", + "Portugal", + "Qatar", + "Romania", + "Russia", + "Rwanda", + "Saudi Arabia", + "Senegal", + "Serbia", + "Singapore", + "Slovakia", + "Slovenia", + "Somalia", + "South Africa", + "South Korea", + "Spain", + "Sri Lanka", + "Sudan", + "Sweden", + "Switzerland", + "Syria", + "Taiwan", + "Tajikistan", + "Tanzania", + "Thailand", + "Tunisia", + "Turkey", + "Uganda", + "Ukraine", + "United Arab Emirates", + "United Kingdom", + "United States", + "Uruguay", + "Uzbekistan", + "Venezuela", + "Vietnam", + "Yemen", + "Zambia", + "Zimbabwe" + ]; void setSelectedCountry(String value) { _selectedCountry = value; - if (selectedCountry != 'Ethiopia') { - _selectedRegion = 'Other'; - } else { - _selectedRegion = 'Addis Ababa'; - } + rebuildUi(); } - // Region - List getRegions(String country) { - if (country == 'Ethiopia') { - return [ - 'Afar', - 'SNNPR', - 'Amhara', - 'Harari', - 'Oromia', - 'Sidama', - 'Somali', - 'Tigray', - 'Gambela', - 'Dire Dawa', - 'Addis Ababa', - 'Central Ethiopia', - 'Benishangul-Gumuz', - 'South West Ethiopia', - ]; - } else { - return ['Other']; - } - } - - void setSelectedRegion(String value) { - _selectedRegion = value; - rebuildUi(); - } // Occupation void setOccupationFocus() { @@ -167,6 +280,12 @@ class ProfileDetailViewModel extends ReactiveViewModel rebuildUi(); } + // Region + void setRegionFocus() { + _focusRegion = true; + rebuildUi(); + } + // User data void addUserData(Map data) { _userData.addAll(data); diff --git a/lib/ui/widgets/birthday_selector.dart b/lib/ui/widgets/birthday_selector.dart deleted file mode 100644 index 02fdeb0..0000000 --- a/lib/ui/widgets/birthday_selector.dart +++ /dev/null @@ -1,103 +0,0 @@ -import 'package:flutter/material.dart'; -import '../common/app_colors.dart'; -import '../common/ui_helpers.dart'; -import 'package:omni_datetime_picker/omni_datetime_picker.dart'; - -class BirthdaySelector extends StatelessWidget { - final String? birthday; - final void Function(DateTime)? onSelected; - - const BirthdaySelector({super.key, this.birthday, this.onSelected}); - - DateTime _initialDate() { - try { - final parsedDate = format.parse(birthday ?? ''); - return parsedDate.isAfter(DateTime.now()) ? DateTime.now() : parsedDate; - } catch (_) { - return DateTime.now(); - } - } - - Future _pickDateTime( - BuildContext context, - ) async { - DateTime? dateTime = await showOmniDateTimePicker( - context: context, - is24HourMode: false, - isShowSeconds: false, - title: _buildTitle(), - lastDate: DateTime.now(), - firstDate: DateTime(1900), - barrierDismissible: true, - initialDate: _initialDate(), - titleSeparator: const Divider(), - padding: const EdgeInsets.all(16), - type: OmniDateTimePickerType.date, - borderRadius: const BorderRadius.all(Radius.circular(15)), - insetPadding: const EdgeInsets.symmetric(horizontal: 40, vertical: 24), - theme: ThemeData( - colorScheme: - const ColorScheme.light().copyWith(primary: kcPrimaryColor), - ), - ); - - if (dateTime != null) { - // String formattedDateTime = DateFormat('d MMM, yyyy').format(dateTime); - - if (onSelected != null) { - onSelected!(dateTime); - } - } - } - - @override - Widget build( - BuildContext context, - ) => - _buildButtonWrapper( - context, - ); - - Widget _buildButtonWrapper(BuildContext context) => Container( - height: 50, - width: double.maxFinite, - margin: const EdgeInsets.only(bottom: 15), - child: _buildContainerWrapper(context), - ); - - Widget _buildContainerWrapper(BuildContext context) => GestureDetector( - onTap: () async => await _pickDateTime( - context, - ), - child: _buildContainer(), - ); - - Widget _buildTitle() => Text('Birthday', style: style16DG600); - - Widget _buildContainer() => Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(4), - color: kcPrimaryColor.withOpacity(0.1), - border: Border.all(color: kcPrimaryColor), - ), - padding: const EdgeInsets.symmetric(horizontal: 15), - child: _buildButtonRowWrapper(), - ); - - Widget _buildButtonRowWrapper() => Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: _buildButtonRowChildren(), - ); - - List _buildButtonRowChildren() => [_buildText(), _buildIcon()]; - - Widget _buildText() => Text( - birthday ?? 'Pick birthday', - style: const TextStyle(color: kcDarkGrey), - ); - - Widget _buildIcon() => const Icon( - Icons.calendar_month, - color: kcPrimaryColor, - ); -} diff --git a/lib/ui/widgets/cancel_learn_practice_sheet.dart b/lib/ui/widgets/cancel_learn_practice_sheet.dart index a3c5de3..ba9a328 100644 --- a/lib/ui/widgets/cancel_learn_practice_sheet.dart +++ b/lib/ui/widgets/cancel_learn_practice_sheet.dart @@ -15,15 +15,17 @@ class CancelLearnPracticeSheet extends StatelessWidget { final GestureTapCallback? onContinue; const CancelLearnPracticeSheet( - {super.key, this.onClose, this.onCancel, this.onContinue,required this.user}); + {super.key, + this.onClose, + this.onCancel, + this.onContinue, + required this.user}); @override - Widget build(BuildContext context) => - _buildSheetWrapper(); + Widget build(BuildContext context) => _buildSheetWrapper(); - Widget _buildSheetWrapper() => - CustomBottomSheet( - height: 500, onTap: onClose, child: _buildColumnWrapper()); + Widget _buildSheetWrapper() => CustomBottomSheet( + height: 500, onTap: onClose, child: _buildColumnWrapper()); Widget _buildColumnWrapper() => Padding( padding: const EdgeInsets.symmetric(horizontal: 15), diff --git a/lib/ui/widgets/learn_practice_results_wrapper.dart b/lib/ui/widgets/learn_practice_results_wrapper.dart index b9ea64c..7c0fa76 100644 --- a/lib/ui/widgets/learn_practice_results_wrapper.dart +++ b/lib/ui/widgets/learn_practice_results_wrapper.dart @@ -29,11 +29,8 @@ class LearnPracticeResultsWrapper children: _buildColumnChildren(viewModel), ); - List _buildColumnChildren(LearnPracticeViewModel viewModel) => [ - _buildTitle(), - verticalSpaceSmall, - _buildResults(viewModel) - ]; + List _buildColumnChildren(LearnPracticeViewModel viewModel) => + [_buildTitle(), verticalSpaceSmall, _buildResults(viewModel)]; Widget _buildTitle() => Text( 'Conversation Review', @@ -42,14 +39,14 @@ class LearnPracticeResultsWrapper ); Widget _buildResults(LearnPracticeViewModel viewModel) => ListView.separated( - shrinkWrap: true, - itemCount: viewModel.answers.length, - physics: const NeverScrollableScrollPhysics(), - separatorBuilder: (context, index) => verticalSpaceSmall, - itemBuilder: (context, index) => _buildResult( viewModel.answers[index]), - ); + shrinkWrap: true, + itemCount: viewModel.answers.length, + physics: const NeverScrollableScrollPhysics(), + separatorBuilder: (context, index) => verticalSpaceSmall, + itemBuilder: (context, index) => _buildResult(viewModel.answers[index]), + ); - - - Widget _buildResult(Map answer) => LearnPracticeResultCard(answer: answer,); + Widget _buildResult(Map answer) => LearnPracticeResultCard( + answer: answer, + ); } diff --git a/lib/ui/widgets/learn_practice_tip_section.dart b/lib/ui/widgets/learn_practice_tip_section.dart index a1233ee..49efe70 100644 --- a/lib/ui/widgets/learn_practice_tip_section.dart +++ b/lib/ui/widgets/learn_practice_tip_section.dart @@ -10,7 +10,8 @@ class LearnPracticeTipSection extends ViewModelWidget { const LearnPracticeTipSection({super.key}); @override - Widget build(BuildContext context,LearnPracticeViewModel viewModel) => _buildContainer(viewModel); + Widget build(BuildContext context, LearnPracticeViewModel viewModel) => + _buildContainer(viewModel); Widget _buildContainer(LearnPracticeViewModel viewModel) => Container( padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 25), @@ -24,7 +25,7 @@ class LearnPracticeTipSection extends ViewModelWidget { Widget _buildColumn(LearnPracticeViewModel viewModel) => Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, - children: _buildColumnChildren( viewModel), + children: _buildColumnChildren(viewModel), ); List _buildColumnChildren(LearnPracticeViewModel viewModel) => diff --git a/pubspec.yaml b/pubspec.yaml index 1866157..b3c9d0b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: yimaru_app -version: 0.1.4+6 +version: 0.1.5+7 publish_to: 'none' description: A new Flutter project. diff --git a/test/helpers/test_helpers.mocks.dart b/test/helpers/test_helpers.mocks.dart index de8f496..9202a62 100644 --- a/test/helpers/test_helpers.mocks.dart +++ b/test/helpers/test_helpers.mocks.dart @@ -1992,6 +1992,13 @@ class MockVoiceRecorderService extends _i1.Mock ), ) as _i5.WaveformRecorderController); + @override + bool get isRecording => (super.noSuchMethod( + Invocation.getter(#isRecording), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + @override int get listenersCount => (super.noSuchMethod( Invocation.getter(#listenersCount),