fix: Apply UAT fixes
This commit is contained in:
parent
2619210611
commit
2eb7e7f031
|
|
@ -53,6 +53,8 @@ import 'package:yimaru_app/ui/views/learn_program/learn_program_view.dart';
|
||||||
import 'package:yimaru_app/ui/views/learn_course/learn_course_view.dart';
|
import 'package:yimaru_app/ui/views/learn_course/learn_course_view.dart';
|
||||||
import 'package:yimaru_app/ui/views/assessment/assessment_view.dart';
|
import 'package:yimaru_app/ui/views/assessment/assessment_view.dart';
|
||||||
import 'package:yimaru_app/services/vimeo_service.dart';
|
import 'package:yimaru_app/services/vimeo_service.dart';
|
||||||
|
import 'package:yimaru_app/services/url_launcher_service.dart';
|
||||||
|
import 'package:yimaru_app/services/phone_caller_service.dart';
|
||||||
// @stacked-import
|
// @stacked-import
|
||||||
|
|
||||||
@StackedApp(
|
@StackedApp(
|
||||||
|
|
@ -114,6 +116,8 @@ import 'package:yimaru_app/services/vimeo_service.dart';
|
||||||
LazySingleton(classType: VoiceRecorderService),
|
LazySingleton(classType: VoiceRecorderService),
|
||||||
LazySingleton(classType: InAppUpdateService),
|
LazySingleton(classType: InAppUpdateService),
|
||||||
LazySingleton(classType: VimeoService),
|
LazySingleton(classType: VimeoService),
|
||||||
|
LazySingleton(classType: UrlLauncherService),
|
||||||
|
LazySingleton(classType: PhoneCallerService),
|
||||||
// @stacked-service
|
// @stacked-service
|
||||||
],
|
],
|
||||||
bottomsheets: [
|
bottomsheets: [
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,11 @@ import '../services/image_picker_service.dart';
|
||||||
import '../services/in_app_update_service.dart';
|
import '../services/in_app_update_service.dart';
|
||||||
import '../services/notification_service.dart';
|
import '../services/notification_service.dart';
|
||||||
import '../services/permission_handler_service.dart';
|
import '../services/permission_handler_service.dart';
|
||||||
|
import '../services/phone_caller_service.dart';
|
||||||
import '../services/secure_storage_service.dart';
|
import '../services/secure_storage_service.dart';
|
||||||
import '../services/smart_auth_service.dart';
|
import '../services/smart_auth_service.dart';
|
||||||
import '../services/status_checker_service.dart';
|
import '../services/status_checker_service.dart';
|
||||||
|
import '../services/url_launcher_service.dart';
|
||||||
import '../services/vimeo_service.dart';
|
import '../services/vimeo_service.dart';
|
||||||
import '../services/voice_recorder_service.dart';
|
import '../services/voice_recorder_service.dart';
|
||||||
|
|
||||||
|
|
@ -57,4 +59,6 @@ Future<void> setupLocator(
|
||||||
locator.registerLazySingleton(() => VoiceRecorderService());
|
locator.registerLazySingleton(() => VoiceRecorderService());
|
||||||
locator.registerLazySingleton(() => InAppUpdateService());
|
locator.registerLazySingleton(() => InAppUpdateService());
|
||||||
locator.registerLazySingleton(() => VimeoService());
|
locator.registerLazySingleton(() => VimeoService());
|
||||||
|
locator.registerLazySingleton(() => UrlLauncherService());
|
||||||
|
locator.registerLazySingleton(() => PhoneCallerService());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -366,11 +366,15 @@ class ApiService {
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
var data = response.data;
|
var data = response.data;
|
||||||
var decodedData = data['data']['question_sets'] as List;
|
var decodedData = data['data']['question_sets'] as List;
|
||||||
assessments = decodedData.map(
|
assessments = decodedData
|
||||||
(e) {
|
.map(
|
||||||
return Assessment.fromJson(e);
|
(e) {
|
||||||
},
|
return Assessment.fromJson(e);
|
||||||
).toList();
|
},
|
||||||
|
)
|
||||||
|
.toList()
|
||||||
|
.reversed
|
||||||
|
.toList();
|
||||||
return assessments;
|
return assessments;
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
|
|
|
||||||
8
lib/services/phone_caller_service.dart
Normal file
8
lib/services/phone_caller_service.dart
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
import 'package:flutter_phone_direct_caller/flutter_phone_direct_caller.dart';
|
||||||
|
|
||||||
|
class PhoneCallerService {
|
||||||
|
Future<void> call(String phone) async =>
|
||||||
|
await FlutterPhoneDirectCaller.callNumber(phone);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
12
lib/services/url_launcher_service.dart
Normal file
12
lib/services/url_launcher_service.dart
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
|
class UrlLauncherService {
|
||||||
|
Future<void> launchUri(String url) async {
|
||||||
|
Uri uri = Uri.parse(url);
|
||||||
|
if (await canLaunchUrl(uri)) {
|
||||||
|
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||||
|
} else {
|
||||||
|
launchUrl(uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
// Endpoints
|
||||||
String kBaseUrl = 'https://api.yimaruacademy.com';
|
String kBaseUrl = 'https://api.yimaruacademy.com';
|
||||||
|
|
||||||
String kApiUrl = 'api';
|
String kApiUrl = 'api';
|
||||||
|
|
@ -83,3 +84,8 @@ String kSampleVideoUrl =
|
||||||
|
|
||||||
String kServerClientId =
|
String kServerClientId =
|
||||||
'900714037062-24ria5fcfet71o3vde8f6gsvsj1n68ec.apps.googleusercontent.com';
|
'900714037062-24ria5fcfet71o3vde8f6gsvsj1n68ec.apps.googleusercontent.com';
|
||||||
|
|
||||||
|
// Other
|
||||||
|
String kPhoneSupport = '+251946396655';
|
||||||
|
|
||||||
|
String kTelegramSupport = '@yimaruacademy2026';
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
|
||||||
const String ksHomeBottomSheetTitle = 'Build Great Apps!';
|
const String ksHomeBottomSheetTitle = 'Build Great Apps!';
|
||||||
|
|
||||||
const String ksSuggestion =
|
const String ksSuggestion =
|
||||||
|
|
|
||||||
|
|
@ -99,8 +99,7 @@ class AssessmentViewModel extends BaseViewModel {
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
print('COUNT: $count');
|
|
||||||
print('ASSESSMENT: ${_currentAssessment?.passingScore}');
|
|
||||||
if (count >= (_currentAssessment?.passingScore ?? 0)) {
|
if (count >= (_currentAssessment?.passingScore ?? 0)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -152,9 +151,7 @@ class AssessmentViewModel extends BaseViewModel {
|
||||||
Future<void> nextQuestion() async {
|
Future<void> nextQuestion() async {
|
||||||
_currentQuestionIndex++;
|
_currentQuestionIndex++;
|
||||||
Map<String, dynamic> response = evaluateAssessment();
|
Map<String, dynamic> response = evaluateAssessment();
|
||||||
print('LEVEL: $response');
|
|
||||||
print('LENGTH: ${_assessmentQuestions.length}');
|
|
||||||
print('INDEX: $_currentQuestionIndex');
|
|
||||||
if (_currentQuestionIndex == _assessmentQuestions.length) {
|
if (_currentQuestionIndex == _assessmentQuestions.length) {
|
||||||
_currentAssessmentIndex = _currentAssessmentIndex + 1;
|
_currentAssessmentIndex = _currentAssessmentIndex + 1;
|
||||||
if (_currentAssessmentIndex == _assessments.length) {
|
if (_currentAssessmentIndex == _assessments.length) {
|
||||||
|
|
@ -279,7 +276,6 @@ class AssessmentViewModel extends BaseViewModel {
|
||||||
Future<void> _getAssessments() async {
|
Future<void> _getAssessments() async {
|
||||||
if (await _statusChecker.checkConnection()) {
|
if (await _statusChecker.checkConnection()) {
|
||||||
_assessments = await _apiService.getAssessments();
|
_assessments = await _apiService.getAssessments();
|
||||||
_assessments.reversed;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -290,6 +286,8 @@ class AssessmentViewModel extends BaseViewModel {
|
||||||
Future<void> _getAssessmentQuestions(int id) async {
|
Future<void> _getAssessmentQuestions(int id) async {
|
||||||
if (await _statusChecker.checkConnection()) {
|
if (await _statusChecker.checkConnection()) {
|
||||||
_assessmentQuestions = await _apiService.getAssessmentQuestions(id);
|
_assessmentQuestions = await _apiService.getAssessmentQuestions(id);
|
||||||
|
_assessmentQuestions
|
||||||
|
.removeWhere((question) => question.questionType != 'MCQ');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,57 +54,61 @@ class AssessmentQuestionsScreen extends ViewModelWidget<AssessmentViewModel> {
|
||||||
itemCount: viewModel.assessmentQuestions.length,
|
itemCount: viewModel.assessmentQuestions.length,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
itemBuilder: (cotext, index) =>
|
itemBuilder: (cotext, index) =>
|
||||||
_buildBodyScroller(index: index, viewModel: viewModel),
|
_buildBodyScroller( viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildBodyScroller(
|
Widget _buildBodyScroller(
|
||||||
{required int index, required AssessmentViewModel viewModel}) =>
|
AssessmentViewModel viewModel) =>
|
||||||
SingleChildScrollView(
|
SingleChildScrollView(
|
||||||
child: _buildBody(index: index, viewModel: viewModel),
|
child: _buildBody( viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildBody(
|
Widget _buildBody(
|
||||||
{required int index, required AssessmentViewModel viewModel}) =>
|
AssessmentViewModel viewModel) =>
|
||||||
Column(
|
Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: _buildBodyChildren(viewModel: viewModel, index: index),
|
children: _buildBodyChildren( viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildBodyChildren(
|
List<Widget> _buildBodyChildren(
|
||||||
{required int index, required AssessmentViewModel viewModel}) =>
|
AssessmentViewModel viewModel) =>[
|
||||||
[
|
|
||||||
verticalSpaceMedium,
|
|
||||||
_buildTitle(index: index, viewModel: viewModel),
|
|
||||||
verticalSpaceMedium,
|
|
||||||
_buildAnswers(index: index, viewModel: viewModel),
|
|
||||||
_buildContinueButtonWrapper(viewModel: viewModel, question: index + 1)
|
|
||||||
];
|
|
||||||
|
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildTitleState( viewModel),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildAnswersState( viewModel),
|
||||||
|
_buildContinueButtonWrapper( viewModel)
|
||||||
|
];
|
||||||
|
Widget _buildTitleState( AssessmentViewModel viewModel)=> viewModel.currentQuestionIndex ==
|
||||||
|
viewModel.assessmentQuestions.length ?Container(): _buildTitle(viewModel);
|
||||||
Widget _buildTitle(
|
Widget _buildTitle(
|
||||||
{required int index, required AssessmentViewModel viewModel}) =>
|
AssessmentViewModel viewModel) =>
|
||||||
Text(
|
Text(
|
||||||
'Q${index + 1}. ${viewModel.assessmentQuestions[index].questionText} ',
|
'Q${viewModel.currentQuestionIndex + 1}. ${viewModel.assessmentQuestions[viewModel.currentQuestionIndex].questionText} ',
|
||||||
style: style16DG600,
|
style: style16DG600,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Widget _buildAnswersState( AssessmentViewModel viewModel)=> viewModel.currentQuestionIndex ==
|
||||||
|
viewModel.assessmentQuestions.length ?Container(): _buildAnswers(viewModel);
|
||||||
|
|
||||||
Widget _buildAnswers(
|
Widget _buildAnswers(
|
||||||
{required int index, required AssessmentViewModel viewModel}) =>
|
AssessmentViewModel viewModel) =>
|
||||||
ListView.builder(
|
ListView.builder(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
itemCount: viewModel.assessmentQuestions[index].options?.length,
|
itemCount: viewModel.assessmentQuestions[viewModel.currentQuestionIndex].options?.length,
|
||||||
itemBuilder: (context, inner) => _buildAnswer(
|
itemBuilder: (context, inner) => _buildAnswer(
|
||||||
onTap: () => viewModel.setSelectedAnswer(
|
onTap: () => viewModel.setSelectedAnswer(
|
||||||
question: index + 1,
|
question: viewModel.currentQuestionIndex + 1,
|
||||||
option: viewModel.assessmentQuestions[index].options?[inner]),
|
option: viewModel.assessmentQuestions[viewModel.currentQuestionIndex].options?[inner]),
|
||||||
title:
|
title:
|
||||||
viewModel.assessmentQuestions[index].options?[inner].optionText ??
|
viewModel.assessmentQuestions[viewModel.currentQuestionIndex].options?[inner].optionText ??
|
||||||
'',
|
'',
|
||||||
selected: viewModel.isSelectedAnswer(
|
selected: viewModel.isSelectedAnswer(
|
||||||
question: index + 1,
|
question: viewModel.currentQuestionIndex + 1,
|
||||||
answer: viewModel
|
answer: viewModel
|
||||||
.assessmentQuestions[index].options?[inner].optionText ??
|
.assessmentQuestions[viewModel.currentQuestionIndex ].options?[inner].optionText ??
|
||||||
''),
|
''),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -120,28 +124,29 @@ class AssessmentQuestionsScreen extends ViewModelWidget<AssessmentViewModel> {
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildContinueButtonWrapper(
|
Widget _buildContinueButtonWrapper(
|
||||||
{required int question, required AssessmentViewModel viewModel}) =>
|
AssessmentViewModel viewModel) =>
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 50),
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
child: _buildContinueButton(viewModel: viewModel, question: question),
|
child: _buildContinueButton( viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildContinueButton(
|
Widget _buildContinueButton(
|
||||||
{required int question, required AssessmentViewModel viewModel}) =>
|
AssessmentViewModel viewModel) =>
|
||||||
CustomElevatedButton(
|
CustomElevatedButton(
|
||||||
height: 55,
|
height: 55,
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
foregroundColor: kcWhite,
|
foregroundColor: kcWhite,
|
||||||
backgroundColor:
|
|
||||||
viewModel.selectedAnswers.containsKey(question.toString())
|
|
||||||
? kcPrimaryColor
|
|
||||||
: kcPrimaryColor.withOpacity(0.1),
|
|
||||||
onTap: viewModel.selectedAnswers.containsKey(question.toString())
|
|
||||||
? () => viewModel.nextQuestion()
|
|
||||||
: null,
|
|
||||||
text: viewModel.currentQuestionIndex ==
|
text: viewModel.currentQuestionIndex ==
|
||||||
viewModel.assessmentQuestions.length - 1
|
viewModel.assessmentQuestions.length - 1
|
||||||
? 'Finish Level'
|
? 'Finish Level'
|
||||||
: 'Continue',
|
: 'Continue',
|
||||||
|
backgroundColor:
|
||||||
|
viewModel.selectedAnswers.containsKey('${viewModel.currentQuestionIndex + 1}')
|
||||||
|
? kcPrimaryColor
|
||||||
|
: kcPrimaryColor.withOpacity(0.1),
|
||||||
|
onTap: viewModel.selectedAnswers.containsKey('${viewModel.currentQuestionIndex + 1}')
|
||||||
|
? () => viewModel.nextQuestion()
|
||||||
|
: null,
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -104,15 +104,14 @@ class LearnPracticeView extends StackedView<LearnPracticeViewModel> {
|
||||||
|
|
||||||
List<Widget> _buildScreens(LearnPracticeViewModel viewModel) => [
|
List<Widget> _buildScreens(LearnPracticeViewModel viewModel) => [
|
||||||
_buildLearnPracticeIntroScreen(),
|
_buildLearnPracticeIntroScreen(),
|
||||||
_buildLearnPracticeElementsScreen(),
|
_buildLearnPracticeElementsScreen(),
|
||||||
_buildLearnPracticeQuestionsScreen(),
|
_buildLearnPracticeQuestionsScreen(),
|
||||||
_buildFinishLearnPracticeScreen(),
|
_buildFinishLearnPracticeScreen(),
|
||||||
_buildLearnPracticeResultScreen(),
|
_buildLearnPracticeResultScreen(),
|
||||||
_buildLearnPracticeCompletionScreen()
|
_buildLearnPracticeCompletionScreen()
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildLearnPracticeIntroScreen() =>
|
Widget _buildLearnPracticeIntroScreen() => const LearnPracticeIntroScreen();
|
||||||
const LearnPracticeIntroScreen();
|
|
||||||
|
|
||||||
Widget _buildLearnPracticeElementsScreen() =>
|
Widget _buildLearnPracticeElementsScreen() =>
|
||||||
const LearnPracticeDescriptionScreen();
|
const LearnPracticeDescriptionScreen();
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,8 @@ class FinishLearnPracticeScreen
|
||||||
extends ViewModelWidget<LearnPracticeViewModel> {
|
extends ViewModelWidget<LearnPracticeViewModel> {
|
||||||
const FinishLearnPracticeScreen({super.key});
|
const FinishLearnPracticeScreen({super.key});
|
||||||
|
|
||||||
Future<void> _reset(LearnPracticeViewModel viewModel)async =>await viewModel.reset();
|
Future<void> _reset(LearnPracticeViewModel viewModel) async =>
|
||||||
|
await viewModel.reset();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, LearnPracticeViewModel viewModel) =>
|
Widget build(BuildContext context, LearnPracticeViewModel viewModel) =>
|
||||||
|
|
@ -123,6 +124,6 @@ class FinishLearnPracticeScreen
|
||||||
backgroundColor: kcWhite,
|
backgroundColor: kcWhite,
|
||||||
borderColor: kcPrimaryColor,
|
borderColor: kcPrimaryColor,
|
||||||
foregroundColor: kcPrimaryColor,
|
foregroundColor: kcPrimaryColor,
|
||||||
onTap: ()async => await _reset(viewModel) ,
|
onTap: () async => await _reset(viewModel),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -153,9 +153,9 @@ class LearnPracticeDescriptionScreen
|
||||||
Widget _buildImage(LearnPracticeViewModel viewModel) => CachedNetworkImage(
|
Widget _buildImage(LearnPracticeViewModel viewModel) => CachedNetworkImage(
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
width: double.maxFinite,
|
width: double.maxFinite,
|
||||||
imageUrl: getReadableUrl(viewModel.practices.first.storyImage ?? '') ?? '',
|
imageUrl:
|
||||||
|
getReadableUrl(viewModel.practices.first.storyImage ?? '') ?? '',
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildContinueButtonWrapper(LearnPracticeViewModel viewModel) =>
|
Widget _buildContinueButtonWrapper(LearnPracticeViewModel viewModel) =>
|
||||||
Padding(
|
Padding(
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,8 @@ class LearnPracticeResultScreen
|
||||||
extends ViewModelWidget<LearnPracticeViewModel> {
|
extends ViewModelWidget<LearnPracticeViewModel> {
|
||||||
const LearnPracticeResultScreen({super.key});
|
const LearnPracticeResultScreen({super.key});
|
||||||
|
|
||||||
void _navigate(LearnPracticeViewModel viewModel) async=>await viewModel.reset();
|
void _navigate(LearnPracticeViewModel viewModel) async =>
|
||||||
|
await viewModel.reset();
|
||||||
|
|
||||||
Future<void> _cancel(LearnPracticeViewModel viewModel) async {
|
Future<void> _cancel(LearnPracticeViewModel viewModel) async {
|
||||||
await viewModel.stopRecording();
|
await viewModel.stopRecording();
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,8 @@ class StartLearnPracticeScreen extends ViewModelWidget<LearnPracticeViewModel> {
|
||||||
viewModel.pop();
|
viewModel.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _start(LearnPracticeViewModel viewModel)async =>
|
Future<void> _start(LearnPracticeViewModel viewModel) async =>
|
||||||
await viewModel.playVoicePrompt(question);
|
await viewModel.playVoicePrompt(question);
|
||||||
|
|
||||||
|
|
||||||
Future<void> _showSheet(
|
Future<void> _showSheet(
|
||||||
{required BuildContext context,
|
{required BuildContext context,
|
||||||
|
|
@ -115,7 +114,7 @@ class StartLearnPracticeScreen extends ViewModelWidget<LearnPracticeViewModel> {
|
||||||
|
|
||||||
Widget _buildStartButtonContainer(LearnPracticeViewModel viewModel) =>
|
Widget _buildStartButtonContainer(LearnPracticeViewModel viewModel) =>
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () async=>await _start(viewModel),
|
onTap: () async => await _start(viewModel),
|
||||||
child: _buildStartButton(),
|
child: _buildStartButton(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -142,16 +141,16 @@ class StartLearnPracticeScreen extends ViewModelWidget<LearnPracticeViewModel> {
|
||||||
startAngle: 0.0,
|
startAngle: 0.0,
|
||||||
center: Alignment.center,
|
center: Alignment.center,
|
||||||
colors: [
|
colors: [
|
||||||
kcPrimaryColor.withValues(alpha:0.3),
|
kcPrimaryColor.withValues(alpha: 0.3),
|
||||||
kcIndigo.withValues(alpha:0.2),
|
kcIndigo.withValues(alpha: 0.2),
|
||||||
kcIndigo.withValues(alpha:0.3),
|
kcIndigo.withValues(alpha: 0.3),
|
||||||
kcIndigo.withValues(alpha:0.4),
|
kcIndigo.withValues(alpha: 0.4),
|
||||||
kcIndigo.withValues(alpha:0.5),
|
kcIndigo.withValues(alpha: 0.5),
|
||||||
kcPrimaryColor.withValues(alpha:0.5),
|
kcPrimaryColor.withValues(alpha: 0.5),
|
||||||
kcPrimaryColor.withValues(alpha:0.4),
|
kcPrimaryColor.withValues(alpha: 0.4),
|
||||||
kcPrimaryColor.withValues(alpha:0.3),
|
kcPrimaryColor.withValues(alpha: 0.3),
|
||||||
kcPrimaryColor.withValues(alpha: 0.2),
|
kcPrimaryColor.withValues(alpha: 0.2),
|
||||||
kcPrimaryColor.withValues(alpha:0.5),
|
kcPrimaryColor.withValues(alpha: 0.5),
|
||||||
],
|
],
|
||||||
// quarterly spread
|
// quarterly spread
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ import 'onboarding_view.form.dart';
|
||||||
name: 'fullName', validator: FormValidator.validateFullNameForm),
|
name: 'fullName', validator: FormValidator.validateFullNameForm),
|
||||||
FormTextField(name: 'region', validator: FormValidator.validateForm),
|
FormTextField(name: 'region', validator: FormValidator.validateForm),
|
||||||
FormTextField(name: 'challenge', validator: FormValidator.validateForm),
|
FormTextField(name: 'challenge', validator: FormValidator.validateForm),
|
||||||
FormTextField(name: 'occupation', validator: FormValidator.validateForm),
|
|
||||||
FormTextField(name: 'languageGoal', validator: FormValidator.validateForm),
|
FormTextField(name: 'languageGoal', validator: FormValidator.validateForm),
|
||||||
])
|
])
|
||||||
class OnboardingView extends StackedView<OnboardingViewModel>
|
class OnboardingView extends StackedView<OnboardingViewModel>
|
||||||
|
|
@ -39,7 +38,6 @@ class OnboardingView extends StackedView<OnboardingViewModel>
|
||||||
regionController.clear();
|
regionController.clear();
|
||||||
fullNameController.clear();
|
fullNameController.clear();
|
||||||
challengeController.clear();
|
challengeController.clear();
|
||||||
occupationController.clear();
|
|
||||||
languageGoalController.clear();
|
languageGoalController.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,7 +52,6 @@ class OnboardingView extends StackedView<OnboardingViewModel>
|
||||||
} else if (viewModel.currentPage == 3) {
|
} else if (viewModel.currentPage == 3) {
|
||||||
viewModel.resetEducationalBackgroundFormScreen();
|
viewModel.resetEducationalBackgroundFormScreen();
|
||||||
} else if (viewModel.currentPage == 4) {
|
} else if (viewModel.currentPage == 4) {
|
||||||
occupationController.clear();
|
|
||||||
viewModel.resetOccupationFormScreen();
|
viewModel.resetOccupationFormScreen();
|
||||||
} else if (viewModel.currentPage == 5) {
|
} else if (viewModel.currentPage == 5) {
|
||||||
viewModel.resetCountryRegionFormScreen();
|
viewModel.resetCountryRegionFormScreen();
|
||||||
|
|
@ -135,8 +132,7 @@ class OnboardingView extends StackedView<OnboardingViewModel>
|
||||||
Widget _buildEducationalBackgroundForm() =>
|
Widget _buildEducationalBackgroundForm() =>
|
||||||
const EducationalBackgroundFormScreen();
|
const EducationalBackgroundFormScreen();
|
||||||
|
|
||||||
Widget _buildOccupationForm() =>
|
Widget _buildOccupationForm() => const OccupationFormScreen();
|
||||||
OccupationFormScreen(occupationController: occupationController);
|
|
||||||
|
|
||||||
Widget _buildCountryRegionForm() => CountryRegionFormScreen(
|
Widget _buildCountryRegionForm() => CountryRegionFormScreen(
|
||||||
regionController: regionController,
|
regionController: regionController,
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@ const String TopicValueKey = 'topic';
|
||||||
const String FullNameValueKey = 'fullName';
|
const String FullNameValueKey = 'fullName';
|
||||||
const String RegionValueKey = 'region';
|
const String RegionValueKey = 'region';
|
||||||
const String ChallengeValueKey = 'challenge';
|
const String ChallengeValueKey = 'challenge';
|
||||||
const String OccupationValueKey = 'occupation';
|
|
||||||
const String LanguageGoalValueKey = 'languageGoal';
|
const String LanguageGoalValueKey = 'languageGoal';
|
||||||
|
|
||||||
final Map<String, TextEditingController> _OnboardingViewTextEditingControllers =
|
final Map<String, TextEditingController> _OnboardingViewTextEditingControllers =
|
||||||
|
|
@ -30,7 +29,6 @@ final Map<String, String? Function(String?)?> _OnboardingViewTextValidations = {
|
||||||
FullNameValueKey: FormValidator.validateFullNameForm,
|
FullNameValueKey: FormValidator.validateFullNameForm,
|
||||||
RegionValueKey: FormValidator.validateForm,
|
RegionValueKey: FormValidator.validateForm,
|
||||||
ChallengeValueKey: FormValidator.validateForm,
|
ChallengeValueKey: FormValidator.validateForm,
|
||||||
OccupationValueKey: FormValidator.validateForm,
|
|
||||||
LanguageGoalValueKey: FormValidator.validateForm,
|
LanguageGoalValueKey: FormValidator.validateForm,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -43,8 +41,6 @@ mixin $OnboardingView {
|
||||||
_getFormTextEditingController(RegionValueKey);
|
_getFormTextEditingController(RegionValueKey);
|
||||||
TextEditingController get challengeController =>
|
TextEditingController get challengeController =>
|
||||||
_getFormTextEditingController(ChallengeValueKey);
|
_getFormTextEditingController(ChallengeValueKey);
|
||||||
TextEditingController get occupationController =>
|
|
||||||
_getFormTextEditingController(OccupationValueKey);
|
|
||||||
TextEditingController get languageGoalController =>
|
TextEditingController get languageGoalController =>
|
||||||
_getFormTextEditingController(LanguageGoalValueKey);
|
_getFormTextEditingController(LanguageGoalValueKey);
|
||||||
|
|
||||||
|
|
@ -52,7 +48,6 @@ mixin $OnboardingView {
|
||||||
FocusNode get fullNameFocusNode => _getFormFocusNode(FullNameValueKey);
|
FocusNode get fullNameFocusNode => _getFormFocusNode(FullNameValueKey);
|
||||||
FocusNode get regionFocusNode => _getFormFocusNode(RegionValueKey);
|
FocusNode get regionFocusNode => _getFormFocusNode(RegionValueKey);
|
||||||
FocusNode get challengeFocusNode => _getFormFocusNode(ChallengeValueKey);
|
FocusNode get challengeFocusNode => _getFormFocusNode(ChallengeValueKey);
|
||||||
FocusNode get occupationFocusNode => _getFormFocusNode(OccupationValueKey);
|
|
||||||
FocusNode get languageGoalFocusNode =>
|
FocusNode get languageGoalFocusNode =>
|
||||||
_getFormFocusNode(LanguageGoalValueKey);
|
_getFormFocusNode(LanguageGoalValueKey);
|
||||||
|
|
||||||
|
|
@ -84,7 +79,6 @@ mixin $OnboardingView {
|
||||||
fullNameController.addListener(() => _updateFormData(model));
|
fullNameController.addListener(() => _updateFormData(model));
|
||||||
regionController.addListener(() => _updateFormData(model));
|
regionController.addListener(() => _updateFormData(model));
|
||||||
challengeController.addListener(() => _updateFormData(model));
|
challengeController.addListener(() => _updateFormData(model));
|
||||||
occupationController.addListener(() => _updateFormData(model));
|
|
||||||
languageGoalController.addListener(() => _updateFormData(model));
|
languageGoalController.addListener(() => _updateFormData(model));
|
||||||
|
|
||||||
_updateFormData(model, forceValidate: _autoTextFieldValidation);
|
_updateFormData(model, forceValidate: _autoTextFieldValidation);
|
||||||
|
|
@ -101,7 +95,6 @@ mixin $OnboardingView {
|
||||||
fullNameController.addListener(() => _updateFormData(model));
|
fullNameController.addListener(() => _updateFormData(model));
|
||||||
regionController.addListener(() => _updateFormData(model));
|
regionController.addListener(() => _updateFormData(model));
|
||||||
challengeController.addListener(() => _updateFormData(model));
|
challengeController.addListener(() => _updateFormData(model));
|
||||||
occupationController.addListener(() => _updateFormData(model));
|
|
||||||
languageGoalController.addListener(() => _updateFormData(model));
|
languageGoalController.addListener(() => _updateFormData(model));
|
||||||
|
|
||||||
_updateFormData(model, forceValidate: _autoTextFieldValidation);
|
_updateFormData(model, forceValidate: _autoTextFieldValidation);
|
||||||
|
|
@ -116,7 +109,6 @@ mixin $OnboardingView {
|
||||||
FullNameValueKey: fullNameController.text,
|
FullNameValueKey: fullNameController.text,
|
||||||
RegionValueKey: regionController.text,
|
RegionValueKey: regionController.text,
|
||||||
ChallengeValueKey: challengeController.text,
|
ChallengeValueKey: challengeController.text,
|
||||||
OccupationValueKey: occupationController.text,
|
|
||||||
LanguageGoalValueKey: languageGoalController.text,
|
LanguageGoalValueKey: languageGoalController.text,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
@ -163,8 +155,6 @@ extension ValueProperties on FormStateHelper {
|
||||||
String? get fullNameValue => this.formValueMap[FullNameValueKey] as String?;
|
String? get fullNameValue => this.formValueMap[FullNameValueKey] as String?;
|
||||||
String? get regionValue => this.formValueMap[RegionValueKey] as String?;
|
String? get regionValue => this.formValueMap[RegionValueKey] as String?;
|
||||||
String? get challengeValue => this.formValueMap[ChallengeValueKey] as String?;
|
String? get challengeValue => this.formValueMap[ChallengeValueKey] as String?;
|
||||||
String? get occupationValue =>
|
|
||||||
this.formValueMap[OccupationValueKey] as String?;
|
|
||||||
String? get languageGoalValue =>
|
String? get languageGoalValue =>
|
||||||
this.formValueMap[LanguageGoalValueKey] as String?;
|
this.formValueMap[LanguageGoalValueKey] as String?;
|
||||||
|
|
||||||
|
|
@ -210,17 +200,6 @@ extension ValueProperties on FormStateHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
set occupationValue(String? value) {
|
|
||||||
this.setData(
|
|
||||||
this.formValueMap..addAll({OccupationValueKey: value}),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (_OnboardingViewTextEditingControllers.containsKey(OccupationValueKey)) {
|
|
||||||
_OnboardingViewTextEditingControllers[OccupationValueKey]?.text =
|
|
||||||
value ?? '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
set languageGoalValue(String? value) {
|
set languageGoalValue(String? value) {
|
||||||
this.setData(
|
this.setData(
|
||||||
this.formValueMap..addAll({LanguageGoalValueKey: value}),
|
this.formValueMap..addAll({LanguageGoalValueKey: value}),
|
||||||
|
|
@ -245,9 +224,6 @@ extension ValueProperties on FormStateHelper {
|
||||||
bool get hasChallenge =>
|
bool get hasChallenge =>
|
||||||
this.formValueMap.containsKey(ChallengeValueKey) &&
|
this.formValueMap.containsKey(ChallengeValueKey) &&
|
||||||
(challengeValue?.isNotEmpty ?? false);
|
(challengeValue?.isNotEmpty ?? false);
|
||||||
bool get hasOccupation =>
|
|
||||||
this.formValueMap.containsKey(OccupationValueKey) &&
|
|
||||||
(occupationValue?.isNotEmpty ?? false);
|
|
||||||
bool get hasLanguageGoal =>
|
bool get hasLanguageGoal =>
|
||||||
this.formValueMap.containsKey(LanguageGoalValueKey) &&
|
this.formValueMap.containsKey(LanguageGoalValueKey) &&
|
||||||
(languageGoalValue?.isNotEmpty ?? false);
|
(languageGoalValue?.isNotEmpty ?? false);
|
||||||
|
|
@ -260,8 +236,6 @@ extension ValueProperties on FormStateHelper {
|
||||||
this.fieldsValidationMessages[RegionValueKey]?.isNotEmpty ?? false;
|
this.fieldsValidationMessages[RegionValueKey]?.isNotEmpty ?? false;
|
||||||
bool get hasChallengeValidationMessage =>
|
bool get hasChallengeValidationMessage =>
|
||||||
this.fieldsValidationMessages[ChallengeValueKey]?.isNotEmpty ?? false;
|
this.fieldsValidationMessages[ChallengeValueKey]?.isNotEmpty ?? false;
|
||||||
bool get hasOccupationValidationMessage =>
|
|
||||||
this.fieldsValidationMessages[OccupationValueKey]?.isNotEmpty ?? false;
|
|
||||||
bool get hasLanguageGoalValidationMessage =>
|
bool get hasLanguageGoalValidationMessage =>
|
||||||
this.fieldsValidationMessages[LanguageGoalValueKey]?.isNotEmpty ?? false;
|
this.fieldsValidationMessages[LanguageGoalValueKey]?.isNotEmpty ?? false;
|
||||||
|
|
||||||
|
|
@ -273,8 +247,6 @@ extension ValueProperties on FormStateHelper {
|
||||||
this.fieldsValidationMessages[RegionValueKey];
|
this.fieldsValidationMessages[RegionValueKey];
|
||||||
String? get challengeValidationMessage =>
|
String? get challengeValidationMessage =>
|
||||||
this.fieldsValidationMessages[ChallengeValueKey];
|
this.fieldsValidationMessages[ChallengeValueKey];
|
||||||
String? get occupationValidationMessage =>
|
|
||||||
this.fieldsValidationMessages[OccupationValueKey];
|
|
||||||
String? get languageGoalValidationMessage =>
|
String? get languageGoalValidationMessage =>
|
||||||
this.fieldsValidationMessages[LanguageGoalValueKey];
|
this.fieldsValidationMessages[LanguageGoalValueKey];
|
||||||
}
|
}
|
||||||
|
|
@ -288,8 +260,6 @@ extension Methods on FormStateHelper {
|
||||||
this.fieldsValidationMessages[RegionValueKey] = validationMessage;
|
this.fieldsValidationMessages[RegionValueKey] = validationMessage;
|
||||||
void setChallengeValidationMessage(String? validationMessage) =>
|
void setChallengeValidationMessage(String? validationMessage) =>
|
||||||
this.fieldsValidationMessages[ChallengeValueKey] = validationMessage;
|
this.fieldsValidationMessages[ChallengeValueKey] = validationMessage;
|
||||||
void setOccupationValidationMessage(String? validationMessage) =>
|
|
||||||
this.fieldsValidationMessages[OccupationValueKey] = validationMessage;
|
|
||||||
void setLanguageGoalValidationMessage(String? validationMessage) =>
|
void setLanguageGoalValidationMessage(String? validationMessage) =>
|
||||||
this.fieldsValidationMessages[LanguageGoalValueKey] = validationMessage;
|
this.fieldsValidationMessages[LanguageGoalValueKey] = validationMessage;
|
||||||
|
|
||||||
|
|
@ -299,7 +269,6 @@ extension Methods on FormStateHelper {
|
||||||
fullNameValue = '';
|
fullNameValue = '';
|
||||||
regionValue = '';
|
regionValue = '';
|
||||||
challengeValue = '';
|
challengeValue = '';
|
||||||
occupationValue = '';
|
|
||||||
languageGoalValue = '';
|
languageGoalValue = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -310,7 +279,6 @@ extension Methods on FormStateHelper {
|
||||||
FullNameValueKey: getValidationMessage(FullNameValueKey),
|
FullNameValueKey: getValidationMessage(FullNameValueKey),
|
||||||
RegionValueKey: getValidationMessage(RegionValueKey),
|
RegionValueKey: getValidationMessage(RegionValueKey),
|
||||||
ChallengeValueKey: getValidationMessage(ChallengeValueKey),
|
ChallengeValueKey: getValidationMessage(ChallengeValueKey),
|
||||||
OccupationValueKey: getValidationMessage(OccupationValueKey),
|
|
||||||
LanguageGoalValueKey: getValidationMessage(LanguageGoalValueKey),
|
LanguageGoalValueKey: getValidationMessage(LanguageGoalValueKey),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -335,6 +303,5 @@ void updateValidationData(FormStateHelper model) =>
|
||||||
FullNameValueKey: getValidationMessage(FullNameValueKey),
|
FullNameValueKey: getValidationMessage(FullNameValueKey),
|
||||||
RegionValueKey: getValidationMessage(RegionValueKey),
|
RegionValueKey: getValidationMessage(RegionValueKey),
|
||||||
ChallengeValueKey: getValidationMessage(ChallengeValueKey),
|
ChallengeValueKey: getValidationMessage(ChallengeValueKey),
|
||||||
OccupationValueKey: getValidationMessage(OccupationValueKey),
|
|
||||||
LanguageGoalValueKey: getValidationMessage(LanguageGoalValueKey),
|
LanguageGoalValueKey: getValidationMessage(LanguageGoalValueKey),
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -91,9 +91,9 @@ class OnboardingViewModel extends ReactiveViewModel
|
||||||
Map<String, dynamic>? get selectedAgeGroup => _selectedAgeGroup;
|
Map<String, dynamic>? get selectedAgeGroup => _selectedAgeGroup;
|
||||||
|
|
||||||
// Occupation
|
// Occupation
|
||||||
bool _focusOccupation = false;
|
String _selectedOccupation = 'Students (High school & University)';
|
||||||
|
|
||||||
bool get focusOccupation => _focusOccupation;
|
String get selectedOccupation => _selectedOccupation;
|
||||||
|
|
||||||
// Country
|
// Country
|
||||||
String _selectedCountry = 'Ethiopia';
|
String _selectedCountry = 'Ethiopia';
|
||||||
|
|
@ -105,6 +105,14 @@ class OnboardingViewModel extends ReactiveViewModel
|
||||||
|
|
||||||
bool get focusRegion => _focusRegion;
|
bool get focusRegion => _focusRegion;
|
||||||
|
|
||||||
|
bool _dropdownRegion = true;
|
||||||
|
|
||||||
|
bool get dropdownRegion => _dropdownRegion;
|
||||||
|
|
||||||
|
String _selectedRegion = 'Addis Ababa';
|
||||||
|
|
||||||
|
String get selectedRegion => _selectedRegion;
|
||||||
|
|
||||||
// Learning goal
|
// Learning goal
|
||||||
String? _selectedLearningGoal;
|
String? _selectedLearningGoal;
|
||||||
|
|
||||||
|
|
@ -253,8 +261,18 @@ class OnboardingViewModel extends ReactiveViewModel
|
||||||
_selectedAgeGroup == value;
|
_selectedAgeGroup == value;
|
||||||
|
|
||||||
// Occupation
|
// Occupation
|
||||||
void setOccupationFocus() {
|
List<String> getOccupations() => [
|
||||||
_focusOccupation = true;
|
'Students (High school & University)',
|
||||||
|
'Job Seekers / Fresh Graduates',
|
||||||
|
'Working Professionals (Corporate/Office)',
|
||||||
|
'Government & NGO Workers',
|
||||||
|
'Entrepreneurs & Small Business Owners',
|
||||||
|
'Hospitality & Tourism Workers',
|
||||||
|
'Freelancers / Remote Workers (Digital Economy)'
|
||||||
|
];
|
||||||
|
|
||||||
|
void setSelectedOccupation(String value) {
|
||||||
|
_selectedOccupation = value;
|
||||||
rebuildUi();
|
rebuildUi();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -415,15 +433,50 @@ class OnboardingViewModel extends ReactiveViewModel
|
||||||
|
|
||||||
void setSelectedCountry(String value) {
|
void setSelectedCountry(String value) {
|
||||||
_selectedCountry = value;
|
_selectedCountry = value;
|
||||||
|
|
||||||
|
if (value == 'Ethiopia') {
|
||||||
|
_dropdownRegion = true;
|
||||||
|
_selectedRegion = 'Addis Ababa';
|
||||||
|
} else {
|
||||||
|
_dropdownRegion = false;
|
||||||
|
}
|
||||||
|
|
||||||
rebuildUi();
|
rebuildUi();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Region
|
// 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',
|
||||||
|
];
|
||||||
|
|
||||||
|
void setSelectedRegion(String value) {
|
||||||
|
_selectedRegion = value;
|
||||||
|
rebuildUi();
|
||||||
|
}
|
||||||
|
|
||||||
void setRegionFocus() {
|
void setRegionFocus() {
|
||||||
_focusRegion = true;
|
_focusRegion = true;
|
||||||
rebuildUi();
|
rebuildUi();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void unsetRegionFocus() {
|
||||||
|
_focusRegion = false;
|
||||||
|
rebuildUi();
|
||||||
|
}
|
||||||
|
|
||||||
// Learning goal
|
// Learning goal
|
||||||
void setSelectedLearningGoal(String value) {
|
void setSelectedLearningGoal(String value) {
|
||||||
_selectedLearningGoal = value;
|
_selectedLearningGoal = value;
|
||||||
|
|
@ -541,7 +594,7 @@ class OnboardingViewModel extends ReactiveViewModel
|
||||||
|
|
||||||
// Reset occupation form screen
|
// Reset occupation form screen
|
||||||
void resetOccupationFormScreen() {
|
void resetOccupationFormScreen() {
|
||||||
_focusOccupation = false;
|
_selectedOccupation = 'Students (High school & University)';
|
||||||
rebuildUi();
|
rebuildUi();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -549,6 +602,7 @@ class OnboardingViewModel extends ReactiveViewModel
|
||||||
void resetCountryRegionFormScreen() {
|
void resetCountryRegionFormScreen() {
|
||||||
_focusRegion = false;
|
_focusRegion = false;
|
||||||
_selectedCountry = 'Ethiopia';
|
_selectedCountry = 'Ethiopia';
|
||||||
|
_selectedRegion = 'Addis Ababa';
|
||||||
rebuildUi();
|
rebuildUi();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,16 @@ class CountryRegionFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
||||||
final TextEditingController regionController;
|
final TextEditingController regionController;
|
||||||
const CountryRegionFormScreen({super.key, required this.regionController});
|
const CountryRegionFormScreen({super.key, required this.regionController});
|
||||||
|
|
||||||
|
void _setSelectedCountry(
|
||||||
|
{String? value, required OnboardingViewModel viewModel}) {
|
||||||
|
viewModel.setSelectedCountry(value ?? 'Ethiopia');
|
||||||
|
|
||||||
|
if (viewModel.selectedCountry != 'Ethiopia') {
|
||||||
|
regionController.clear();
|
||||||
|
viewModel.unsetRegionFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void _pop(OnboardingViewModel viewModel) {
|
void _pop(OnboardingViewModel viewModel) {
|
||||||
viewModel.resetCountryRegionFormScreen();
|
viewModel.resetCountryRegionFormScreen();
|
||||||
viewModel.goBack();
|
viewModel.goBack();
|
||||||
|
|
@ -21,7 +31,9 @@ class CountryRegionFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
||||||
FocusManager.instance.primaryFocus?.unfocus();
|
FocusManager.instance.primaryFocus?.unfocus();
|
||||||
|
|
||||||
Map<String, dynamic> data = {
|
Map<String, dynamic> data = {
|
||||||
'region': regionController.text,
|
'region': viewModel.dropdownRegion
|
||||||
|
? viewModel.selectedRegion
|
||||||
|
: regionController.text,
|
||||||
'country': viewModel.selectedCountry,
|
'country': viewModel.selectedCountry,
|
||||||
};
|
};
|
||||||
viewModel.addUserData(data);
|
viewModel.addUserData(data);
|
||||||
|
|
@ -85,10 +97,14 @@ class CountryRegionFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildCountryDropDown(viewModel),
|
_buildCountryDropDown(viewModel),
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildRegionFormField(viewModel),
|
_buildRegionFormState(viewModel),
|
||||||
if (viewModel.hasRegionValidationMessage && viewModel.focusRegion)
|
if (viewModel.hasRegionValidationMessage &&
|
||||||
|
!viewModel.dropdownRegion &&
|
||||||
|
viewModel.focusRegion)
|
||||||
verticalSpaceTiny,
|
verticalSpaceTiny,
|
||||||
if (viewModel.hasRegionValidationMessage && viewModel.focusRegion)
|
if (viewModel.hasRegionValidationMessage &&
|
||||||
|
!viewModel.dropdownRegion &&
|
||||||
|
viewModel.focusRegion)
|
||||||
_buildRegionValidatorWrapper(viewModel)
|
_buildRegionValidatorWrapper(viewModel)
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -116,7 +132,22 @@ class CountryRegionFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
||||||
selectedItem: viewModel.selectedCountry,
|
selectedItem: viewModel.selectedCountry,
|
||||||
items: (value, props) => viewModel.getCountries(),
|
items: (value, props) => viewModel.getCountries(),
|
||||||
onChanged: (value) =>
|
onChanged: (value) =>
|
||||||
viewModel.setSelectedCountry(value ?? 'Ethiopia'));
|
_setSelectedCountry(value: value, viewModel: viewModel));
|
||||||
|
|
||||||
|
Widget _buildRegionFormState(OnboardingViewModel viewModel) =>
|
||||||
|
viewModel.dropdownRegion
|
||||||
|
? _buildRegionDropDown(viewModel)
|
||||||
|
: _buildRegionFormField(viewModel);
|
||||||
|
|
||||||
|
Widget _buildRegionDropDown(OnboardingViewModel viewModel) =>
|
||||||
|
CustomDropdownPicker(
|
||||||
|
hint: 'Select region',
|
||||||
|
icon: _buildSearchIcon(),
|
||||||
|
selectedItem: viewModel.selectedRegion,
|
||||||
|
items: (value, props) => viewModel.getRegions(),
|
||||||
|
onChanged: (value) =>
|
||||||
|
viewModel.setSelectedRegion(value ?? 'Addis Ababa'));
|
||||||
|
|
||||||
Widget _buildRegionFormField(OnboardingViewModel viewModel) => TextFormField(
|
Widget _buildRegionFormField(OnboardingViewModel viewModel) => TextFormField(
|
||||||
controller: regionController,
|
controller: regionController,
|
||||||
onTap: viewModel.setRegionFocus,
|
onTap: viewModel.setRegionFocus,
|
||||||
|
|
@ -152,9 +183,15 @@ class CountryRegionFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
||||||
text: 'Continue',
|
text: 'Continue',
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
foregroundColor: kcWhite,
|
foregroundColor: kcWhite,
|
||||||
onTap: regionController.text.isNotEmpty ? () => _next(viewModel) : null,
|
onTap: !viewModel.dropdownRegion
|
||||||
backgroundColor: regionController.text.isNotEmpty
|
? regionController.text.isNotEmpty
|
||||||
? kcPrimaryColor
|
? () => _next(viewModel)
|
||||||
: kcPrimaryColor.withOpacity(0.1),
|
: null
|
||||||
|
: () => _next(viewModel),
|
||||||
|
backgroundColor: !viewModel.dropdownRegion
|
||||||
|
? regionController.text.isNotEmpty
|
||||||
|
? kcPrimaryColor
|
||||||
|
: kcPrimaryColor.withOpacity(0.1)
|
||||||
|
: kcPrimaryColor,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,15 +6,13 @@ 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/views/onboarding/onboarding_viewmodel.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/large_app_bar.dart';
|
import 'package:yimaru_app/ui/widgets/large_app_bar.dart';
|
||||||
|
|
||||||
|
import '../../../widgets/custom_dropdown.dart';
|
||||||
import '../onboarding_view.form.dart';
|
import '../onboarding_view.form.dart';
|
||||||
|
|
||||||
class OccupationFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
class OccupationFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
||||||
final TextEditingController occupationController;
|
const OccupationFormScreen({super.key});
|
||||||
|
|
||||||
const OccupationFormScreen({super.key, required this.occupationController});
|
|
||||||
|
|
||||||
void _pop(OnboardingViewModel viewModel) {
|
void _pop(OnboardingViewModel viewModel) {
|
||||||
occupationController.clear();
|
|
||||||
viewModel.resetOccupationFormScreen();
|
viewModel.resetOccupationFormScreen();
|
||||||
viewModel.goBack();
|
viewModel.goBack();
|
||||||
}
|
}
|
||||||
|
|
@ -22,7 +20,7 @@ class OccupationFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
||||||
Future<void> _next(OnboardingViewModel viewModel) async {
|
Future<void> _next(OnboardingViewModel viewModel) async {
|
||||||
FocusManager.instance.primaryFocus?.unfocus();
|
FocusManager.instance.primaryFocus?.unfocus();
|
||||||
|
|
||||||
Map<String, dynamic> data = {'occupation': occupationController.text};
|
Map<String, dynamic> data = {'occupation': viewModel.selectedOccupation};
|
||||||
viewModel.addUserData(data);
|
viewModel.addUserData(data);
|
||||||
|
|
||||||
viewModel.next();
|
viewModel.next();
|
||||||
|
|
@ -82,13 +80,7 @@ class OccupationFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
||||||
verticalSpaceSmall,
|
verticalSpaceSmall,
|
||||||
_buildSubtitle(),
|
_buildSubtitle(),
|
||||||
verticalSpaceLarge,
|
verticalSpaceLarge,
|
||||||
_buildOccupationFormField(viewModel),
|
_buildOccupationDropdown(viewModel),
|
||||||
if (viewModel.hasOccupationValidationMessage &&
|
|
||||||
viewModel.focusOccupation)
|
|
||||||
verticalSpaceTiny,
|
|
||||||
if (viewModel.hasOccupationValidationMessage &&
|
|
||||||
viewModel.focusOccupation)
|
|
||||||
_buildOccupationValidatorWrapper(viewModel)
|
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildAppBar(OnboardingViewModel viewModel) => LargeAppBar(
|
Widget _buildAppBar(OnboardingViewModel viewModel) => LargeAppBar(
|
||||||
|
|
@ -108,24 +100,17 @@ class OccupationFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
||||||
style: style14MG400,
|
style: style14MG400,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildOccupationFormField(OnboardingViewModel viewModel) =>
|
Widget _buildOccupationDropdown(OnboardingViewModel viewModel) =>
|
||||||
TextFormField(
|
CustomDropdownPicker(
|
||||||
controller: occupationController,
|
hint: 'Select occupation',
|
||||||
onTap: viewModel.setOccupationFocus,
|
icon: _buildSearchIcon(),
|
||||||
decoration: inputDecoration(
|
selectedItem: viewModel.selectedOccupation,
|
||||||
hint: 'Enter Your Occupation',
|
items: (value, props) => viewModel.getOccupations(),
|
||||||
focus: viewModel.focusOccupation,
|
onChanged: (value) => viewModel.setSelectedOccupation(
|
||||||
filled: occupationController.text.isNotEmpty),
|
value ?? 'Students (High school & University)'));
|
||||||
);
|
Icon _buildSearchIcon() => const Icon(
|
||||||
|
Icons.search,
|
||||||
Widget _buildOccupationValidatorWrapper(OnboardingViewModel viewModel) =>
|
color: kcPrimaryColor,
|
||||||
viewModel.hasOccupationValidationMessage
|
|
||||||
? _buildOccupationValidator(viewModel)
|
|
||||||
: Container();
|
|
||||||
|
|
||||||
Widget _buildOccupationValidator(OnboardingViewModel viewModel) => Text(
|
|
||||||
viewModel.occupationValidationMessage!,
|
|
||||||
style: style12R700,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildContinueButtonWrapper(OnboardingViewModel viewModel) => Padding(
|
Widget _buildContinueButtonWrapper(OnboardingViewModel viewModel) => Padding(
|
||||||
|
|
@ -135,15 +120,10 @@ class OccupationFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
||||||
|
|
||||||
Widget _buildContinueButton(OnboardingViewModel viewModel) =>
|
Widget _buildContinueButton(OnboardingViewModel viewModel) =>
|
||||||
CustomElevatedButton(
|
CustomElevatedButton(
|
||||||
height: 55,
|
height: 55,
|
||||||
text: 'Continue',
|
text: 'Continue',
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
foregroundColor: kcWhite,
|
foregroundColor: kcWhite,
|
||||||
onTap: occupationController.text.isNotEmpty
|
onTap: () => _next(viewModel),
|
||||||
? () => _next(viewModel)
|
backgroundColor: kcPrimaryColor);
|
||||||
: null,
|
|
||||||
backgroundColor: occupationController.text.isNotEmpty
|
|
||||||
? kcPrimaryColor
|
|
||||||
: kcPrimaryColor.withOpacity(0.1),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ import 'package:stacked/stacked.dart';
|
||||||
import 'package:stacked_services/stacked_services.dart';
|
import 'package:stacked_services/stacked_services.dart';
|
||||||
import 'package:yimaru_app/app/app.router.dart';
|
import 'package:yimaru_app/app/app.router.dart';
|
||||||
import 'package:yimaru_app/services/image_picker_service.dart';
|
import 'package:yimaru_app/services/image_picker_service.dart';
|
||||||
|
import 'package:yimaru_app/services/phone_caller_service.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/app_constants.dart';
|
||||||
import 'package:yimaru_app/ui/common/enmus.dart';
|
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||||
|
|
||||||
import '../../../app/app.locator.dart';
|
import '../../../app/app.locator.dart';
|
||||||
|
|
@ -10,6 +12,7 @@ import '../../../services/api_service.dart';
|
||||||
import '../../../services/authentication_service.dart';
|
import '../../../services/authentication_service.dart';
|
||||||
import '../../../services/google_auth_service.dart';
|
import '../../../services/google_auth_service.dart';
|
||||||
import '../../../services/status_checker_service.dart';
|
import '../../../services/status_checker_service.dart';
|
||||||
|
import '../../../services/url_launcher_service.dart';
|
||||||
import '../../common/app_colors.dart';
|
import '../../common/app_colors.dart';
|
||||||
|
|
||||||
class ProfileViewModel extends ReactiveViewModel {
|
class ProfileViewModel extends ReactiveViewModel {
|
||||||
|
|
@ -23,6 +26,10 @@ class ProfileViewModel extends ReactiveViewModel {
|
||||||
|
|
||||||
final _googleAuthService = locator<GoogleAuthService>();
|
final _googleAuthService = locator<GoogleAuthService>();
|
||||||
|
|
||||||
|
final _phoneCallerService = locator<PhoneCallerService>();
|
||||||
|
|
||||||
|
final _urlLauncherService = locator<UrlLauncherService>();
|
||||||
|
|
||||||
final _imagePickerService = locator<ImagePickerService>();
|
final _imagePickerService = locator<ImagePickerService>();
|
||||||
|
|
||||||
final _authenticationService = locator<AuthenticationService>();
|
final _authenticationService = locator<AuthenticationService>();
|
||||||
|
|
@ -61,6 +68,13 @@ class ProfileViewModel extends ReactiveViewModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Launch telegram
|
||||||
|
Future<void> launchTelegram() =>
|
||||||
|
_urlLauncherService.launchUri(kTelegramSupport);
|
||||||
|
|
||||||
|
// Call support
|
||||||
|
Future<void> callSupport() => _phoneCallerService.call(kPhoneSupport);
|
||||||
|
|
||||||
// Dialog
|
// Dialog
|
||||||
Future<bool?> showAbortDialog() async {
|
Future<bool?> showAbortDialog() async {
|
||||||
DialogResponse? response = await _dialogService.showDialog(
|
DialogResponse? response = await _dialogService.showDialog(
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ import 'profile_detail_view.form.dart';
|
||||||
name: 'phoneNumber', validator: FormValidator.validatePhoneNumberForm),
|
name: 'phoneNumber', validator: FormValidator.validatePhoneNumberForm),
|
||||||
FormTextField(name: 'lastName', validator: FormValidator.validateForm),
|
FormTextField(name: 'lastName', validator: FormValidator.validateForm),
|
||||||
FormTextField(name: 'firstName', validator: FormValidator.validateForm),
|
FormTextField(name: 'firstName', validator: FormValidator.validateForm),
|
||||||
FormTextField(name: 'occupation', validator: FormValidator.validateForm),
|
|
||||||
])
|
])
|
||||||
class ProfileDetailView extends StackedView<ProfileDetailViewModel>
|
class ProfileDetailView extends StackedView<ProfileDetailViewModel>
|
||||||
with $ProfileDetailView {
|
with $ProfileDetailView {
|
||||||
|
|
@ -34,7 +33,9 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
|
||||||
|
|
||||||
Future<void> _update(ProfileDetailViewModel viewModel) async {
|
Future<void> _update(ProfileDetailViewModel viewModel) async {
|
||||||
Map<String, dynamic> data = {
|
Map<String, dynamic> data = {
|
||||||
'region': regionController.text,
|
'region': viewModel.dropdownRegion
|
||||||
|
? viewModel.selectedRegion
|
||||||
|
: regionController.text,
|
||||||
'gender': viewModel.selectedGender,
|
'gender': viewModel.selectedGender,
|
||||||
'last_name': lastNameController.text,
|
'last_name': lastNameController.text,
|
||||||
'country': viewModel.selectedCountry,
|
'country': viewModel.selectedCountry,
|
||||||
|
|
@ -65,13 +66,23 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
|
||||||
content: _buildImagePicker(context: context, viewModel: viewModel),
|
content: _buildImagePicker(context: context, viewModel: viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
void _checkRegion(ProfileDetailViewModel viewModel){
|
||||||
|
if(viewModel.checkRegion(viewModel.user?.region ?? '')){
|
||||||
|
viewModel.setSelectedRegion(viewModel.user?.region ?? 'Addis Ababa');
|
||||||
|
}else{
|
||||||
|
regionController.text = viewModel.user?.region ?? '';
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void _onModelReady(ProfileDetailViewModel viewModel) {
|
void _onModelReady(ProfileDetailViewModel viewModel) {
|
||||||
phoneNumberController.text = '251900000000';
|
phoneNumberController.text = '251900000000';
|
||||||
emailController.text = viewModel.user?.email ?? '';
|
emailController.text = viewModel.user?.email ?? '';
|
||||||
regionController.text = viewModel.user?.region ?? '';
|
|
||||||
lastNameController.text = viewModel.user?.lastName ?? '';
|
lastNameController.text = viewModel.user?.lastName ?? '';
|
||||||
firstNameController.text = viewModel.user?.firstName ?? '';
|
firstNameController.text = viewModel.user?.firstName ?? '';
|
||||||
occupationController.text = viewModel.user?.occupation ?? '';
|
occupationController.text = viewModel.user?.occupation ?? '';
|
||||||
|
_checkRegion(viewModel);
|
||||||
viewModel.clearUserData();
|
viewModel.clearUserData();
|
||||||
viewModel.setGender(viewModel.user?.gender ?? '');
|
viewModel.setGender(viewModel.user?.gender ?? '');
|
||||||
viewModel.setSelectedCountry(viewModel.user?.country ?? 'Ethiopia');
|
viewModel.setSelectedCountry(viewModel.user?.country ?? 'Ethiopia');
|
||||||
|
|
@ -535,11 +546,13 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
|
||||||
[
|
[
|
||||||
_buildRegionFormFieldLabel(),
|
_buildRegionFormFieldLabel(),
|
||||||
verticalSpaceSmall,
|
verticalSpaceSmall,
|
||||||
_buildRegionFormField(viewModel),
|
_buildRegionFormState(viewModel),
|
||||||
if (viewModel.hasRegionValidationMessage && viewModel.focusRegion)
|
if (viewModel.hasRegionValidationMessage &&
|
||||||
verticalSpaceTiny,
|
!viewModel.dropdownRegion &&
|
||||||
if (viewModel.hasRegionValidationMessage && viewModel.focusRegion)
|
viewModel.focusRegion) verticalSpaceTiny,
|
||||||
_buildRegionValidatorWrapper(viewModel),
|
if (viewModel.hasRegionValidationMessage &&
|
||||||
|
!viewModel.dropdownRegion &&
|
||||||
|
viewModel.focusRegion) _buildRegionValidatorWrapper(viewModel),
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildRegionFormFieldLabel() => CustomFormLabel(
|
Widget _buildRegionFormFieldLabel() => CustomFormLabel(
|
||||||
|
|
@ -547,15 +560,28 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
|
||||||
style: style16DG600,
|
style: style16DG600,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildRegionFormField(ProfileDetailViewModel viewModel) =>
|
Widget _buildRegionFormState(ProfileDetailViewModel viewModel) =>
|
||||||
TextFormField(
|
viewModel.dropdownRegion
|
||||||
controller: regionController,
|
? _buildRegionDropDown(viewModel)
|
||||||
onTap: viewModel.setRegionFocus,
|
: _buildRegionFormField(viewModel);
|
||||||
decoration: inputDecoration(
|
|
||||||
hint: 'Enter Your City',
|
Widget _buildRegionDropDown(ProfileDetailViewModel viewModel) =>
|
||||||
focus: viewModel.focusRegion,
|
CustomDropdownPicker(
|
||||||
filled: regionController.text.isNotEmpty),
|
hint: 'Select region',
|
||||||
);
|
icon: _buildSearchIcon(),
|
||||||
|
selectedItem: viewModel.selectedRegion,
|
||||||
|
items: (value, props) => viewModel.getRegions(),
|
||||||
|
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) =>
|
Widget _buildRegionValidatorWrapper(ProfileDetailViewModel viewModel) =>
|
||||||
viewModel.hasRegionValidationMessage
|
viewModel.hasRegionValidationMessage
|
||||||
|
|
@ -563,9 +589,10 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
|
||||||
: Container();
|
: Container();
|
||||||
|
|
||||||
Widget _buildRegionValidator(ProfileDetailViewModel viewModel) => Text(
|
Widget _buildRegionValidator(ProfileDetailViewModel viewModel) => Text(
|
||||||
viewModel.regionValidationMessage!,
|
viewModel.regionValidationMessage!,
|
||||||
style: style12R700,
|
style: style12R700,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
Widget _buildOccupationDropdownWrapper(ProfileDetailViewModel viewModel) =>
|
Widget _buildOccupationDropdownWrapper(ProfileDetailViewModel viewModel) =>
|
||||||
Column(
|
Column(
|
||||||
|
|
@ -580,13 +607,8 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
|
||||||
[
|
[
|
||||||
_buildOccupationDropdownLabel(),
|
_buildOccupationDropdownLabel(),
|
||||||
verticalSpaceSmall,
|
verticalSpaceSmall,
|
||||||
_buildOccupationFormField(viewModel),
|
_buildOccupationDropdown(viewModel)
|
||||||
if (viewModel.hasOccupationValidationMessage &&
|
|
||||||
viewModel.focusOccupation)
|
|
||||||
verticalSpaceTiny,
|
|
||||||
if (viewModel.hasOccupationValidationMessage &&
|
|
||||||
viewModel.focusOccupation)
|
|
||||||
_buildOccupationValidatorWrapper(viewModel)
|
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildOccupationDropdownLabel() => CustomFormLabel(
|
Widget _buildOccupationDropdownLabel() => CustomFormLabel(
|
||||||
|
|
@ -594,29 +616,18 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
|
||||||
style: style16DG600,
|
style: style16DG600,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildOccupationFormField(ProfileDetailViewModel viewModel) =>
|
Widget _buildOccupationDropdown(ProfileDetailViewModel viewModel) =>
|
||||||
TextFormField(
|
CustomDropdownPicker(
|
||||||
controller: occupationController,
|
hint: 'Select occupation',
|
||||||
onTap: viewModel.setOccupationFocus,
|
icon: _buildSearchIcon(),
|
||||||
decoration: inputDecoration(
|
selectedItem: viewModel.selectedOccupation,
|
||||||
hint: 'Enter Your Occupation',
|
items: (value, props) => viewModel.getOccupations(),
|
||||||
focus: viewModel.focusOccupation,
|
onChanged: (value) => viewModel.setSelectedOccupation(
|
||||||
filled: occupationController.text.isNotEmpty),
|
value ?? 'Students (High school & University)'));
|
||||||
);
|
Icon _buildSearchIcon() => const Icon(
|
||||||
|
Icons.search,
|
||||||
Widget _buildOccupationValidatorWrapper(ProfileDetailViewModel viewModel) =>
|
color: kcPrimaryColor,
|
||||||
viewModel.hasOccupationValidationMessage
|
);
|
||||||
? _buildOccupationValidator(viewModel)
|
|
||||||
: Container();
|
|
||||||
|
|
||||||
Widget _buildOccupationValidator(ProfileDetailViewModel viewModel) => Text(
|
|
||||||
viewModel.occupationValidationMessage!,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: Colors.red,
|
|
||||||
fontWeight: FontWeight.w700,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
Widget _buildLowerColumn(ProfileDetailViewModel viewModel) => Column(
|
Widget _buildLowerColumn(ProfileDetailViewModel viewModel) => Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: _buildLowerColumnChildren(viewModel),
|
children: _buildLowerColumnChildren(viewModel),
|
||||||
|
|
|
||||||
|
|
@ -57,21 +57,31 @@ class ProfileDetailViewModel extends ReactiveViewModel
|
||||||
|
|
||||||
bool get focusEmail => _focusEmail;
|
bool get focusEmail => _focusEmail;
|
||||||
|
|
||||||
|
|
||||||
|
// Occupation
|
||||||
|
String _selectedOccupation = 'Students (High school & University)';
|
||||||
|
|
||||||
|
String get selectedOccupation => _selectedOccupation;
|
||||||
|
|
||||||
|
|
||||||
// Country
|
// Country
|
||||||
String _selectedCountry = 'Ethiopia';
|
String _selectedCountry = 'Ethiopia';
|
||||||
|
|
||||||
String get selectedCountry => _selectedCountry;
|
String get selectedCountry => _selectedCountry;
|
||||||
|
|
||||||
// Occupation
|
|
||||||
bool _focusOccupation = false;
|
|
||||||
|
|
||||||
bool get focusOccupation => _focusOccupation;
|
|
||||||
|
|
||||||
// Region
|
// Region
|
||||||
bool _focusRegion = false;
|
bool _focusRegion = false;
|
||||||
|
|
||||||
bool get focusRegion => _focusRegion;
|
bool get focusRegion => _focusRegion;
|
||||||
|
|
||||||
|
bool _dropdownRegion = true;
|
||||||
|
|
||||||
|
bool get dropdownRegion => _dropdownRegion;
|
||||||
|
|
||||||
|
String _selectedRegion = 'Addis Ababa';
|
||||||
|
|
||||||
|
String get selectedRegion => _selectedRegion;
|
||||||
|
|
||||||
// User data
|
// User data
|
||||||
final Map<String, dynamic> _userData = {};
|
final Map<String, dynamic> _userData = {};
|
||||||
|
|
||||||
|
|
@ -107,180 +117,229 @@ class ProfileDetailViewModel extends ReactiveViewModel
|
||||||
rebuildUi();
|
rebuildUi();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Country
|
|
||||||
|
|
||||||
|
// 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)'
|
||||||
|
];
|
||||||
|
|
||||||
|
void setSelectedOccupation(String value) {
|
||||||
|
_selectedOccupation = value;
|
||||||
|
rebuildUi();
|
||||||
|
}
|
||||||
|
|
||||||
// Country
|
// Country
|
||||||
List<String> getCountries() => [
|
List<String> getCountries() => [
|
||||||
"Afghanistan",
|
"Afghanistan",
|
||||||
"Albania",
|
"Albania",
|
||||||
"Algeria",
|
"Algeria",
|
||||||
"Andorra",
|
"Andorra",
|
||||||
"Angola",
|
"Angola",
|
||||||
"Argentina",
|
"Argentina",
|
||||||
"Armenia",
|
"Armenia",
|
||||||
"Australia",
|
"Australia",
|
||||||
"Austria",
|
"Austria",
|
||||||
"Azerbaijan",
|
"Azerbaijan",
|
||||||
"Bahrain",
|
"Bahrain",
|
||||||
"Bangladesh",
|
"Bangladesh",
|
||||||
"Belarus",
|
"Belarus",
|
||||||
"Belgium",
|
"Belgium",
|
||||||
"Belize",
|
"Belize",
|
||||||
"Benin",
|
"Benin",
|
||||||
"Bhutan",
|
"Bhutan",
|
||||||
"Bolivia",
|
"Bolivia",
|
||||||
"Bosnia and Herzegovina",
|
"Bosnia and Herzegovina",
|
||||||
"Botswana",
|
"Botswana",
|
||||||
"Brazil",
|
"Brazil",
|
||||||
"Brunei",
|
"Brunei",
|
||||||
"Bulgaria",
|
"Bulgaria",
|
||||||
"Burkina Faso",
|
"Burkina Faso",
|
||||||
"Burundi",
|
"Burundi",
|
||||||
"Cambodia",
|
"Cambodia",
|
||||||
"Cameroon",
|
"Cameroon",
|
||||||
"Canada",
|
"Canada",
|
||||||
"Chad",
|
"Chad",
|
||||||
"Chile",
|
"Chile",
|
||||||
"China",
|
"China",
|
||||||
"Colombia",
|
"Colombia",
|
||||||
"Comoros",
|
"Comoros",
|
||||||
"Congo",
|
"Congo",
|
||||||
"Costa Rica",
|
"Costa Rica",
|
||||||
"Croatia",
|
"Croatia",
|
||||||
"Cuba",
|
"Cuba",
|
||||||
"Cyprus",
|
"Cyprus",
|
||||||
"Czech Republic",
|
"Czech Republic",
|
||||||
"Denmark",
|
"Denmark",
|
||||||
"Djibouti",
|
"Djibouti",
|
||||||
"Dominican Republic",
|
"Dominican Republic",
|
||||||
"Ecuador",
|
"Ecuador",
|
||||||
"Egypt",
|
"Egypt",
|
||||||
"El Salvador",
|
"El Salvador",
|
||||||
"Eritrea",
|
"Eritrea",
|
||||||
"Estonia",
|
"Estonia",
|
||||||
"Eswatini",
|
"Eswatini",
|
||||||
"Ethiopia",
|
"Ethiopia",
|
||||||
"Finland",
|
"Finland",
|
||||||
"France",
|
"France",
|
||||||
"Gabon",
|
"Gabon",
|
||||||
"Gambia",
|
"Gambia",
|
||||||
"Georgia",
|
"Georgia",
|
||||||
"Germany",
|
"Germany",
|
||||||
"Ghana",
|
"Ghana",
|
||||||
"Greece",
|
"Greece",
|
||||||
"Guatemala",
|
"Guatemala",
|
||||||
"Guinea",
|
"Guinea",
|
||||||
"Haiti",
|
"Haiti",
|
||||||
"Honduras",
|
"Honduras",
|
||||||
"Hungary",
|
"Hungary",
|
||||||
"Iceland",
|
"Iceland",
|
||||||
"India",
|
"India",
|
||||||
"Indonesia",
|
"Indonesia",
|
||||||
"Iran",
|
"Iran",
|
||||||
"Iraq",
|
"Iraq",
|
||||||
"Ireland",
|
"Ireland",
|
||||||
"Israel",
|
"Israel",
|
||||||
"Italy",
|
"Italy",
|
||||||
"Jamaica",
|
"Jamaica",
|
||||||
"Japan",
|
"Japan",
|
||||||
"Jordan",
|
"Jordan",
|
||||||
"Kazakhstan",
|
"Kazakhstan",
|
||||||
"Kenya",
|
"Kenya",
|
||||||
"Kuwait",
|
"Kuwait",
|
||||||
"Kyrgyzstan",
|
"Kyrgyzstan",
|
||||||
"Laos",
|
"Laos",
|
||||||
"Latvia",
|
"Latvia",
|
||||||
"Lebanon",
|
"Lebanon",
|
||||||
"Liberia",
|
"Liberia",
|
||||||
"Libya",
|
"Libya",
|
||||||
"Lithuania",
|
"Lithuania",
|
||||||
"Luxembourg",
|
"Luxembourg",
|
||||||
"Madagascar",
|
"Madagascar",
|
||||||
"Malawi",
|
"Malawi",
|
||||||
"Malaysia",
|
"Malaysia",
|
||||||
"Maldives",
|
"Maldives",
|
||||||
"Mali",
|
"Mali",
|
||||||
"Malta",
|
"Malta",
|
||||||
"Mexico",
|
"Mexico",
|
||||||
"Moldova",
|
"Moldova",
|
||||||
"Monaco",
|
"Monaco",
|
||||||
"Mongolia",
|
"Mongolia",
|
||||||
"Morocco",
|
"Morocco",
|
||||||
"Mozambique",
|
"Mozambique",
|
||||||
"Myanmar",
|
"Myanmar",
|
||||||
"Namibia",
|
"Namibia",
|
||||||
"Nepal",
|
"Nepal",
|
||||||
"Netherlands",
|
"Netherlands",
|
||||||
"New Zealand",
|
"New Zealand",
|
||||||
"Nicaragua",
|
"Nicaragua",
|
||||||
"Niger",
|
"Niger",
|
||||||
"Nigeria",
|
"Nigeria",
|
||||||
"North Korea",
|
"North Korea",
|
||||||
"Norway",
|
"Norway",
|
||||||
"Oman",
|
"Oman",
|
||||||
"Pakistan",
|
"Pakistan",
|
||||||
"Panama",
|
"Panama",
|
||||||
"Paraguay",
|
"Paraguay",
|
||||||
"Peru",
|
"Peru",
|
||||||
"Philippines",
|
"Philippines",
|
||||||
"Poland",
|
"Poland",
|
||||||
"Portugal",
|
"Portugal",
|
||||||
"Qatar",
|
"Qatar",
|
||||||
"Romania",
|
"Romania",
|
||||||
"Russia",
|
"Russia",
|
||||||
"Rwanda",
|
"Rwanda",
|
||||||
"Saudi Arabia",
|
"Saudi Arabia",
|
||||||
"Senegal",
|
"Senegal",
|
||||||
"Serbia",
|
"Serbia",
|
||||||
"Singapore",
|
"Singapore",
|
||||||
"Slovakia",
|
"Slovakia",
|
||||||
"Slovenia",
|
"Slovenia",
|
||||||
"Somalia",
|
"Somalia",
|
||||||
"South Africa",
|
"South Africa",
|
||||||
"South Korea",
|
"South Korea",
|
||||||
"Spain",
|
"Spain",
|
||||||
"Sri Lanka",
|
"Sri Lanka",
|
||||||
"Sudan",
|
"Sudan",
|
||||||
"Sweden",
|
"Sweden",
|
||||||
"Switzerland",
|
"Switzerland",
|
||||||
"Syria",
|
"Syria",
|
||||||
"Taiwan",
|
"Taiwan",
|
||||||
"Tajikistan",
|
"Tajikistan",
|
||||||
"Tanzania",
|
"Tanzania",
|
||||||
"Thailand",
|
"Thailand",
|
||||||
"Tunisia",
|
"Tunisia",
|
||||||
"Turkey",
|
"Turkey",
|
||||||
"Uganda",
|
"Uganda",
|
||||||
"Ukraine",
|
"Ukraine",
|
||||||
"United Arab Emirates",
|
"United Arab Emirates",
|
||||||
"United Kingdom",
|
"United Kingdom",
|
||||||
"United States",
|
"United States",
|
||||||
"Uruguay",
|
"Uruguay",
|
||||||
"Uzbekistan",
|
"Uzbekistan",
|
||||||
"Venezuela",
|
"Venezuela",
|
||||||
"Vietnam",
|
"Vietnam",
|
||||||
"Yemen",
|
"Yemen",
|
||||||
"Zambia",
|
"Zambia",
|
||||||
"Zimbabwe"
|
"Zimbabwe"
|
||||||
];
|
];
|
||||||
|
|
||||||
void setSelectedCountry(String value) {
|
void setSelectedCountry(String value) {
|
||||||
_selectedCountry = value;
|
_selectedCountry = value;
|
||||||
|
|
||||||
rebuildUi();
|
if (value == 'Ethiopia') {
|
||||||
}
|
_dropdownRegion = true;
|
||||||
|
_selectedRegion = 'Addis Ababa';
|
||||||
|
} else {
|
||||||
|
_dropdownRegion = false;
|
||||||
|
}
|
||||||
|
|
||||||
// Occupation
|
|
||||||
void setOccupationFocus() {
|
|
||||||
_focusOccupation = true;
|
|
||||||
rebuildUi();
|
rebuildUi();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Region
|
// 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',
|
||||||
|
];
|
||||||
|
|
||||||
|
bool checkRegion(String value){
|
||||||
|
return getRegions().contains(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSelectedRegion(String value) {
|
||||||
|
_selectedRegion = value;
|
||||||
|
rebuildUi();
|
||||||
|
}
|
||||||
|
|
||||||
void setRegionFocus() {
|
void setRegionFocus() {
|
||||||
_focusRegion = true;
|
_focusRegion = true;
|
||||||
rebuildUi();
|
rebuildUi();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void unsetRegionFocus() {
|
||||||
|
_focusRegion = false;
|
||||||
|
rebuildUi();
|
||||||
|
}
|
||||||
|
|
||||||
// User data
|
// User data
|
||||||
void addUserData(Map<String, dynamic> data) {
|
void addUserData(Map<String, dynamic> data) {
|
||||||
_userData.addAll(data);
|
_userData.addAll(data);
|
||||||
|
|
|
||||||
|
|
@ -49,14 +49,6 @@ class RegisterViewModel extends ReactiveViewModel
|
||||||
|
|
||||||
bool get length => _length;
|
bool get length => _length;
|
||||||
|
|
||||||
bool _number = false;
|
|
||||||
|
|
||||||
bool get number => _number;
|
|
||||||
|
|
||||||
bool _specialChar = false;
|
|
||||||
|
|
||||||
bool get specialChar => _specialChar;
|
|
||||||
|
|
||||||
bool _focusPassword = false;
|
bool _focusPassword = false;
|
||||||
|
|
||||||
bool get focusPassword => _focusPassword;
|
bool get focusPassword => _focusPassword;
|
||||||
|
|
@ -138,17 +130,6 @@ class RegisterViewModel extends ReactiveViewModel
|
||||||
_length = false;
|
_length = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (RegExp(r'\d').hasMatch(password)) {
|
|
||||||
_number = true;
|
|
||||||
} else {
|
|
||||||
_number = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (RegExp(r'[!@#$%^&*(),.?":{}|<>]').hasMatch(password)) {
|
|
||||||
_specialChar = true;
|
|
||||||
} else {
|
|
||||||
_specialChar = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (password == confirmPassword) {
|
if (password == confirmPassword) {
|
||||||
_passwordMatch = true;
|
_passwordMatch = true;
|
||||||
|
|
@ -160,13 +141,9 @@ class RegisterViewModel extends ReactiveViewModel
|
||||||
|
|
||||||
double validationProgress() {
|
double validationProgress() {
|
||||||
int completed = 0;
|
int completed = 0;
|
||||||
|
|
||||||
if (_length) completed++;
|
if (_length) completed++;
|
||||||
if (_number) completed++;
|
|
||||||
if (_specialChar) completed++;
|
|
||||||
if (_passwordMatch) completed++;
|
if (_passwordMatch) completed++;
|
||||||
|
return completed / 2; // returns 0.0 → 1.0
|
||||||
return completed / 4; // returns 0.0 → 1.0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setObscurePassword() {
|
void setObscurePassword() {
|
||||||
|
|
@ -238,8 +215,6 @@ class RegisterViewModel extends ReactiveViewModel
|
||||||
void resetCreatePasswordScreen() {
|
void resetCreatePasswordScreen() {
|
||||||
_agree = false;
|
_agree = false;
|
||||||
_length = false;
|
_length = false;
|
||||||
_number = false;
|
|
||||||
_specialChar = false;
|
|
||||||
_passwordMatch = false;
|
_passwordMatch = false;
|
||||||
_focusPassword = false;
|
_focusPassword = false;
|
||||||
_focusConfirmPassword = false;
|
_focusConfirmPassword = false;
|
||||||
|
|
|
||||||
|
|
@ -114,8 +114,6 @@ class CreatePasswordScreen extends ViewModelWidget<RegisterViewModel> {
|
||||||
_buildLinearProgressIndicator(viewModel),
|
_buildLinearProgressIndicator(viewModel),
|
||||||
verticalSpaceSmall,
|
verticalSpaceSmall,
|
||||||
_buildCharLengthValidator(viewModel),
|
_buildCharLengthValidator(viewModel),
|
||||||
_buildNumberValidator(viewModel),
|
|
||||||
_buildSymbolValidator(viewModel),
|
|
||||||
_buildPasswordMatchValidator(viewModel),
|
_buildPasswordMatchValidator(viewModel),
|
||||||
_buildCheckBox(viewModel),
|
_buildCheckBox(viewModel),
|
||||||
verticalSpaceSmall,
|
verticalSpaceSmall,
|
||||||
|
|
@ -215,15 +213,6 @@ class CreatePasswordScreen extends ViewModelWidget<RegisterViewModel> {
|
||||||
backgroundColor: viewModel.length ? kcPrimaryColor : kcLightGrey,
|
backgroundColor: viewModel.length ? kcPrimaryColor : kcLightGrey,
|
||||||
label: '8 characters minimum');
|
label: '8 characters minimum');
|
||||||
|
|
||||||
Widget _buildNumberValidator(RegisterViewModel viewModel) =>
|
|
||||||
ValidatorListTile(
|
|
||||||
backgroundColor: viewModel.number ? kcPrimaryColor : kcLightGrey,
|
|
||||||
label: 'a number');
|
|
||||||
|
|
||||||
Widget _buildSymbolValidator(RegisterViewModel viewModel) =>
|
|
||||||
ValidatorListTile(
|
|
||||||
backgroundColor: viewModel.specialChar ? kcPrimaryColor : kcLightGrey,
|
|
||||||
label: 'one symbol minimum');
|
|
||||||
|
|
||||||
Widget _buildPasswordMatchValidator(RegisterViewModel viewModel) =>
|
Widget _buildPasswordMatchValidator(RegisterViewModel viewModel) =>
|
||||||
ValidatorListTile(
|
ValidatorListTile(
|
||||||
|
|
@ -265,20 +254,14 @@ class CreatePasswordScreen extends ViewModelWidget<RegisterViewModel> {
|
||||||
foregroundColor: kcWhite,
|
foregroundColor: kcWhite,
|
||||||
onTap: passwordController.text.isNotEmpty &&
|
onTap: passwordController.text.isNotEmpty &&
|
||||||
confirmPasswordController.text.isNotEmpty &&
|
confirmPasswordController.text.isNotEmpty &&
|
||||||
viewModel.number &&
|
|
||||||
viewModel.length &&
|
viewModel.length &&
|
||||||
viewModel.specialChar &&
|
|
||||||
viewModel.specialChar &&
|
|
||||||
viewModel.passwordMatch &&
|
viewModel.passwordMatch &&
|
||||||
viewModel.agree
|
viewModel.agree
|
||||||
? () async => await _signUp(viewModel)
|
? () async => await _signUp(viewModel)
|
||||||
: null,
|
: null,
|
||||||
backgroundColor: passwordController.text.isNotEmpty &&
|
backgroundColor: passwordController.text.isNotEmpty &&
|
||||||
confirmPasswordController.text.isNotEmpty &&
|
confirmPasswordController.text.isNotEmpty &&
|
||||||
viewModel.number &&
|
|
||||||
viewModel.length &&
|
viewModel.length &&
|
||||||
viewModel.specialChar &&
|
|
||||||
viewModel.specialChar &&
|
|
||||||
viewModel.passwordMatch &&
|
viewModel.passwordMatch &&
|
||||||
viewModel.agree
|
viewModel.agree
|
||||||
? kcPrimaryColor
|
? kcPrimaryColor
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,11 @@ class WelcomeViewModel extends BaseViewModel {
|
||||||
// Dependency Injection
|
// Dependency Injection
|
||||||
final _navigationService = locator<NavigationService>();
|
final _navigationService = locator<NavigationService>();
|
||||||
|
|
||||||
final _statusChecker = locator<StatusCheckerService>();
|
|
||||||
|
|
||||||
final _authenticationService = locator<AuthenticationService>();
|
final _authenticationService = locator<AuthenticationService>();
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
Future<void> navigateToLogin() async =>
|
Future<void> navigateToLogin() async =>
|
||||||
await _navigationService.navigateToLoginView();
|
await _navigationService.replaceWithLoginView();
|
||||||
|
|
||||||
// Remote api call
|
// Remote api call
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ class LearningProgressCard extends StatelessWidget {
|
||||||
color: kcPrimaryColor,
|
color: kcPrimaryColor,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildTitle() => Text(
|
Widget _buildTitle() => Text(
|
||||||
'Learn English',
|
'Learn English',
|
||||||
style: style16DG600,
|
style: style16DG600,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
#include <flutter_inappwebview_linux/flutter_inappwebview_linux_plugin.h>
|
#include <flutter_inappwebview_linux/flutter_inappwebview_linux_plugin.h>
|
||||||
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
|
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
|
||||||
#include <record_linux/record_linux_plugin.h>
|
#include <record_linux/record_linux_plugin.h>
|
||||||
|
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||||
|
|
||||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||||
g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar =
|
g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar =
|
||||||
|
|
@ -28,4 +29,7 @@ void fl_register_plugins(FlPluginRegistry* registry) {
|
||||||
g_autoptr(FlPluginRegistrar) record_linux_registrar =
|
g_autoptr(FlPluginRegistrar) record_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "RecordLinuxPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "RecordLinuxPlugin");
|
||||||
record_linux_plugin_register_with_registrar(record_linux_registrar);
|
record_linux_plugin_register_with_registrar(record_linux_registrar);
|
||||||
|
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
||||||
|
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
flutter_inappwebview_linux
|
flutter_inappwebview_linux
|
||||||
flutter_secure_storage_linux
|
flutter_secure_storage_linux
|
||||||
record_linux
|
record_linux
|
||||||
|
url_launcher_linux
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import google_sign_in_ios
|
||||||
import package_info_plus
|
import package_info_plus
|
||||||
import record_macos
|
import record_macos
|
||||||
import sqflite_darwin
|
import sqflite_darwin
|
||||||
|
import url_launcher_macos
|
||||||
import video_player_avfoundation
|
import video_player_avfoundation
|
||||||
import wakelock_plus
|
import wakelock_plus
|
||||||
|
|
||||||
|
|
@ -35,6 +36,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
||||||
RecordMacOsPlugin.register(with: registry.registrar(forPlugin: "RecordMacOsPlugin"))
|
RecordMacOsPlugin.register(with: registry.registrar(forPlugin: "RecordMacOsPlugin"))
|
||||||
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||||
|
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||||
FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin"))
|
FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin"))
|
||||||
WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin"))
|
WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin"))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
72
pubspec.lock
72
pubspec.lock
|
|
@ -662,6 +662,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.7"
|
version: "2.4.7"
|
||||||
|
flutter_phone_direct_caller:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_phone_direct_caller
|
||||||
|
sha256: "8f166b12391572ce5872feeac3f4b47f455ee314ef221d98ba160c296ae2fad3"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.1"
|
||||||
flutter_plugin_android_lifecycle:
|
flutter_plugin_android_lifecycle:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -1693,6 +1701,70 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.0"
|
version: "0.7.0"
|
||||||
|
url_launcher:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: url_launcher
|
||||||
|
sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.3.2"
|
||||||
|
url_launcher_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_android
|
||||||
|
sha256: "3bb000251e55d4a209aa0e2e563309dc9bb2befea2295fd0cec1f51760aac572"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.3.29"
|
||||||
|
url_launcher_ios:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_ios
|
||||||
|
sha256: "580fe5dfb51671ae38191d316e027f6b76272b026370708c2d898799750a02b0"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.4.1"
|
||||||
|
url_launcher_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_linux
|
||||||
|
sha256: d5e14138b3bc193a0f63c10a53c94b91d399df0512b1f29b94a043db7482384a
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.2.2"
|
||||||
|
url_launcher_macos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_macos
|
||||||
|
sha256: "368adf46f71ad3c21b8f06614adb38346f193f3a59ba8fe9a2fd74133070ba18"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.2.5"
|
||||||
|
url_launcher_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_platform_interface
|
||||||
|
sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.2"
|
||||||
|
url_launcher_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_web
|
||||||
|
sha256: "85c81589622fbc87c1c683aaea164d3604a7777495a79d91e39ffcdec39ddb34"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.3"
|
||||||
|
url_launcher_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_windows
|
||||||
|
sha256: "712c70ab1b99744ff066053cbe3e80c73332b38d46e5e945c98689b2e66fc15f"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.5"
|
||||||
uuid:
|
uuid:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ dependencies:
|
||||||
flutter_html: ^3.0.0
|
flutter_html: ^3.0.0
|
||||||
email_validator: any
|
email_validator: any
|
||||||
audioplayers: ^6.6.0
|
audioplayers: ^6.6.0
|
||||||
|
url_launcher: ^6.3.2
|
||||||
video_player: ^2.10.1
|
video_player: ^2.10.1
|
||||||
firebase_core: ^4.4.0
|
firebase_core: ^4.4.0
|
||||||
in_app_update: ^4.2.5
|
in_app_update: ^4.2.5
|
||||||
|
|
@ -49,6 +50,7 @@ dependencies:
|
||||||
flutter_timer_countdown: ^1.0.7
|
flutter_timer_countdown: ^1.0.7
|
||||||
flutter_carousel_widget: ^3.1.0
|
flutter_carousel_widget: ^3.1.0
|
||||||
flutter_inappwebview: ^6.2.0-beta.3
|
flutter_inappwebview: ^6.2.0-beta.3
|
||||||
|
flutter_phone_direct_caller: ^2.2.1
|
||||||
flutter_local_notifications: ^20.1.0
|
flutter_local_notifications: ^20.1.0
|
||||||
internet_connection_checker_plus: ^2.9.1+2
|
internet_connection_checker_plus: ^2.9.1+2
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ import 'package:yimaru_app/services/audio_player_service.dart';
|
||||||
import 'package:yimaru_app/services/voice_recorder_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/services/in_app_update_service.dart';
|
||||||
import 'package:yimaru_app/services/vimeo_service.dart';
|
import 'package:yimaru_app/services/vimeo_service.dart';
|
||||||
|
import 'package:yimaru_app/services/url_launcher_service.dart';
|
||||||
|
import 'package:yimaru_app/services/phone_caller_service.dart';
|
||||||
// @stacked-import
|
// @stacked-import
|
||||||
|
|
||||||
import 'test_helpers.mocks.dart';
|
import 'test_helpers.mocks.dart';
|
||||||
|
|
@ -47,6 +49,9 @@ import 'test_helpers.mocks.dart';
|
||||||
MockSpec<InAppUpdateService>(onMissingStub: OnMissingStub.returnDefault),
|
MockSpec<InAppUpdateService>(onMissingStub: OnMissingStub.returnDefault),
|
||||||
MockSpec<VimeoService>(onMissingStub: OnMissingStub.returnDefault),
|
MockSpec<VimeoService>(onMissingStub: OnMissingStub.returnDefault),
|
||||||
MockSpec<VimeoService>(onMissingStub: OnMissingStub.returnDefault),
|
MockSpec<VimeoService>(onMissingStub: OnMissingStub.returnDefault),
|
||||||
|
MockSpec<UrlLauncherService>(onMissingStub: OnMissingStub.returnDefault),
|
||||||
|
MockSpec<UrlLauncherService>(onMissingStub: OnMissingStub.returnDefault),
|
||||||
|
MockSpec<PhoneCallerService>(onMissingStub: OnMissingStub.returnDefault),
|
||||||
// @stacked-mock-spec
|
// @stacked-mock-spec
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
@ -71,6 +76,9 @@ void registerServices() {
|
||||||
getAndRegisterInAppUpdateService();
|
getAndRegisterInAppUpdateService();
|
||||||
getAndRegisterVimeoService();
|
getAndRegisterVimeoService();
|
||||||
getAndRegisterVimeoService();
|
getAndRegisterVimeoService();
|
||||||
|
getAndRegisterUrlLauncherService();
|
||||||
|
getAndRegisterUrlLauncherService();
|
||||||
|
getAndRegisterPhoneCallerService();
|
||||||
// @stacked-mock-register
|
// @stacked-mock-register
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -239,6 +247,20 @@ MockVimeoService getAndRegisterVimeoService() {
|
||||||
locator.registerSingleton<VimeoService>(service);
|
locator.registerSingleton<VimeoService>(service);
|
||||||
return service;
|
return service;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MockUrlLauncherService getAndRegisterUrlLauncherService() {
|
||||||
|
_removeRegistrationIfExists<UrlLauncherService>();
|
||||||
|
final service = MockUrlLauncherService();
|
||||||
|
locator.registerSingleton<UrlLauncherService>(service);
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
MockPhoneCallerService getAndRegisterPhoneCallerService() {
|
||||||
|
_removeRegistrationIfExists<PhoneCallerService>();
|
||||||
|
final service = MockPhoneCallerService();
|
||||||
|
locator.registerSingleton<PhoneCallerService>(service);
|
||||||
|
return service;
|
||||||
|
}
|
||||||
// @stacked-mock-create
|
// @stacked-mock-create
|
||||||
|
|
||||||
void _removeRegistrationIfExists<T extends Object>() {
|
void _removeRegistrationIfExists<T extends Object>() {
|
||||||
|
|
|
||||||
11
test/services/phone_caller_service_test.dart
Normal file
11
test/services/phone_caller_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('PhoneCallerServiceTest -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
});
|
||||||
|
}
|
||||||
11
test/services/url_launcher_service_test.dart
Normal file
11
test/services/url_launcher_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('UrlLauncherServiceTest -', () {
|
||||||
|
setUp(() => registerServices());
|
||||||
|
tearDown(() => locator.reset());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
||||||
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
#include <permission_handler_windows/permission_handler_windows_plugin.h>
|
||||||
#include <record_windows/record_windows_plugin_c_api.h>
|
#include <record_windows/record_windows_plugin_c_api.h>
|
||||||
|
#include <url_launcher_windows/url_launcher_windows.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
AudioplayersWindowsPluginRegisterWithRegistrar(
|
AudioplayersWindowsPluginRegisterWithRegistrar(
|
||||||
|
|
@ -35,4 +36,6 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
|
registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
|
||||||
RecordWindowsPluginCApiRegisterWithRegistrar(
|
RecordWindowsPluginCApiRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("RecordWindowsPluginCApi"));
|
registry->GetRegistrarForPlugin("RecordWindowsPluginCApi"));
|
||||||
|
UrlLauncherWindowsRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
flutter_secure_storage_windows
|
flutter_secure_storage_windows
|
||||||
permission_handler_windows
|
permission_handler_windows
|
||||||
record_windows
|
record_windows
|
||||||
|
url_launcher_windows
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user