Merge branch 'release/0.1.14'
- fix: Apply UAT comments
This commit is contained in:
commit
a2a26c456d
BIN
assets/images/pattern.png
Normal file
BIN
assets/images/pattern.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
|
|
@ -510,8 +510,8 @@ class StackedRouter extends _i1.RouterBase {
|
||||||
_i22.LearnLessonDetailView: (data) {
|
_i22.LearnLessonDetailView: (data) {
|
||||||
final args = data.getArgs<LearnLessonDetailViewArguments>(nullOk: false);
|
final args = data.getArgs<LearnLessonDetailViewArguments>(nullOk: false);
|
||||||
return _i37.MaterialPageRoute<dynamic>(
|
return _i37.MaterialPageRoute<dynamic>(
|
||||||
builder: (context) =>
|
builder: (context) => _i22.LearnLessonDetailView(
|
||||||
_i22.LearnLessonDetailView(key: args.key, lesson: args.lesson),
|
key: args.key, lesson: args.lesson, hasPractice: args.hasPractice),
|
||||||
settings: data,
|
settings: data,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -519,7 +519,13 @@ class StackedRouter extends _i1.RouterBase {
|
||||||
final args = data.getArgs<LearnPracticeViewArguments>(nullOk: false);
|
final args = data.getArgs<LearnPracticeViewArguments>(nullOk: false);
|
||||||
return _i37.MaterialPageRoute<dynamic>(
|
return _i37.MaterialPageRoute<dynamic>(
|
||||||
builder: (context) => _i23.LearnPracticeView(
|
builder: (context) => _i23.LearnPracticeView(
|
||||||
key: args.key, id: args.id, practice: args.practice),
|
key: args.key,
|
||||||
|
level: args.level,
|
||||||
|
id: args.id,
|
||||||
|
label: args.label,
|
||||||
|
title: args.title,
|
||||||
|
practice: args.practice,
|
||||||
|
subtitle: args.subtitle),
|
||||||
settings: data,
|
settings: data,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -1098,56 +1104,85 @@ class LearnLessonDetailViewArguments {
|
||||||
const LearnLessonDetailViewArguments({
|
const LearnLessonDetailViewArguments({
|
||||||
this.key,
|
this.key,
|
||||||
required this.lesson,
|
required this.lesson,
|
||||||
|
required this.hasPractice,
|
||||||
});
|
});
|
||||||
|
|
||||||
final _i37.Key? key;
|
final _i37.Key? key;
|
||||||
|
|
||||||
final _i40.LearnLesson lesson;
|
final _i40.LearnLesson lesson;
|
||||||
|
|
||||||
|
final bool hasPractice;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return '{"key": "$key", "lesson": "$lesson"}';
|
return '{"key": "$key", "lesson": "$lesson", "hasPractice": "$hasPractice"}';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant LearnLessonDetailViewArguments other) {
|
bool operator ==(covariant LearnLessonDetailViewArguments other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) return true;
|
||||||
return other.key == key && other.lesson == lesson;
|
return other.key == key &&
|
||||||
|
other.lesson == lesson &&
|
||||||
|
other.hasPractice == hasPractice;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode {
|
int get hashCode {
|
||||||
return key.hashCode ^ lesson.hashCode;
|
return key.hashCode ^ lesson.hashCode ^ hasPractice.hashCode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class LearnPracticeViewArguments {
|
class LearnPracticeViewArguments {
|
||||||
const LearnPracticeViewArguments({
|
const LearnPracticeViewArguments({
|
||||||
this.key,
|
this.key,
|
||||||
|
this.level,
|
||||||
required this.id,
|
required this.id,
|
||||||
|
required this.label,
|
||||||
|
required this.title,
|
||||||
required this.practice,
|
required this.practice,
|
||||||
|
required this.subtitle,
|
||||||
});
|
});
|
||||||
|
|
||||||
final _i37.Key? key;
|
final _i37.Key? key;
|
||||||
|
|
||||||
|
final String? level;
|
||||||
|
|
||||||
final int id;
|
final int id;
|
||||||
|
|
||||||
|
final String label;
|
||||||
|
|
||||||
|
final String title;
|
||||||
|
|
||||||
final _i41.LearnPractices practice;
|
final _i41.LearnPractices practice;
|
||||||
|
|
||||||
|
final String subtitle;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return '{"key": "$key", "id": "$id", "practice": "$practice"}';
|
return '{"key": "$key", "level": "$level", "id": "$id", "label": "$label", "title": "$title", "practice": "$practice", "subtitle": "$subtitle"}';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(covariant LearnPracticeViewArguments other) {
|
bool operator ==(covariant LearnPracticeViewArguments other) {
|
||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) return true;
|
||||||
return other.key == key && other.id == id && other.practice == practice;
|
return other.key == key &&
|
||||||
|
other.level == level &&
|
||||||
|
other.id == id &&
|
||||||
|
other.label == label &&
|
||||||
|
other.title == title &&
|
||||||
|
other.practice == practice &&
|
||||||
|
other.subtitle == subtitle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode {
|
int get hashCode {
|
||||||
return key.hashCode ^ id.hashCode ^ practice.hashCode;
|
return key.hashCode ^
|
||||||
|
level.hashCode ^
|
||||||
|
id.hashCode ^
|
||||||
|
label.hashCode ^
|
||||||
|
title.hashCode ^
|
||||||
|
practice.hashCode ^
|
||||||
|
subtitle.hashCode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1817,6 +1852,7 @@ extension NavigatorStateExtension on _i46.NavigationService {
|
||||||
Future<dynamic> navigateToLearnLessonDetailView({
|
Future<dynamic> navigateToLearnLessonDetailView({
|
||||||
_i37.Key? key,
|
_i37.Key? key,
|
||||||
required _i40.LearnLesson lesson,
|
required _i40.LearnLesson lesson,
|
||||||
|
required bool hasPractice,
|
||||||
int? routerId,
|
int? routerId,
|
||||||
bool preventDuplicates = true,
|
bool preventDuplicates = true,
|
||||||
Map<String, String>? parameters,
|
Map<String, String>? parameters,
|
||||||
|
|
@ -1824,7 +1860,8 @@ extension NavigatorStateExtension on _i46.NavigationService {
|
||||||
transition,
|
transition,
|
||||||
}) async {
|
}) async {
|
||||||
return navigateTo<dynamic>(Routes.learnLessonDetailView,
|
return navigateTo<dynamic>(Routes.learnLessonDetailView,
|
||||||
arguments: LearnLessonDetailViewArguments(key: key, lesson: lesson),
|
arguments: LearnLessonDetailViewArguments(
|
||||||
|
key: key, lesson: lesson, hasPractice: hasPractice),
|
||||||
id: routerId,
|
id: routerId,
|
||||||
preventDuplicates: preventDuplicates,
|
preventDuplicates: preventDuplicates,
|
||||||
parameters: parameters,
|
parameters: parameters,
|
||||||
|
|
@ -1833,8 +1870,12 @@ extension NavigatorStateExtension on _i46.NavigationService {
|
||||||
|
|
||||||
Future<dynamic> navigateToLearnPracticeView({
|
Future<dynamic> navigateToLearnPracticeView({
|
||||||
_i37.Key? key,
|
_i37.Key? key,
|
||||||
|
String? level,
|
||||||
required int id,
|
required int id,
|
||||||
|
required String label,
|
||||||
|
required String title,
|
||||||
required _i41.LearnPractices practice,
|
required _i41.LearnPractices practice,
|
||||||
|
required String subtitle,
|
||||||
int? routerId,
|
int? routerId,
|
||||||
bool preventDuplicates = true,
|
bool preventDuplicates = true,
|
||||||
Map<String, String>? parameters,
|
Map<String, String>? parameters,
|
||||||
|
|
@ -1842,8 +1883,14 @@ extension NavigatorStateExtension on _i46.NavigationService {
|
||||||
transition,
|
transition,
|
||||||
}) async {
|
}) async {
|
||||||
return navigateTo<dynamic>(Routes.learnPracticeView,
|
return navigateTo<dynamic>(Routes.learnPracticeView,
|
||||||
arguments:
|
arguments: LearnPracticeViewArguments(
|
||||||
LearnPracticeViewArguments(key: key, id: id, practice: practice),
|
key: key,
|
||||||
|
level: level,
|
||||||
|
id: id,
|
||||||
|
label: label,
|
||||||
|
title: title,
|
||||||
|
practice: practice,
|
||||||
|
subtitle: subtitle),
|
||||||
id: routerId,
|
id: routerId,
|
||||||
preventDuplicates: preventDuplicates,
|
preventDuplicates: preventDuplicates,
|
||||||
parameters: parameters,
|
parameters: parameters,
|
||||||
|
|
@ -2395,6 +2442,7 @@ extension NavigatorStateExtension on _i46.NavigationService {
|
||||||
Future<dynamic> replaceWithLearnLessonDetailView({
|
Future<dynamic> replaceWithLearnLessonDetailView({
|
||||||
_i37.Key? key,
|
_i37.Key? key,
|
||||||
required _i40.LearnLesson lesson,
|
required _i40.LearnLesson lesson,
|
||||||
|
required bool hasPractice,
|
||||||
int? routerId,
|
int? routerId,
|
||||||
bool preventDuplicates = true,
|
bool preventDuplicates = true,
|
||||||
Map<String, String>? parameters,
|
Map<String, String>? parameters,
|
||||||
|
|
@ -2402,7 +2450,8 @@ extension NavigatorStateExtension on _i46.NavigationService {
|
||||||
transition,
|
transition,
|
||||||
}) async {
|
}) async {
|
||||||
return replaceWith<dynamic>(Routes.learnLessonDetailView,
|
return replaceWith<dynamic>(Routes.learnLessonDetailView,
|
||||||
arguments: LearnLessonDetailViewArguments(key: key, lesson: lesson),
|
arguments: LearnLessonDetailViewArguments(
|
||||||
|
key: key, lesson: lesson, hasPractice: hasPractice),
|
||||||
id: routerId,
|
id: routerId,
|
||||||
preventDuplicates: preventDuplicates,
|
preventDuplicates: preventDuplicates,
|
||||||
parameters: parameters,
|
parameters: parameters,
|
||||||
|
|
@ -2411,8 +2460,12 @@ extension NavigatorStateExtension on _i46.NavigationService {
|
||||||
|
|
||||||
Future<dynamic> replaceWithLearnPracticeView({
|
Future<dynamic> replaceWithLearnPracticeView({
|
||||||
_i37.Key? key,
|
_i37.Key? key,
|
||||||
|
String? level,
|
||||||
required int id,
|
required int id,
|
||||||
|
required String label,
|
||||||
|
required String title,
|
||||||
required _i41.LearnPractices practice,
|
required _i41.LearnPractices practice,
|
||||||
|
required String subtitle,
|
||||||
int? routerId,
|
int? routerId,
|
||||||
bool preventDuplicates = true,
|
bool preventDuplicates = true,
|
||||||
Map<String, String>? parameters,
|
Map<String, String>? parameters,
|
||||||
|
|
@ -2420,8 +2473,14 @@ extension NavigatorStateExtension on _i46.NavigationService {
|
||||||
transition,
|
transition,
|
||||||
}) async {
|
}) async {
|
||||||
return replaceWith<dynamic>(Routes.learnPracticeView,
|
return replaceWith<dynamic>(Routes.learnPracticeView,
|
||||||
arguments:
|
arguments: LearnPracticeViewArguments(
|
||||||
LearnPracticeViewArguments(key: key, id: id, practice: practice),
|
key: key,
|
||||||
|
level: level,
|
||||||
|
id: id,
|
||||||
|
label: label,
|
||||||
|
title: title,
|
||||||
|
practice: practice,
|
||||||
|
subtitle: subtitle),
|
||||||
id: routerId,
|
id: routerId,
|
||||||
preventDuplicates: preventDuplicates,
|
preventDuplicates: preventDuplicates,
|
||||||
parameters: parameters,
|
parameters: parameters,
|
||||||
|
|
|
||||||
|
|
@ -158,6 +158,7 @@ class DioService {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
print('REFRESH EXCEPTION: ${e.toString()}');
|
||||||
await _authenticationService.logout();
|
await _authenticationService.logout();
|
||||||
await _navigationService.replaceWithLoginView();
|
await _navigationService.replaceWithLoginView();
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,5 @@ import 'package:flutter_phone_direct_caller/flutter_phone_direct_caller.dart';
|
||||||
|
|
||||||
class PhoneCallerService {
|
class PhoneCallerService {
|
||||||
Future<void> call(String phone) async =>
|
Future<void> call(String phone) async =>
|
||||||
await FlutterPhoneDirectCaller.callNumber(phone);
|
await FlutterPhoneDirectCaller.callNumber(phone);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,4 +88,4 @@ String kServerClientId =
|
||||||
// Other
|
// Other
|
||||||
String kPhoneSupport = '+251946396655';
|
String kPhoneSupport = '+251946396655';
|
||||||
|
|
||||||
String kTelegramSupport = '@yimaruacademy2026';
|
String kTelegramSupport = '@yimaruacademy2026';
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
const String ksHomeBottomSheetTitle = 'Build Great Apps!';
|
const String ksHomeBottomSheetTitle = 'Build Great Apps!';
|
||||||
|
|
||||||
const String ksSuggestion =
|
const String ksSuggestion =
|
||||||
|
|
|
||||||
|
|
@ -238,6 +238,12 @@ TextStyle style25P600 = const TextStyle(
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
TextStyle style40P900 = const TextStyle(
|
||||||
|
fontSize: 40,
|
||||||
|
color: kcPrimaryColor,
|
||||||
|
fontWeight: FontWeight.w900,
|
||||||
|
);
|
||||||
|
|
||||||
TextStyle style25DG600 = const TextStyle(
|
TextStyle style25DG600 = const TextStyle(
|
||||||
fontSize: 25,
|
fontSize: 25,
|
||||||
color: kcDarkGrey,
|
color: kcDarkGrey,
|
||||||
|
|
@ -309,16 +315,14 @@ TextStyle style14LG400 = const TextStyle(
|
||||||
TextStyle style14MG400 = const TextStyle(
|
TextStyle style14MG400 = const TextStyle(
|
||||||
color: kcMediumGrey,
|
color: kcMediumGrey,
|
||||||
);
|
);
|
||||||
|
TextStyle style14DG400 = const TextStyle(color: kcDarkGrey);
|
||||||
|
|
||||||
TextStyle style14DG500 =
|
TextStyle style14DG500 =
|
||||||
const TextStyle(color: kcDarkGrey, fontWeight: FontWeight.w500);
|
const TextStyle(color: kcDarkGrey, fontWeight: FontWeight.w500);
|
||||||
|
|
||||||
TextStyle style18MG500 = const TextStyle(
|
TextStyle style18MG500 = const TextStyle(
|
||||||
fontSize: 18, color: kcMediumGrey, fontWeight: FontWeight.w500);
|
fontSize: 18, color: kcMediumGrey, fontWeight: FontWeight.w500);
|
||||||
|
|
||||||
TextStyle style14DG400 = const TextStyle(
|
|
||||||
color: kcDarkGrey,
|
|
||||||
);
|
|
||||||
|
|
||||||
TextStyle style14DG600 = const TextStyle(
|
TextStyle style14DG600 = const TextStyle(
|
||||||
color: kcDarkGrey,
|
color: kcDarkGrey,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
|
|
|
||||||
|
|
@ -53,62 +53,61 @@ class AssessmentQuestionsScreen extends ViewModelWidget<AssessmentViewModel> {
|
||||||
controller: viewModel.pageController,
|
controller: viewModel.pageController,
|
||||||
itemCount: viewModel.assessmentQuestions.length,
|
itemCount: viewModel.assessmentQuestions.length,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
itemBuilder: (cotext, index) =>
|
itemBuilder: (cotext, index) => _buildBodyScroller(viewModel),
|
||||||
_buildBodyScroller( viewModel),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildBodyScroller(
|
Widget _buildBodyScroller(AssessmentViewModel viewModel) =>
|
||||||
AssessmentViewModel viewModel) =>
|
|
||||||
SingleChildScrollView(
|
SingleChildScrollView(
|
||||||
child: _buildBody( viewModel),
|
child: _buildBody(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildBody(
|
Widget _buildBody(AssessmentViewModel viewModel) => Column(
|
||||||
AssessmentViewModel viewModel) =>
|
|
||||||
Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: _buildBodyChildren( viewModel),
|
children: _buildBodyChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildBodyChildren(
|
List<Widget> _buildBodyChildren(AssessmentViewModel viewModel) => [
|
||||||
AssessmentViewModel viewModel) =>[
|
|
||||||
|
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildTitleState( viewModel),
|
_buildTitleState(viewModel),
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildAnswersState( viewModel),
|
_buildAnswersState(viewModel),
|
||||||
_buildContinueButtonWrapper( viewModel)
|
_buildContinueButtonWrapper(viewModel)
|
||||||
];
|
];
|
||||||
Widget _buildTitleState( AssessmentViewModel viewModel)=> viewModel.currentQuestionIndex ==
|
Widget _buildTitleState(AssessmentViewModel viewModel) =>
|
||||||
viewModel.assessmentQuestions.length ?Container(): _buildTitle(viewModel);
|
viewModel.currentQuestionIndex == viewModel.assessmentQuestions.length
|
||||||
Widget _buildTitle(
|
? Container()
|
||||||
AssessmentViewModel viewModel) =>
|
: _buildTitle(viewModel);
|
||||||
Text(
|
Widget _buildTitle(AssessmentViewModel viewModel) => Text(
|
||||||
'Q${viewModel.currentQuestionIndex + 1}. ${viewModel.assessmentQuestions[viewModel.currentQuestionIndex].questionText} ',
|
'Q${viewModel.currentQuestionIndex + 1}. ${viewModel.assessmentQuestions[viewModel.currentQuestionIndex].questionText} ',
|
||||||
style: style16DG600,
|
style: style16DG600,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAnswersState( AssessmentViewModel viewModel)=> viewModel.currentQuestionIndex ==
|
Widget _buildAnswersState(AssessmentViewModel viewModel) =>
|
||||||
viewModel.assessmentQuestions.length ?Container(): _buildAnswers(viewModel);
|
viewModel.currentQuestionIndex == viewModel.assessmentQuestions.length
|
||||||
|
? Container()
|
||||||
|
: _buildAnswers(viewModel);
|
||||||
|
|
||||||
Widget _buildAnswers(
|
Widget _buildAnswers(AssessmentViewModel viewModel) => ListView.builder(
|
||||||
AssessmentViewModel viewModel) =>
|
|
||||||
ListView.builder(
|
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
itemCount: viewModel.assessmentQuestions[viewModel.currentQuestionIndex].options?.length,
|
itemCount: viewModel.assessmentQuestions[viewModel.currentQuestionIndex]
|
||||||
|
.options?.length,
|
||||||
itemBuilder: (context, inner) => _buildAnswer(
|
itemBuilder: (context, inner) => _buildAnswer(
|
||||||
onTap: () => viewModel.setSelectedAnswer(
|
onTap: () => viewModel.setSelectedAnswer(
|
||||||
question: viewModel.currentQuestionIndex + 1,
|
question: viewModel.currentQuestionIndex + 1,
|
||||||
option: viewModel.assessmentQuestions[viewModel.currentQuestionIndex].options?[inner]),
|
option: viewModel
|
||||||
title:
|
.assessmentQuestions[viewModel.currentQuestionIndex]
|
||||||
viewModel.assessmentQuestions[viewModel.currentQuestionIndex].options?[inner].optionText ??
|
.options?[inner]),
|
||||||
'',
|
title: viewModel.assessmentQuestions[viewModel.currentQuestionIndex]
|
||||||
|
.options?[inner].optionText ??
|
||||||
|
'',
|
||||||
selected: viewModel.isSelectedAnswer(
|
selected: viewModel.isSelectedAnswer(
|
||||||
question: viewModel.currentQuestionIndex + 1,
|
question: viewModel.currentQuestionIndex + 1,
|
||||||
answer: viewModel
|
answer: viewModel
|
||||||
.assessmentQuestions[viewModel.currentQuestionIndex ].options?[inner].optionText ??
|
.assessmentQuestions[viewModel.currentQuestionIndex]
|
||||||
|
.options?[inner]
|
||||||
|
.optionText ??
|
||||||
''),
|
''),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -123,30 +122,27 @@ class AssessmentQuestionsScreen extends ViewModelWidget<AssessmentViewModel> {
|
||||||
selected: selected,
|
selected: selected,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildContinueButtonWrapper(
|
Widget _buildContinueButtonWrapper(AssessmentViewModel viewModel) => Padding(
|
||||||
AssessmentViewModel viewModel) =>
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 50),
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
child: _buildContinueButton( viewModel),
|
child: _buildContinueButton(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildContinueButton(
|
Widget _buildContinueButton(AssessmentViewModel viewModel) =>
|
||||||
AssessmentViewModel viewModel) =>
|
|
||||||
CustomElevatedButton(
|
CustomElevatedButton(
|
||||||
height: 55,
|
height: 55,
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
foregroundColor: kcWhite,
|
foregroundColor: kcWhite,
|
||||||
text: viewModel.currentQuestionIndex ==
|
text: viewModel.currentQuestionIndex ==
|
||||||
viewModel.assessmentQuestions.length - 1
|
viewModel.assessmentQuestions.length - 1
|
||||||
? 'Finish Level'
|
? 'Finish Level'
|
||||||
: 'Continue',
|
: 'Continue',
|
||||||
backgroundColor:
|
backgroundColor: viewModel.selectedAnswers
|
||||||
viewModel.selectedAnswers.containsKey('${viewModel.currentQuestionIndex + 1}')
|
.containsKey('${viewModel.currentQuestionIndex + 1}')
|
||||||
? kcPrimaryColor
|
? kcPrimaryColor
|
||||||
: kcPrimaryColor.withOpacity(0.1),
|
: kcPrimaryColor.withOpacity(0.1),
|
||||||
onTap: viewModel.selectedAnswers.containsKey('${viewModel.currentQuestionIndex + 1}')
|
onTap: viewModel.selectedAnswers
|
||||||
|
.containsKey('${viewModel.currentQuestionIndex + 1}')
|
||||||
? () => viewModel.nextQuestion()
|
? () => viewModel.nextQuestion()
|
||||||
: null,
|
: null,
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/app_constants.dart';
|
||||||
|
|
||||||
import '../../common/app_colors.dart';
|
import '../../common/app_colors.dart';
|
||||||
import '../../common/ui_helpers.dart';
|
import '../../common/ui_helpers.dart';
|
||||||
|
|
@ -83,9 +84,7 @@ class CallSupportView extends StackedView<CallSupportViewModel> {
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildTitle(),
|
_buildTitle(),
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildSubtitle('+2519012345678'),
|
_buildSubtitle(kPhoneSupport),
|
||||||
verticalSpaceSmall,
|
|
||||||
_buildSubtitle('+2519012345678'),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildIcon() =>
|
Widget _buildIcon() =>
|
||||||
|
|
@ -109,12 +108,13 @@ class CallSupportView extends StackedView<CallSupportViewModel> {
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildContinueButton(CallSupportViewModel viewModel) =>
|
Widget _buildContinueButton(CallSupportViewModel viewModel) =>
|
||||||
const CustomElevatedButton(
|
CustomElevatedButton(
|
||||||
height: 55,
|
height: 55,
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
text: 'Tap to Call',
|
text: 'Tap to Call',
|
||||||
leadingIcon: Icons.call,
|
leadingIcon: Icons.call,
|
||||||
foregroundColor: kcWhite,
|
foregroundColor: kcWhite,
|
||||||
backgroundColor: kcPrimaryColor,
|
backgroundColor: kcPrimaryColor,
|
||||||
|
onTap: () async => await viewModel.callSupport(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,18 @@ import 'package:stacked/stacked.dart';
|
||||||
import 'package:stacked_services/stacked_services.dart';
|
import 'package:stacked_services/stacked_services.dart';
|
||||||
|
|
||||||
import '../../../app/app.locator.dart';
|
import '../../../app/app.locator.dart';
|
||||||
|
import '../../../services/phone_caller_service.dart';
|
||||||
|
import '../../common/app_constants.dart';
|
||||||
|
|
||||||
class CallSupportViewModel extends BaseViewModel {
|
class CallSupportViewModel extends BaseViewModel {
|
||||||
// Dependency injection
|
// Dependency injection
|
||||||
final _navigationService = locator<NavigationService>();
|
final _navigationService = locator<NavigationService>();
|
||||||
|
|
||||||
|
final _phoneCallerService = locator<PhoneCallerService>();
|
||||||
|
|
||||||
|
// Call support
|
||||||
|
Future<void> callSupport() => _phoneCallerService.call(kPhoneSupport);
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
void pop() => _navigationService.back();
|
void pop() => _navigationService.back();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,14 +40,6 @@ class ForgetPasswordViewModel extends FormViewModel {
|
||||||
|
|
||||||
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;
|
||||||
|
|
@ -105,11 +97,9 @@ class ForgetPasswordViewModel extends FormViewModel {
|
||||||
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 / 4; // returns 0.0 → 1.0
|
return completed / 2; // returns 0.0 → 1.0
|
||||||
}
|
}
|
||||||
|
|
||||||
void validatePassword(
|
void validatePassword(
|
||||||
|
|
@ -120,17 +110,7 @@ class ForgetPasswordViewModel extends FormViewModel {
|
||||||
_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;
|
||||||
|
|
@ -156,8 +136,6 @@ class ForgetPasswordViewModel extends FormViewModel {
|
||||||
// Reset reset password screen
|
// Reset reset password screen
|
||||||
void resetResetPasswordScreen() {
|
void resetResetPasswordScreen() {
|
||||||
_length = false;
|
_length = false;
|
||||||
_number = false;
|
|
||||||
_specialChar = false;
|
|
||||||
_passwordMatch = false;
|
_passwordMatch = false;
|
||||||
_focusPassword = false;
|
_focusPassword = false;
|
||||||
_focusResetCode = false;
|
_focusResetCode = false;
|
||||||
|
|
|
||||||
|
|
@ -140,8 +140,6 @@ class ResetPasswordScreen extends ViewModelWidget<ForgetPasswordViewModel> {
|
||||||
_buildLinearProgressIndicator(viewModel),
|
_buildLinearProgressIndicator(viewModel),
|
||||||
verticalSpaceSmall,
|
verticalSpaceSmall,
|
||||||
_buildCharLengthValidator(viewModel),
|
_buildCharLengthValidator(viewModel),
|
||||||
_buildNumberValidator(viewModel),
|
|
||||||
_buildSymbolValidator(viewModel),
|
|
||||||
_buildPasswordMatchValidator(viewModel),
|
_buildPasswordMatchValidator(viewModel),
|
||||||
verticalSpaceSmall,
|
verticalSpaceSmall,
|
||||||
_buildSignUpButton(viewModel),
|
_buildSignUpButton(viewModel),
|
||||||
|
|
@ -256,16 +254,6 @@ class ResetPasswordScreen extends ViewModelWidget<ForgetPasswordViewModel> {
|
||||||
backgroundColor: viewModel.length ? kcPrimaryColor : kcLightGrey,
|
backgroundColor: viewModel.length ? kcPrimaryColor : kcLightGrey,
|
||||||
label: '8 characters minimum');
|
label: '8 characters minimum');
|
||||||
|
|
||||||
Widget _buildNumberValidator(ForgetPasswordViewModel viewModel) =>
|
|
||||||
ValidatorListTile(
|
|
||||||
backgroundColor: viewModel.number ? kcPrimaryColor : kcLightGrey,
|
|
||||||
label: 'a number');
|
|
||||||
|
|
||||||
Widget _buildSymbolValidator(ForgetPasswordViewModel viewModel) =>
|
|
||||||
ValidatorListTile(
|
|
||||||
backgroundColor: viewModel.specialChar ? kcPrimaryColor : kcLightGrey,
|
|
||||||
label: 'one symbol minimum');
|
|
||||||
|
|
||||||
Widget _buildPasswordMatchValidator(ForgetPasswordViewModel viewModel) =>
|
Widget _buildPasswordMatchValidator(ForgetPasswordViewModel viewModel) =>
|
||||||
ValidatorListTile(
|
ValidatorListTile(
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
|
|
@ -281,20 +269,14 @@ class ResetPasswordScreen extends ViewModelWidget<ForgetPasswordViewModel> {
|
||||||
onTap: passwordController.text.isNotEmpty &&
|
onTap: passwordController.text.isNotEmpty &&
|
||||||
confirmPasswordController.text.isNotEmpty &&
|
confirmPasswordController.text.isNotEmpty &&
|
||||||
resetCodeController.text.isNotEmpty &&
|
resetCodeController.text.isNotEmpty &&
|
||||||
viewModel.number &&
|
|
||||||
viewModel.length &&
|
viewModel.length &&
|
||||||
viewModel.specialChar &&
|
|
||||||
viewModel.specialChar &&
|
|
||||||
viewModel.passwordMatch
|
viewModel.passwordMatch
|
||||||
? () async => await _reset(viewModel)
|
? () async => await _reset(viewModel)
|
||||||
: null,
|
: null,
|
||||||
backgroundColor: passwordController.text.isNotEmpty &&
|
backgroundColor: passwordController.text.isNotEmpty &&
|
||||||
confirmPasswordController.text.isNotEmpty &&
|
confirmPasswordController.text.isNotEmpty &&
|
||||||
resetCodeController.text.isNotEmpty &&
|
resetCodeController.text.isNotEmpty &&
|
||||||
viewModel.number &&
|
|
||||||
viewModel.length &&
|
viewModel.length &&
|
||||||
viewModel.specialChar &&
|
|
||||||
viewModel.specialChar &&
|
|
||||||
viewModel.passwordMatch
|
viewModel.passwordMatch
|
||||||
? kcPrimaryColor
|
? kcPrimaryColor
|
||||||
: kcPrimaryColor.withOpacity(0.1),
|
: kcPrimaryColor.withOpacity(0.1),
|
||||||
|
|
|
||||||
|
|
@ -84,8 +84,9 @@ class LearnCourseView extends StackedView<LearnCourseViewModel> {
|
||||||
course: viewModel.learnCourses[index],
|
course: viewModel.learnCourses[index],
|
||||||
onViewTap: () async => await viewModel
|
onViewTap: () async => await viewModel
|
||||||
.navigateToLearnModule(viewModel.learnCourses[index]),
|
.navigateToLearnModule(viewModel.learnCourses[index]),
|
||||||
onPracticeTap: () async => await viewModel
|
onPracticeTap: () async => await viewModel.navigateToLearnPractice(
|
||||||
.navigateToLearnPractice(viewModel.learnCourses[index].id ?? 0),
|
id: viewModel.learnCourses[index].id ?? 0,
|
||||||
|
level: viewModel.learnCourses[index].name ?? ''),
|
||||||
),
|
),
|
||||||
separatorBuilder: (context, index) => verticalSpaceSmall,
|
separatorBuilder: (context, index) => verticalSpaceSmall,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,16 @@ class LearnCourseViewModel extends BaseViewModel {
|
||||||
Future<void> navigateToLearnModule(LearnCourse course) async =>
|
Future<void> navigateToLearnModule(LearnCourse course) async =>
|
||||||
_navigationService.navigateToLearnModuleView(course: course);
|
_navigationService.navigateToLearnModuleView(course: course);
|
||||||
|
|
||||||
Future<void> navigateToLearnPractice(int id) async => await _navigationService
|
Future<void> navigateToLearnPractice(
|
||||||
.navigateToLearnPracticeView(id: id, practice: LearnPractices.course);
|
{required int id, required String level}) async =>
|
||||||
|
await _navigationService.navigateToLearnPracticeView(
|
||||||
|
id: id,
|
||||||
|
level: level,
|
||||||
|
label: 'Begin Level Practice',
|
||||||
|
practice: LearnPractices.course,
|
||||||
|
title: 'Let’s Practice Course $level',
|
||||||
|
subtitle: 'Let’s quickly review what you’ve learned in this level!',
|
||||||
|
);
|
||||||
|
|
||||||
// Remote api call
|
// Remote api call
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -115,9 +115,9 @@ class LearnLessonView extends StackedView<LearnLessonViewModel> {
|
||||||
_buildSubtitle(),
|
_buildSubtitle(),
|
||||||
verticalSpaceSmall,
|
verticalSpaceSmall,
|
||||||
_buildModuleProgress(),
|
_buildModuleProgress(),
|
||||||
verticalSpaceMedium,
|
verticalSpaceLarge,
|
||||||
_buildMotivationCard(),
|
_buildMotivationCard(),
|
||||||
verticalSpaceMedium,
|
verticalSpaceLarge,
|
||||||
_buildHeader(),
|
_buildHeader(),
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildListViewBuilder(viewModel),
|
_buildListViewBuilder(viewModel),
|
||||||
|
|
@ -156,20 +156,25 @@ class LearnLessonView extends StackedView<LearnLessonViewModel> {
|
||||||
itemCount: viewModel.lessons.length,
|
itemCount: viewModel.lessons.length,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
itemBuilder: (context, index) => _buildTile(
|
itemBuilder: (context, index) => _buildTile(
|
||||||
|
index: index,
|
||||||
lesson: viewModel.lessons[index],
|
lesson: viewModel.lessons[index],
|
||||||
onLessonTap: () async => await viewModel
|
onLessonTap: () async => await viewModel.navigateToLearnLessonDetail(
|
||||||
.navigateToLearnLessonDetail(viewModel.lessons[index]),
|
lesson: viewModel.lessons[index],
|
||||||
|
hasPractice:
|
||||||
|
index != viewModel.lessons.length - 1 ? true : false),
|
||||||
onPracticeTap: () async => await viewModel
|
onPracticeTap: () async => await viewModel
|
||||||
.navigateToLearnPractice(viewModel.lessons[index].id ?? 0),
|
.navigateToLearnPractice(viewModel.lessons[index].id ?? 0),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildTile({
|
Widget _buildTile({
|
||||||
|
required int index,
|
||||||
required LearnLesson lesson,
|
required LearnLesson lesson,
|
||||||
required GestureTapCallback? onLessonTap,
|
required GestureTapCallback? onLessonTap,
|
||||||
required GestureTapCallback? onPracticeTap,
|
required GestureTapCallback? onPracticeTap,
|
||||||
}) =>
|
}) =>
|
||||||
LearnLessonTile(
|
LearnLessonTile(
|
||||||
|
index: index,
|
||||||
lesson: lesson,
|
lesson: lesson,
|
||||||
onLessonTap: onLessonTap,
|
onLessonTap: onLessonTap,
|
||||||
onPracticeTap: onPracticeTap,
|
onPracticeTap: onPracticeTap,
|
||||||
|
|
|
||||||
|
|
@ -24,11 +24,20 @@ class LearnLessonViewModel extends BaseViewModel {
|
||||||
// Navigation
|
// Navigation
|
||||||
void pop() => _navigationService.back();
|
void pop() => _navigationService.back();
|
||||||
|
|
||||||
Future<void> navigateToLearnPractice(int id) async => await _navigationService
|
Future<void> navigateToLearnPractice(int id) async =>
|
||||||
.navigateToLearnPracticeView(id: id, practice: LearnPractices.lesson);
|
await _navigationService.navigateToLearnPracticeView(
|
||||||
|
id: id,
|
||||||
|
label: 'Start Practice',
|
||||||
|
practice: LearnPractices.lesson,
|
||||||
|
title: 'Let\'s practice what you just learnt!',
|
||||||
|
subtitle:
|
||||||
|
'I’ll ask you a few questions, and you can respond naturally.',
|
||||||
|
);
|
||||||
|
|
||||||
Future<void> navigateToLearnLessonDetail(LearnLesson lesson) async =>
|
Future<void> navigateToLearnLessonDetail(
|
||||||
await _navigationService.navigateToLearnLessonDetailView(lesson: lesson);
|
{required bool hasPractice, required LearnLesson lesson}) async =>
|
||||||
|
await _navigationService.navigateToLearnLessonDetailView(
|
||||||
|
lesson: lesson, hasPractice: hasPractice);
|
||||||
|
|
||||||
// Remote api call
|
// Remote api call
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,11 @@ import '../../widgets/small_app_bar.dart';
|
||||||
import 'learn_lesson_detail_viewmodel.dart';
|
import 'learn_lesson_detail_viewmodel.dart';
|
||||||
|
|
||||||
class LearnLessonDetailView extends StackedView<LearnLessonDetailViewModel> {
|
class LearnLessonDetailView extends StackedView<LearnLessonDetailViewModel> {
|
||||||
|
final bool hasPractice;
|
||||||
final LearnLesson lesson;
|
final LearnLesson lesson;
|
||||||
|
|
||||||
const LearnLessonDetailView({Key? key, required this.lesson})
|
const LearnLessonDetailView(
|
||||||
|
{Key? key, required this.lesson, required this.hasPractice})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
Future<void> _navigate(LearnLessonDetailViewModel viewModel) async {
|
Future<void> _navigate(LearnLessonDetailViewModel viewModel) async {
|
||||||
|
|
@ -86,7 +88,7 @@ class LearnLessonDetailView extends StackedView<LearnLessonDetailViewModel> {
|
||||||
List<Widget> _buildBodyColumnChildren(LearnLessonDetailViewModel viewModel) =>
|
List<Widget> _buildBodyColumnChildren(LearnLessonDetailViewModel viewModel) =>
|
||||||
[
|
[
|
||||||
_buildLevelsColumnWrapper(viewModel),
|
_buildLevelsColumnWrapper(viewModel),
|
||||||
_buildContinueButtonWrapper(viewModel)
|
if (hasPractice) _buildPracticeButtonWrapper(viewModel)
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildLevelsColumnWrapper(LearnLessonDetailViewModel viewModel) =>
|
Widget _buildLevelsColumnWrapper(LearnLessonDetailViewModel viewModel) =>
|
||||||
|
|
@ -157,21 +159,21 @@ class LearnLessonDetailView extends StackedView<LearnLessonDetailViewModel> {
|
||||||
style: style14DG400,
|
style: style14DG400,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildContinueButtonWrapper(LearnLessonDetailViewModel viewModel) =>
|
Widget _buildPracticeButtonWrapper(LearnLessonDetailViewModel viewModel) =>
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
left: 15,
|
left: 15,
|
||||||
right: 15,
|
right: 15,
|
||||||
bottom: 50,
|
bottom: 50,
|
||||||
),
|
),
|
||||||
child: _buildContinueButton(viewModel),
|
child: _buildPracticeButton(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildContinueButton(LearnLessonDetailViewModel viewModel) =>
|
Widget _buildPracticeButton(LearnLessonDetailViewModel viewModel) =>
|
||||||
CustomElevatedButton(
|
CustomElevatedButton(
|
||||||
height: 55,
|
height: 55,
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
text: 'Practices',
|
text: 'Take Practice',
|
||||||
foregroundColor: kcWhite,
|
foregroundColor: kcWhite,
|
||||||
backgroundColor: kcPrimaryColor,
|
backgroundColor: kcPrimaryColor,
|
||||||
onTap: () async => await _navigate(viewModel),
|
onTap: () async => await _navigate(viewModel),
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,13 @@ class LearnLessonDetailViewModel extends BaseViewModel {
|
||||||
// Navigation
|
// Navigation
|
||||||
void pop() => _navigationService.back();
|
void pop() => _navigationService.back();
|
||||||
|
|
||||||
Future<void> navigateToLearnPractice(int id) async => await _navigationService
|
Future<void> navigateToLearnPractice(int id) async =>
|
||||||
.navigateToLearnPracticeView(id: id, practice: LearnPractices.lesson);
|
await _navigationService.navigateToLearnPracticeView(
|
||||||
|
id: id,
|
||||||
|
label: 'Start Practice',
|
||||||
|
practice: LearnPractices.lesson,
|
||||||
|
title: 'Let\'s practice what you just learnt!',
|
||||||
|
subtitle:
|
||||||
|
'I’ll ask you a few questions, and you can respond naturally.',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -116,8 +116,9 @@ class LearnModuleView extends StackedView<LearnModuleViewModel> {
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
itemBuilder: (context, index) => _buildTile(
|
itemBuilder: (context, index) => _buildTile(
|
||||||
module: viewModel.modules[index],
|
module: viewModel.modules[index],
|
||||||
onPracticeTap: () async => await viewModel
|
onPracticeTap: () async => await viewModel.navigateToLearnPractice(
|
||||||
.navigateToLearnPractice(viewModel.modules[index].id ?? 0),
|
id: viewModel.modules[index].id ?? 0,
|
||||||
|
module: viewModel.modules[index].name ?? ''),
|
||||||
onModuleTap: () async =>
|
onModuleTap: () async =>
|
||||||
await viewModel.navigateToLearnLesson(viewModel.modules[index]),
|
await viewModel.navigateToLearnLesson(viewModel.modules[index]),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,15 @@ class LearnModuleViewModel extends BaseViewModel {
|
||||||
Future<void> navigateToLearnLesson(LearnModule module) async =>
|
Future<void> navigateToLearnLesson(LearnModule module) async =>
|
||||||
await _navigationService.navigateToLearnLessonView(module: module);
|
await _navigationService.navigateToLearnLessonView(module: module);
|
||||||
|
|
||||||
Future<void> navigateToLearnPractice(int id) async => await _navigationService
|
Future<void> navigateToLearnPractice(
|
||||||
.navigateToLearnPracticeView(id: id, practice: LearnPractices.module);
|
{required int id, required String module}) async =>
|
||||||
|
await _navigationService.navigateToLearnPracticeView(
|
||||||
|
id: id,
|
||||||
|
label: 'Begin Module Practice',
|
||||||
|
practice: LearnPractices.module,
|
||||||
|
title: 'Let’s Practice $module',
|
||||||
|
subtitle: 'Let’s quickly review what you’ve learned in this module! ',
|
||||||
|
);
|
||||||
|
|
||||||
// Remote api call
|
// Remote api call
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,21 @@ import 'learn_practice_viewmodel.dart';
|
||||||
|
|
||||||
class LearnPracticeView extends StackedView<LearnPracticeViewModel> {
|
class LearnPracticeView extends StackedView<LearnPracticeViewModel> {
|
||||||
final int id;
|
final int id;
|
||||||
|
final String label;
|
||||||
|
final String title;
|
||||||
|
final String? level;
|
||||||
|
final String subtitle;
|
||||||
final LearnPractices practice;
|
final LearnPractices practice;
|
||||||
|
|
||||||
const LearnPracticeView({Key? key, required this.id, required this.practice})
|
const LearnPracticeView({
|
||||||
: super(key: key);
|
Key? key,
|
||||||
|
this.level,
|
||||||
|
required this.id,
|
||||||
|
required this.label,
|
||||||
|
required this.title,
|
||||||
|
required this.practice,
|
||||||
|
required this.subtitle,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
Future<void> _cancel(LearnPracticeViewModel viewModel) async {
|
Future<void> _cancel(LearnPracticeViewModel viewModel) async {
|
||||||
await viewModel.stopRecording();
|
await viewModel.stopRecording();
|
||||||
|
|
@ -108,10 +119,17 @@ class LearnPracticeView extends StackedView<LearnPracticeViewModel> {
|
||||||
_buildLearnPracticeQuestionsScreen(),
|
_buildLearnPracticeQuestionsScreen(),
|
||||||
_buildFinishLearnPracticeScreen(),
|
_buildFinishLearnPracticeScreen(),
|
||||||
_buildLearnPracticeResultScreen(),
|
_buildLearnPracticeResultScreen(),
|
||||||
_buildLearnPracticeCompletionScreen()
|
if (practice == LearnPractices.course)
|
||||||
|
_buildLearnPracticeCompletionScreen()
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildLearnPracticeIntroScreen() => const LearnPracticeIntroScreen();
|
Widget _buildLearnPracticeIntroScreen() => LearnPracticeIntroScreen(
|
||||||
|
level: level,
|
||||||
|
title: title,
|
||||||
|
label: label,
|
||||||
|
practice: practice,
|
||||||
|
subtitle: subtitle,
|
||||||
|
);
|
||||||
|
|
||||||
Widget _buildLearnPracticeElementsScreen() =>
|
Widget _buildLearnPracticeElementsScreen() =>
|
||||||
const LearnPracticeDescriptionScreen();
|
const LearnPracticeDescriptionScreen();
|
||||||
|
|
@ -121,8 +139,9 @@ class LearnPracticeView extends StackedView<LearnPracticeViewModel> {
|
||||||
|
|
||||||
Widget _buildFinishLearnPracticeScreen() => const FinishLearnPracticeScreen();
|
Widget _buildFinishLearnPracticeScreen() => const FinishLearnPracticeScreen();
|
||||||
|
|
||||||
Widget _buildLearnPracticeResultScreen() => const LearnPracticeResultScreen();
|
Widget _buildLearnPracticeResultScreen() =>
|
||||||
|
LearnPracticeResultScreen(practice: practice);
|
||||||
|
|
||||||
Widget _buildLearnPracticeCompletionScreen() =>
|
Widget _buildLearnPracticeCompletionScreen() =>
|
||||||
const LearnPracticeCompletionScreen();
|
LearnPracticeCompletionScreen(level: level ?? '');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -225,7 +225,7 @@ class LearnPracticeViewModel extends ReactiveViewModel {
|
||||||
await stopRecording();
|
await stopRecording();
|
||||||
_answers.add({
|
_answers.add({
|
||||||
'busy_object': question.id.toString(),
|
'busy_object': question.id.toString(),
|
||||||
'sample_text_answer': question.audioCorrectAnswerText,
|
'question_text': question.questionText,
|
||||||
'sample_voice_answer': question.sampleAnswerVoicePrompt,
|
'sample_voice_answer': question.sampleAnswerVoicePrompt,
|
||||||
'recorded_voice_answer': await _voiceRecorderService.getRecordedAudio(),
|
'recorded_voice_answer': await _voiceRecorderService.getRecordedAudio(),
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,8 @@ import '../../../widgets/custom_elevated_button.dart';
|
||||||
|
|
||||||
class LearnPracticeCompletionScreen
|
class LearnPracticeCompletionScreen
|
||||||
extends ViewModelWidget<LearnPracticeViewModel> {
|
extends ViewModelWidget<LearnPracticeViewModel> {
|
||||||
const LearnPracticeCompletionScreen({super.key});
|
final String level;
|
||||||
|
const LearnPracticeCompletionScreen({super.key, required this.level});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, LearnPracticeViewModel viewModel) =>
|
Widget build(BuildContext context, LearnPracticeViewModel viewModel) =>
|
||||||
|
|
@ -54,7 +55,7 @@ class LearnPracticeCompletionScreen
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildTitle() => Text(
|
Widget _buildTitle() => Text(
|
||||||
'Yay, you’ve completed A1 ',
|
'Yay, you’ve completed $level ',
|
||||||
style: style25DG600,
|
style: style25DG600,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import 'package:stacked/stacked.dart';
|
||||||
import 'package:yimaru_app/ui/views/learn_practice/learn_practice_viewmodel.dart';
|
import 'package:yimaru_app/ui/views/learn_practice/learn_practice_viewmodel.dart';
|
||||||
|
|
||||||
import '../../../common/app_colors.dart';
|
import '../../../common/app_colors.dart';
|
||||||
|
import '../../../common/enmus.dart';
|
||||||
import '../../../common/ui_helpers.dart';
|
import '../../../common/ui_helpers.dart';
|
||||||
import '../../../widgets/cancel_learn_practice_sheet.dart';
|
import '../../../widgets/cancel_learn_practice_sheet.dart';
|
||||||
import '../../../widgets/custom_elevated_button.dart';
|
import '../../../widgets/custom_elevated_button.dart';
|
||||||
|
|
@ -10,7 +11,19 @@ import '../../../widgets/small_app_bar.dart';
|
||||||
import '../../../widgets/speaking_partner_image.dart';
|
import '../../../widgets/speaking_partner_image.dart';
|
||||||
|
|
||||||
class LearnPracticeIntroScreen extends ViewModelWidget<LearnPracticeViewModel> {
|
class LearnPracticeIntroScreen extends ViewModelWidget<LearnPracticeViewModel> {
|
||||||
const LearnPracticeIntroScreen({super.key});
|
final String title;
|
||||||
|
final String label;
|
||||||
|
final String? level;
|
||||||
|
final String subtitle;
|
||||||
|
final LearnPractices practice;
|
||||||
|
|
||||||
|
const LearnPracticeIntroScreen(
|
||||||
|
{super.key,
|
||||||
|
this.level,
|
||||||
|
required this.label,
|
||||||
|
required this.title,
|
||||||
|
required this.subtitle,
|
||||||
|
required this.practice});
|
||||||
|
|
||||||
Future<void> _cancel(LearnPracticeViewModel viewModel) async {
|
Future<void> _cancel(LearnPracticeViewModel viewModel) async {
|
||||||
await viewModel.stopRecording();
|
await viewModel.stopRecording();
|
||||||
|
|
@ -114,21 +127,44 @@ class LearnPracticeIntroScreen extends ViewModelWidget<LearnPracticeViewModel> {
|
||||||
List<Widget> _buildPracticeColumnChildren(LearnPracticeViewModel viewModel) =>
|
List<Widget> _buildPracticeColumnChildren(LearnPracticeViewModel viewModel) =>
|
||||||
[
|
[
|
||||||
verticalSpaceMassive,
|
verticalSpaceMassive,
|
||||||
_buildImage(),
|
_buildImageState(),
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildPartnerName(),
|
_buildPartnerNameState(),
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildTitle(),
|
_buildTitle(),
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildSubtitle()
|
_buildSubtitle()
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildImage() => const SpeakingPartnerImage(
|
Widget _buildImageState() =>
|
||||||
|
level != null ? _buildCourseSpeakingPartnerImage() : _buildImage(75);
|
||||||
|
|
||||||
|
Widget _buildCourseSpeakingPartnerImage() => CircleAvatar(
|
||||||
radius: 75,
|
radius: 75,
|
||||||
|
backgroundColor: kcPrimaryColorLight,
|
||||||
|
child: _buildCourseSpeakingPartnerText(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Widget _buildCourseSpeakingPartnerText() => Text(
|
||||||
|
level?.toUpperCase() ?? '',
|
||||||
|
style: style40P900,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildImage(double radius) => SpeakingPartnerImage(radius: radius);
|
||||||
|
|
||||||
|
Widget _buildPartnerNameState() =>
|
||||||
|
level != null ? _buildCourseSpeakingPartner() : _buildPartnerName();
|
||||||
|
|
||||||
|
Widget _buildCourseSpeakingPartner() => Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: _buildCourseSpeakingPartnerChildren(),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildCourseSpeakingPartnerChildren() =>
|
||||||
|
[_buildImage(15), horizontalSpaceTiny, _buildPartnerName()];
|
||||||
|
|
||||||
Widget _buildPartnerName() => Text.rich(
|
Widget _buildPartnerName() => Text.rich(
|
||||||
TextSpan(text: 'Dawit', style: style14DG600, children: [
|
TextSpan(text: 'Daniel', style: style14DG600, children: [
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: ' - Your Speaking Partner',
|
text: ' - Your Speaking Partner',
|
||||||
style: style14MG400,
|
style: style14MG400,
|
||||||
|
|
@ -137,13 +173,13 @@ class LearnPracticeIntroScreen extends ViewModelWidget<LearnPracticeViewModel> {
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildTitle() => Text(
|
Widget _buildTitle() => Text(
|
||||||
'Let\'s practice what you just learnt!',
|
title,
|
||||||
style: style25DG600,
|
style: style25DG600,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildSubtitle() => Text(
|
Widget _buildSubtitle() => Text(
|
||||||
'I’ll ask you a few questions, and you can respond naturally.',
|
subtitle,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
style: style14DG400,
|
style: style14DG400,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
|
|
@ -158,8 +194,8 @@ class LearnPracticeIntroScreen extends ViewModelWidget<LearnPracticeViewModel> {
|
||||||
Widget _buildContinueButton(LearnPracticeViewModel viewModel) =>
|
Widget _buildContinueButton(LearnPracticeViewModel viewModel) =>
|
||||||
CustomElevatedButton(
|
CustomElevatedButton(
|
||||||
height: 55,
|
height: 55,
|
||||||
|
text: label,
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
text: 'Practice',
|
|
||||||
foregroundColor: kcWhite,
|
foregroundColor: kcWhite,
|
||||||
onTap: () => viewModel.goTo(1),
|
onTap: () => viewModel.goTo(1),
|
||||||
backgroundColor: kcPrimaryColor,
|
backgroundColor: kcPrimaryColor,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||||
import 'package:yimaru_app/ui/views/learn_practice/learn_practice_viewmodel.dart';
|
import 'package:yimaru_app/ui/views/learn_practice/learn_practice_viewmodel.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/learn_practice_tip_section.dart';
|
import 'package:yimaru_app/ui/widgets/learn_practice_tip_section.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/learn_practice_results_wrapper.dart';
|
import 'package:yimaru_app/ui/widgets/learn_practice_results_wrapper.dart';
|
||||||
|
|
@ -12,9 +13,19 @@ import '../../../widgets/small_app_bar.dart';
|
||||||
|
|
||||||
class LearnPracticeResultScreen
|
class LearnPracticeResultScreen
|
||||||
extends ViewModelWidget<LearnPracticeViewModel> {
|
extends ViewModelWidget<LearnPracticeViewModel> {
|
||||||
const LearnPracticeResultScreen({super.key});
|
final LearnPractices practice;
|
||||||
|
|
||||||
void _navigate(LearnPracticeViewModel viewModel) async =>
|
const LearnPracticeResultScreen({super.key, required this.practice});
|
||||||
|
|
||||||
|
Future<void> _navigate(LearnPracticeViewModel viewModel) async {
|
||||||
|
if (practice == LearnPractices.course) {
|
||||||
|
viewModel.goTo(5);
|
||||||
|
} else {
|
||||||
|
viewModel.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _retry(LearnPracticeViewModel viewModel) async =>
|
||||||
await viewModel.reset();
|
await viewModel.reset();
|
||||||
|
|
||||||
Future<void> _cancel(LearnPracticeViewModel viewModel) async {
|
Future<void> _cancel(LearnPracticeViewModel viewModel) async {
|
||||||
|
|
@ -145,8 +156,8 @@ class LearnPracticeResultScreen
|
||||||
text: 'Continue',
|
text: 'Continue',
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
foregroundColor: kcWhite,
|
foregroundColor: kcWhite,
|
||||||
onTap: () => viewModel.goTo(5),
|
|
||||||
backgroundColor: kcPrimaryColor,
|
backgroundColor: kcPrimaryColor,
|
||||||
|
onTap: () async => await _navigate(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildPracticeAgainButton(LearnPracticeViewModel viewModel) =>
|
Widget _buildPracticeAgainButton(LearnPracticeViewModel viewModel) =>
|
||||||
|
|
@ -157,6 +168,6 @@ class LearnPracticeResultScreen
|
||||||
backgroundColor: kcWhite,
|
backgroundColor: kcWhite,
|
||||||
borderColor: kcPrimaryColor,
|
borderColor: kcPrimaryColor,
|
||||||
foregroundColor: kcPrimaryColor,
|
foregroundColor: kcPrimaryColor,
|
||||||
onTap: () => _navigate(viewModel),
|
onTap: () async => await _retry(viewModel),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -98,9 +98,9 @@ class CountryRegionFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
||||||
_buildCountryDropDown(viewModel),
|
_buildCountryDropDown(viewModel),
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildRegionFormState(viewModel),
|
_buildRegionFormState(viewModel),
|
||||||
if (viewModel.hasRegionValidationMessage &&
|
if (viewModel.hasRegionValidationMessage &&
|
||||||
!viewModel.dropdownRegion &&
|
!viewModel.dropdownRegion &&
|
||||||
viewModel.focusRegion)
|
viewModel.focusRegion)
|
||||||
verticalSpaceTiny,
|
verticalSpaceTiny,
|
||||||
if (viewModel.hasRegionValidationMessage &&
|
if (viewModel.hasRegionValidationMessage &&
|
||||||
!viewModel.dropdownRegion &&
|
!viewModel.dropdownRegion &&
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,6 @@ 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>();
|
||||||
|
|
@ -68,13 +64,6 @@ 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(
|
||||||
|
|
|
||||||
|
|
@ -66,13 +66,14 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
|
||||||
content: _buildImagePicker(context: context, viewModel: viewModel),
|
content: _buildImagePicker(context: context, viewModel: viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
void _checkRegion(ProfileDetailViewModel viewModel){
|
void _checkRegion(ProfileDetailViewModel viewModel) {
|
||||||
bool region = viewModel.checkRegion(region:viewModel.user?.region ?? 'Addis Ababa',country:viewModel.user?.country ?? 'Ethiopia' );
|
bool region = viewModel.checkRegion(
|
||||||
if(region){
|
region: viewModel.user?.region ?? 'Addis Ababa',
|
||||||
|
country: viewModel.user?.country ?? 'Ethiopia');
|
||||||
|
if (region) {
|
||||||
viewModel.setSelectedRegion(viewModel.user?.region ?? 'Addis Ababa');
|
viewModel.setSelectedRegion(viewModel.user?.region ?? 'Addis Ababa');
|
||||||
}else{
|
} else {
|
||||||
regionController.text = viewModel.user?.region ?? '';
|
regionController.text = viewModel.user?.region ?? '';
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -549,10 +550,12 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
|
||||||
_buildRegionFormState(viewModel),
|
_buildRegionFormState(viewModel),
|
||||||
if (viewModel.hasRegionValidationMessage &&
|
if (viewModel.hasRegionValidationMessage &&
|
||||||
!viewModel.dropdownRegion &&
|
!viewModel.dropdownRegion &&
|
||||||
viewModel.focusRegion) verticalSpaceTiny,
|
viewModel.focusRegion)
|
||||||
|
verticalSpaceTiny,
|
||||||
if (viewModel.hasRegionValidationMessage &&
|
if (viewModel.hasRegionValidationMessage &&
|
||||||
!viewModel.dropdownRegion &&
|
!viewModel.dropdownRegion &&
|
||||||
viewModel.focusRegion) _buildRegionValidatorWrapper(viewModel),
|
viewModel.focusRegion)
|
||||||
|
_buildRegionValidatorWrapper(viewModel),
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildRegionFormFieldLabel() => CustomFormLabel(
|
Widget _buildRegionFormFieldLabel() => CustomFormLabel(
|
||||||
|
|
@ -574,14 +577,15 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
|
||||||
onChanged: (value) =>
|
onChanged: (value) =>
|
||||||
viewModel.setSelectedRegion(value ?? 'Addis Ababa'));
|
viewModel.setSelectedRegion(value ?? 'Addis Ababa'));
|
||||||
|
|
||||||
Widget _buildRegionFormField(ProfileDetailViewModel viewModel) => TextFormField(
|
Widget _buildRegionFormField(ProfileDetailViewModel viewModel) =>
|
||||||
controller: regionController,
|
TextFormField(
|
||||||
onTap: viewModel.setRegionFocus,
|
controller: regionController,
|
||||||
decoration: inputDecoration(
|
onTap: viewModel.setRegionFocus,
|
||||||
hint: 'Enter Your City',
|
decoration: inputDecoration(
|
||||||
focus: viewModel.focusRegion,
|
hint: 'Enter Your City',
|
||||||
filled: regionController.text.isNotEmpty),
|
focus: viewModel.focusRegion,
|
||||||
);
|
filled: regionController.text.isNotEmpty),
|
||||||
|
);
|
||||||
|
|
||||||
Widget _buildRegionValidatorWrapper(ProfileDetailViewModel viewModel) =>
|
Widget _buildRegionValidatorWrapper(ProfileDetailViewModel viewModel) =>
|
||||||
viewModel.hasRegionValidationMessage
|
viewModel.hasRegionValidationMessage
|
||||||
|
|
@ -589,10 +593,9 @@ 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(
|
||||||
|
|
@ -608,7 +611,6 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
|
||||||
_buildOccupationDropdownLabel(),
|
_buildOccupationDropdownLabel(),
|
||||||
verticalSpaceSmall,
|
verticalSpaceSmall,
|
||||||
_buildOccupationDropdown(viewModel)
|
_buildOccupationDropdown(viewModel)
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildOccupationDropdownLabel() => CustomFormLabel(
|
Widget _buildOccupationDropdownLabel() => CustomFormLabel(
|
||||||
|
|
@ -625,9 +627,9 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
|
||||||
onChanged: (value) => viewModel.setSelectedOccupation(
|
onChanged: (value) => viewModel.setSelectedOccupation(
|
||||||
value ?? 'Students (High school & University)'));
|
value ?? 'Students (High school & University)'));
|
||||||
Icon _buildSearchIcon() => const Icon(
|
Icon _buildSearchIcon() => const Icon(
|
||||||
Icons.search,
|
Icons.search,
|
||||||
color: kcPrimaryColor,
|
color: kcPrimaryColor,
|
||||||
);
|
);
|
||||||
Widget _buildLowerColumn(ProfileDetailViewModel viewModel) => Column(
|
Widget _buildLowerColumn(ProfileDetailViewModel viewModel) => Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: _buildLowerColumnChildren(viewModel),
|
children: _buildLowerColumnChildren(viewModel),
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ const String RegionValueKey = 'region';
|
||||||
const String PhoneNumberValueKey = 'phoneNumber';
|
const String PhoneNumberValueKey = 'phoneNumber';
|
||||||
const String LastNameValueKey = 'lastName';
|
const String LastNameValueKey = 'lastName';
|
||||||
const String FirstNameValueKey = 'firstName';
|
const String FirstNameValueKey = 'firstName';
|
||||||
const String OccupationValueKey = 'occupation';
|
|
||||||
|
|
||||||
final Map<String, TextEditingController>
|
final Map<String, TextEditingController>
|
||||||
_ProfileDetailViewTextEditingControllers = {};
|
_ProfileDetailViewTextEditingControllers = {};
|
||||||
|
|
@ -32,7 +31,6 @@ final Map<String, String? Function(String?)?>
|
||||||
PhoneNumberValueKey: FormValidator.validatePhoneNumberForm,
|
PhoneNumberValueKey: FormValidator.validatePhoneNumberForm,
|
||||||
LastNameValueKey: FormValidator.validateForm,
|
LastNameValueKey: FormValidator.validateForm,
|
||||||
FirstNameValueKey: FormValidator.validateForm,
|
FirstNameValueKey: FormValidator.validateForm,
|
||||||
OccupationValueKey: FormValidator.validateForm,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
mixin $ProfileDetailView {
|
mixin $ProfileDetailView {
|
||||||
|
|
@ -46,15 +44,12 @@ mixin $ProfileDetailView {
|
||||||
_getFormTextEditingController(LastNameValueKey);
|
_getFormTextEditingController(LastNameValueKey);
|
||||||
TextEditingController get firstNameController =>
|
TextEditingController get firstNameController =>
|
||||||
_getFormTextEditingController(FirstNameValueKey);
|
_getFormTextEditingController(FirstNameValueKey);
|
||||||
TextEditingController get occupationController =>
|
|
||||||
_getFormTextEditingController(OccupationValueKey);
|
|
||||||
|
|
||||||
FocusNode get emailFocusNode => _getFormFocusNode(EmailValueKey);
|
FocusNode get emailFocusNode => _getFormFocusNode(EmailValueKey);
|
||||||
FocusNode get regionFocusNode => _getFormFocusNode(RegionValueKey);
|
FocusNode get regionFocusNode => _getFormFocusNode(RegionValueKey);
|
||||||
FocusNode get phoneNumberFocusNode => _getFormFocusNode(PhoneNumberValueKey);
|
FocusNode get phoneNumberFocusNode => _getFormFocusNode(PhoneNumberValueKey);
|
||||||
FocusNode get lastNameFocusNode => _getFormFocusNode(LastNameValueKey);
|
FocusNode get lastNameFocusNode => _getFormFocusNode(LastNameValueKey);
|
||||||
FocusNode get firstNameFocusNode => _getFormFocusNode(FirstNameValueKey);
|
FocusNode get firstNameFocusNode => _getFormFocusNode(FirstNameValueKey);
|
||||||
FocusNode get occupationFocusNode => _getFormFocusNode(OccupationValueKey);
|
|
||||||
|
|
||||||
TextEditingController _getFormTextEditingController(
|
TextEditingController _getFormTextEditingController(
|
||||||
String key, {
|
String key, {
|
||||||
|
|
@ -85,7 +80,6 @@ mixin $ProfileDetailView {
|
||||||
phoneNumberController.addListener(() => _updateFormData(model));
|
phoneNumberController.addListener(() => _updateFormData(model));
|
||||||
lastNameController.addListener(() => _updateFormData(model));
|
lastNameController.addListener(() => _updateFormData(model));
|
||||||
firstNameController.addListener(() => _updateFormData(model));
|
firstNameController.addListener(() => _updateFormData(model));
|
||||||
occupationController.addListener(() => _updateFormData(model));
|
|
||||||
|
|
||||||
_updateFormData(model, forceValidate: _autoTextFieldValidation);
|
_updateFormData(model, forceValidate: _autoTextFieldValidation);
|
||||||
}
|
}
|
||||||
|
|
@ -102,7 +96,6 @@ mixin $ProfileDetailView {
|
||||||
phoneNumberController.addListener(() => _updateFormData(model));
|
phoneNumberController.addListener(() => _updateFormData(model));
|
||||||
lastNameController.addListener(() => _updateFormData(model));
|
lastNameController.addListener(() => _updateFormData(model));
|
||||||
firstNameController.addListener(() => _updateFormData(model));
|
firstNameController.addListener(() => _updateFormData(model));
|
||||||
occupationController.addListener(() => _updateFormData(model));
|
|
||||||
|
|
||||||
_updateFormData(model, forceValidate: _autoTextFieldValidation);
|
_updateFormData(model, forceValidate: _autoTextFieldValidation);
|
||||||
}
|
}
|
||||||
|
|
@ -117,7 +110,6 @@ mixin $ProfileDetailView {
|
||||||
PhoneNumberValueKey: phoneNumberController.text,
|
PhoneNumberValueKey: phoneNumberController.text,
|
||||||
LastNameValueKey: lastNameController.text,
|
LastNameValueKey: lastNameController.text,
|
||||||
FirstNameValueKey: firstNameController.text,
|
FirstNameValueKey: firstNameController.text,
|
||||||
OccupationValueKey: occupationController.text,
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -165,8 +157,6 @@ extension ValueProperties on FormStateHelper {
|
||||||
this.formValueMap[PhoneNumberValueKey] as String?;
|
this.formValueMap[PhoneNumberValueKey] as String?;
|
||||||
String? get lastNameValue => this.formValueMap[LastNameValueKey] as String?;
|
String? get lastNameValue => this.formValueMap[LastNameValueKey] as String?;
|
||||||
String? get firstNameValue => this.formValueMap[FirstNameValueKey] as String?;
|
String? get firstNameValue => this.formValueMap[FirstNameValueKey] as String?;
|
||||||
String? get occupationValue =>
|
|
||||||
this.formValueMap[OccupationValueKey] as String?;
|
|
||||||
|
|
||||||
set emailValue(String? value) {
|
set emailValue(String? value) {
|
||||||
this.setData(
|
this.setData(
|
||||||
|
|
@ -226,18 +216,6 @@ extension ValueProperties on FormStateHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
set occupationValue(String? value) {
|
|
||||||
this.setData(
|
|
||||||
this.formValueMap..addAll({OccupationValueKey: value}),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (_ProfileDetailViewTextEditingControllers.containsKey(
|
|
||||||
OccupationValueKey)) {
|
|
||||||
_ProfileDetailViewTextEditingControllers[OccupationValueKey]?.text =
|
|
||||||
value ?? '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get hasEmail =>
|
bool get hasEmail =>
|
||||||
this.formValueMap.containsKey(EmailValueKey) &&
|
this.formValueMap.containsKey(EmailValueKey) &&
|
||||||
(emailValue?.isNotEmpty ?? false);
|
(emailValue?.isNotEmpty ?? false);
|
||||||
|
|
@ -253,9 +231,6 @@ extension ValueProperties on FormStateHelper {
|
||||||
bool get hasFirstName =>
|
bool get hasFirstName =>
|
||||||
this.formValueMap.containsKey(FirstNameValueKey) &&
|
this.formValueMap.containsKey(FirstNameValueKey) &&
|
||||||
(firstNameValue?.isNotEmpty ?? false);
|
(firstNameValue?.isNotEmpty ?? false);
|
||||||
bool get hasOccupation =>
|
|
||||||
this.formValueMap.containsKey(OccupationValueKey) &&
|
|
||||||
(occupationValue?.isNotEmpty ?? false);
|
|
||||||
|
|
||||||
bool get hasEmailValidationMessage =>
|
bool get hasEmailValidationMessage =>
|
||||||
this.fieldsValidationMessages[EmailValueKey]?.isNotEmpty ?? false;
|
this.fieldsValidationMessages[EmailValueKey]?.isNotEmpty ?? false;
|
||||||
|
|
@ -267,8 +242,6 @@ extension ValueProperties on FormStateHelper {
|
||||||
this.fieldsValidationMessages[LastNameValueKey]?.isNotEmpty ?? false;
|
this.fieldsValidationMessages[LastNameValueKey]?.isNotEmpty ?? false;
|
||||||
bool get hasFirstNameValidationMessage =>
|
bool get hasFirstNameValidationMessage =>
|
||||||
this.fieldsValidationMessages[FirstNameValueKey]?.isNotEmpty ?? false;
|
this.fieldsValidationMessages[FirstNameValueKey]?.isNotEmpty ?? false;
|
||||||
bool get hasOccupationValidationMessage =>
|
|
||||||
this.fieldsValidationMessages[OccupationValueKey]?.isNotEmpty ?? false;
|
|
||||||
|
|
||||||
String? get emailValidationMessage =>
|
String? get emailValidationMessage =>
|
||||||
this.fieldsValidationMessages[EmailValueKey];
|
this.fieldsValidationMessages[EmailValueKey];
|
||||||
|
|
@ -280,8 +253,6 @@ extension ValueProperties on FormStateHelper {
|
||||||
this.fieldsValidationMessages[LastNameValueKey];
|
this.fieldsValidationMessages[LastNameValueKey];
|
||||||
String? get firstNameValidationMessage =>
|
String? get firstNameValidationMessage =>
|
||||||
this.fieldsValidationMessages[FirstNameValueKey];
|
this.fieldsValidationMessages[FirstNameValueKey];
|
||||||
String? get occupationValidationMessage =>
|
|
||||||
this.fieldsValidationMessages[OccupationValueKey];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Methods on FormStateHelper {
|
extension Methods on FormStateHelper {
|
||||||
|
|
@ -295,8 +266,6 @@ extension Methods on FormStateHelper {
|
||||||
this.fieldsValidationMessages[LastNameValueKey] = validationMessage;
|
this.fieldsValidationMessages[LastNameValueKey] = validationMessage;
|
||||||
void setFirstNameValidationMessage(String? validationMessage) =>
|
void setFirstNameValidationMessage(String? validationMessage) =>
|
||||||
this.fieldsValidationMessages[FirstNameValueKey] = validationMessage;
|
this.fieldsValidationMessages[FirstNameValueKey] = validationMessage;
|
||||||
void setOccupationValidationMessage(String? validationMessage) =>
|
|
||||||
this.fieldsValidationMessages[OccupationValueKey] = validationMessage;
|
|
||||||
|
|
||||||
/// Clears text input fields on the Form
|
/// Clears text input fields on the Form
|
||||||
void clearForm() {
|
void clearForm() {
|
||||||
|
|
@ -305,7 +274,6 @@ extension Methods on FormStateHelper {
|
||||||
phoneNumberValue = '';
|
phoneNumberValue = '';
|
||||||
lastNameValue = '';
|
lastNameValue = '';
|
||||||
firstNameValue = '';
|
firstNameValue = '';
|
||||||
occupationValue = '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Validates text input fields on the Form
|
/// Validates text input fields on the Form
|
||||||
|
|
@ -316,7 +284,6 @@ extension Methods on FormStateHelper {
|
||||||
PhoneNumberValueKey: getValidationMessage(PhoneNumberValueKey),
|
PhoneNumberValueKey: getValidationMessage(PhoneNumberValueKey),
|
||||||
LastNameValueKey: getValidationMessage(LastNameValueKey),
|
LastNameValueKey: getValidationMessage(LastNameValueKey),
|
||||||
FirstNameValueKey: getValidationMessage(FirstNameValueKey),
|
FirstNameValueKey: getValidationMessage(FirstNameValueKey),
|
||||||
OccupationValueKey: getValidationMessage(OccupationValueKey),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -341,5 +308,4 @@ void updateValidationData(FormStateHelper model) =>
|
||||||
PhoneNumberValueKey: getValidationMessage(PhoneNumberValueKey),
|
PhoneNumberValueKey: getValidationMessage(PhoneNumberValueKey),
|
||||||
LastNameValueKey: getValidationMessage(LastNameValueKey),
|
LastNameValueKey: getValidationMessage(LastNameValueKey),
|
||||||
FirstNameValueKey: getValidationMessage(FirstNameValueKey),
|
FirstNameValueKey: getValidationMessage(FirstNameValueKey),
|
||||||
OccupationValueKey: getValidationMessage(OccupationValueKey),
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -57,13 +57,11 @@ class ProfileDetailViewModel extends ReactiveViewModel
|
||||||
|
|
||||||
bool get focusEmail => _focusEmail;
|
bool get focusEmail => _focusEmail;
|
||||||
|
|
||||||
|
|
||||||
// Occupation
|
// Occupation
|
||||||
String _selectedOccupation = 'Students (High school & University)';
|
String _selectedOccupation = 'Students (High school & University)';
|
||||||
|
|
||||||
String get selectedOccupation => _selectedOccupation;
|
String get selectedOccupation => _selectedOccupation;
|
||||||
|
|
||||||
|
|
||||||
// Country
|
// Country
|
||||||
String _selectedCountry = 'Ethiopia';
|
String _selectedCountry = 'Ethiopia';
|
||||||
|
|
||||||
|
|
@ -117,18 +115,16 @@ class ProfileDetailViewModel extends ReactiveViewModel
|
||||||
rebuildUi();
|
rebuildUi();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Occupation
|
// Occupation
|
||||||
List<String> getOccupations() => [
|
List<String> getOccupations() => [
|
||||||
'Students (High school & University)',
|
'Students (High school & University)',
|
||||||
'Job Seekers / Fresh Graduates',
|
'Job Seekers / Fresh Graduates',
|
||||||
'Working Professionals (Corporate/Office)',
|
'Working Professionals (Corporate/Office)',
|
||||||
'Government & NGO Workers',
|
'Government & NGO Workers',
|
||||||
'Entrepreneurs & Small Business Owners',
|
'Entrepreneurs & Small Business Owners',
|
||||||
'Hospitality & Tourism Workers',
|
'Hospitality & Tourism Workers',
|
||||||
'Freelancers / Remote Workers (Digital Economy)'
|
'Freelancers / Remote Workers (Digital Economy)'
|
||||||
];
|
];
|
||||||
|
|
||||||
void setSelectedOccupation(String value) {
|
void setSelectedOccupation(String value) {
|
||||||
_selectedOccupation = value;
|
_selectedOccupation = value;
|
||||||
|
|
@ -137,158 +133,158 @@ class ProfileDetailViewModel extends ReactiveViewModel
|
||||||
|
|
||||||
// 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;
|
||||||
|
|
@ -305,24 +301,24 @@ class ProfileDetailViewModel extends ReactiveViewModel
|
||||||
|
|
||||||
// Region
|
// Region
|
||||||
List<String> getRegions() => [
|
List<String> getRegions() => [
|
||||||
'Addis Ababa',
|
'Addis Ababa',
|
||||||
'Afar',
|
'Afar',
|
||||||
'Amhara',
|
'Amhara',
|
||||||
'Benishangul-Gumuz',
|
'Benishangul-Gumuz',
|
||||||
'Central Ethiopia',
|
'Central Ethiopia',
|
||||||
'Dire Dawa',
|
'Dire Dawa',
|
||||||
'Gambela',
|
'Gambela',
|
||||||
'Harari',
|
'Harari',
|
||||||
'Oromia',
|
'Oromia',
|
||||||
'Sidama',
|
'Sidama',
|
||||||
'Somali',
|
'Somali',
|
||||||
'South Ethiopia',
|
'South Ethiopia',
|
||||||
'South West Ethiopia Peoples',
|
'South West Ethiopia Peoples',
|
||||||
'Tigray',
|
'Tigray',
|
||||||
];
|
];
|
||||||
|
|
||||||
bool checkRegion({required String region,required String country}){
|
bool checkRegion({required String region, required String country}) {
|
||||||
if(country == 'Ethiopia'){
|
if (country == 'Ethiopia') {
|
||||||
return getRegions().contains(region);
|
return getRegions().contains(region);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,6 @@ class RegisterViewModel extends ReactiveViewModel
|
||||||
_length = false;
|
_length = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (password == confirmPassword) {
|
if (password == confirmPassword) {
|
||||||
_passwordMatch = true;
|
_passwordMatch = true;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -237,6 +236,7 @@ class RegisterViewModel extends ReactiveViewModel
|
||||||
}
|
}
|
||||||
|
|
||||||
void goBack() {
|
void goBack() {
|
||||||
|
print('HERE');
|
||||||
if (_currentPage == 1) {
|
if (_currentPage == 1) {
|
||||||
_currentPage = 0;
|
_currentPage = 0;
|
||||||
rebuildUi();
|
rebuildUi();
|
||||||
|
|
|
||||||
|
|
@ -213,7 +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 _buildPasswordMatchValidator(RegisterViewModel viewModel) =>
|
Widget _buildPasswordMatchValidator(RegisterViewModel viewModel) =>
|
||||||
ValidatorListTile(
|
ValidatorListTile(
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/app_constants.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/circular_icon.dart';
|
import 'package:yimaru_app/ui/widgets/circular_icon.dart';
|
||||||
|
|
||||||
import '../../common/app_colors.dart';
|
import '../../common/app_colors.dart';
|
||||||
|
|
@ -91,20 +92,16 @@ class TelegramSupportView extends StackedView<TelegramSupportViewModel> {
|
||||||
Widget _buildIcon() =>
|
Widget _buildIcon() =>
|
||||||
const CircularIcon(icon: Icons.telegram, size: 50, color: kcSkyBlue);
|
const CircularIcon(icon: Icons.telegram, size: 50, color: kcSkyBlue);
|
||||||
|
|
||||||
Widget _buildTitle() => const Text(
|
Widget _buildTitle() => Text(
|
||||||
'Join Yimaru Academy on Telegram',
|
'Join Yimaru Academy on Telegram',
|
||||||
|
style: style25DG600,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 25,
|
|
||||||
color: kcDarkGrey,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildSubtitle() => const Text(
|
Widget _buildSubtitle() => Text(
|
||||||
'Connect with our support team instantly on Telegram for quick assistance and community updates',
|
'Connect with our support team instantly on Telegram for quick assistance and community updates',
|
||||||
|
style: style14MG400,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(color: kcMediumGrey),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildLowerColumn(TelegramSupportViewModel viewModel) => Column(
|
Widget _buildLowerColumn(TelegramSupportViewModel viewModel) => Column(
|
||||||
|
|
@ -123,31 +120,24 @@ class TelegramSupportView extends StackedView<TelegramSupportViewModel> {
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildContinueButton(TelegramSupportViewModel viewModel) =>
|
Widget _buildContinueButton(TelegramSupportViewModel viewModel) =>
|
||||||
const CustomElevatedButton(
|
CustomElevatedButton(
|
||||||
height: 55,
|
height: 55,
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
leadingIcon: Icons.telegram,
|
|
||||||
text: 'Open in Telegram',
|
text: 'Open in Telegram',
|
||||||
foregroundColor: kcWhite,
|
foregroundColor: kcWhite,
|
||||||
|
leadingIcon: Icons.telegram,
|
||||||
backgroundColor: kcPrimaryColor,
|
backgroundColor: kcPrimaryColor,
|
||||||
|
onTap: () async => await viewModel.launchTelegram(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildOptionTextDivider() => const OptionTextDivider();
|
Widget _buildOptionTextDivider() => const OptionTextDivider();
|
||||||
|
|
||||||
Widget _buildSearchText() => const Text.rich(
|
Widget _buildSearchText() => Text.rich(
|
||||||
TextSpan(
|
TextSpan(text: 'Search for', style: style14DG500, children: [
|
||||||
text: 'Search for',
|
TextSpan(
|
||||||
style: TextStyle(
|
style: style14P600,
|
||||||
color: kcDarkGrey,
|
text: ' $kTelegramSupport',
|
||||||
),
|
)
|
||||||
children: [
|
]),
|
||||||
TextSpan(
|
|
||||||
text: ' @YimaruSupport',
|
|
||||||
style: TextStyle(
|
|
||||||
color: kcPrimaryColor,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
]),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,19 @@ import 'package:stacked/stacked.dart';
|
||||||
import 'package:stacked_services/stacked_services.dart';
|
import 'package:stacked_services/stacked_services.dart';
|
||||||
|
|
||||||
import '../../../app/app.locator.dart';
|
import '../../../app/app.locator.dart';
|
||||||
|
import '../../../services/url_launcher_service.dart';
|
||||||
|
import '../../common/app_constants.dart';
|
||||||
|
|
||||||
class TelegramSupportViewModel extends BaseViewModel {
|
class TelegramSupportViewModel extends BaseViewModel {
|
||||||
|
// Dependency injection
|
||||||
final _navigationService = locator<NavigationService>();
|
final _navigationService = locator<NavigationService>();
|
||||||
|
|
||||||
|
final _urlLauncherService = locator<UrlLauncherService>();
|
||||||
|
|
||||||
|
// Launch telegram
|
||||||
|
Future<void> launchTelegram() =>
|
||||||
|
_urlLauncherService.launchUri(kTelegramSupport);
|
||||||
|
|
||||||
|
// Navigation
|
||||||
void pop() => _navigationService.back();
|
void pop() => _navigationService.back();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
44
lib/ui/widgets/app_bar_pattern.dart
Normal file
44
lib/ui/widgets/app_bar_pattern.dart
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
|
|
||||||
|
class AppBarPattern extends StatelessWidget {
|
||||||
|
const AppBarPattern({
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) => _buildDecorationImageWrapper();
|
||||||
|
|
||||||
|
Widget _buildDecorationImageWrapper() => ClipRRect(
|
||||||
|
borderRadius: const BorderRadius.only(
|
||||||
|
bottomLeft: Radius.circular(24),
|
||||||
|
bottomRight: Radius.circular(24),
|
||||||
|
),
|
||||||
|
child: _buildDecorationImage(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildDecorationImage() => SizedBox(
|
||||||
|
width: double.maxFinite,
|
||||||
|
height: double.maxFinite,
|
||||||
|
child: _buildPatternWrapper(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildPatternWrapper() => SizedBox(
|
||||||
|
width: double.maxFinite,
|
||||||
|
height: double.maxFinite,
|
||||||
|
child: _buildPatternMask(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildPatternMask() => ShaderMask(
|
||||||
|
shaderCallback: (Rect bounds) => const LinearGradient(
|
||||||
|
colors: [kcWhite, kcWhite],
|
||||||
|
).createShader(bounds),
|
||||||
|
blendMode: BlendMode.modulate,
|
||||||
|
child: _buildPattern(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildPattern() => Image.asset(
|
||||||
|
'assets/images/pattern.png',
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/app_bar_pattern.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/language_button.dart';
|
import 'package:yimaru_app/ui/widgets/language_button.dart';
|
||||||
|
|
||||||
class LargeAppBar extends StatelessWidget {
|
class LargeAppBar extends StatelessWidget {
|
||||||
|
|
@ -18,12 +19,11 @@ class LargeAppBar extends StatelessWidget {
|
||||||
required this.showLanguageSelection});
|
required this.showLanguageSelection});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => _buildAppBarWrapper();
|
Widget build(BuildContext context) => _buildStackWrapper();
|
||||||
|
|
||||||
Widget _buildAppBarWrapper() => Container(
|
Widget _buildStackWrapper() => Container(
|
||||||
height: 125,
|
height: 125,
|
||||||
width: double.maxFinite,
|
width: double.maxFinite,
|
||||||
alignment: Alignment.bottomCenter,
|
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
color: kcPrimaryColor,
|
color: kcPrimaryColor,
|
||||||
borderRadius: BorderRadius.only(
|
borderRadius: BorderRadius.only(
|
||||||
|
|
@ -31,6 +31,18 @@ class LargeAppBar extends StatelessWidget {
|
||||||
bottomRight: Radius.circular(24),
|
bottomRight: Radius.circular(24),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
child: _buildStack(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildStack() => Stack(
|
||||||
|
children: [ _buildPattern(),_buildAppBarWrapper()],
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildAppBarWrapper() => Container(
|
||||||
|
color: kcTransparent,
|
||||||
|
width: double.maxFinite,
|
||||||
|
height: double.maxFinite,
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
padding: const EdgeInsets.only(bottom: 25, right: 15),
|
padding: const EdgeInsets.only(bottom: 25, right: 15),
|
||||||
child: _buildAppBarItems(),
|
child: _buildAppBarItems(),
|
||||||
);
|
);
|
||||||
|
|
@ -74,4 +86,6 @@ class LargeAppBar extends StatelessWidget {
|
||||||
Icons.close,
|
Icons.close,
|
||||||
color: kcWhite,
|
color: kcWhite,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Widget _buildPattern() => const AppBarPattern();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,7 @@ class LearnCourseTile extends ViewModelWidget<LearnCourseViewModel> {
|
||||||
height: 15,
|
height: 15,
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
onTap: onViewTap,
|
onTap: onViewTap,
|
||||||
text: 'View Courses',
|
text: 'View Course',
|
||||||
foregroundColor: kcWhite,
|
foregroundColor: kcWhite,
|
||||||
backgroundColor: kcPrimaryColor,
|
backgroundColor: kcPrimaryColor,
|
||||||
);
|
);
|
||||||
|
|
@ -158,7 +158,7 @@ class LearnCourseTile extends ViewModelWidget<LearnCourseViewModel> {
|
||||||
height: 15,
|
height: 15,
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
onTap: onPracticeTap,
|
onTap: onPracticeTap,
|
||||||
text: 'Take Practices',
|
text: 'Take Practice',
|
||||||
backgroundColor: kcWhite,
|
backgroundColor: kcWhite,
|
||||||
borderColor: kcPrimaryColor,
|
borderColor: kcPrimaryColor,
|
||||||
foregroundColor: kcPrimaryColor,
|
foregroundColor: kcPrimaryColor,
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,16 @@ import 'custom_elevated_button.dart';
|
||||||
import 'custom_linear_progress_indicator.dart';
|
import 'custom_linear_progress_indicator.dart';
|
||||||
|
|
||||||
class LearnLessonTile extends ViewModelWidget<LearnLessonViewModel> {
|
class LearnLessonTile extends ViewModelWidget<LearnLessonViewModel> {
|
||||||
|
final int index;
|
||||||
final LearnLesson lesson;
|
final LearnLesson lesson;
|
||||||
final GestureTapCallback? onLessonTap;
|
final GestureTapCallback? onLessonTap;
|
||||||
final GestureTapCallback? onPracticeTap;
|
final GestureTapCallback? onPracticeTap;
|
||||||
|
|
||||||
const LearnLessonTile(
|
const LearnLessonTile({super.key,
|
||||||
{super.key, this.onLessonTap, this.onPracticeTap, required this.lesson});
|
this.onLessonTap,
|
||||||
|
this.onPracticeTap,
|
||||||
|
required this.index,
|
||||||
|
required this.lesson});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, LearnLessonViewModel viewModel) =>
|
Widget build(BuildContext context, LearnLessonViewModel viewModel) =>
|
||||||
|
|
@ -52,8 +56,8 @@ class LearnLessonTile extends ViewModelWidget<LearnLessonViewModel> {
|
||||||
expandedAlignment: Alignment.centerLeft,
|
expandedAlignment: Alignment.centerLeft,
|
||||||
backgroundColor: kcPrimaryColor.withOpacity(0.1),
|
backgroundColor: kcPrimaryColor.withOpacity(0.1),
|
||||||
controlAffinity: ListTileControlAffinity.trailing,
|
controlAffinity: ListTileControlAffinity.trailing,
|
||||||
tilePadding: const EdgeInsets.fromLTRB(15, 15, 15, 0),
|
|
||||||
expandedCrossAxisAlignment: CrossAxisAlignment.start,
|
expandedCrossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
tilePadding: const EdgeInsets.fromLTRB(15, 15, 15, 15),
|
||||||
collapsedBackgroundColor: kcPrimaryColor.withOpacity(0.1),
|
collapsedBackgroundColor: kcPrimaryColor.withOpacity(0.1),
|
||||||
childrenPadding: const EdgeInsets.fromLTRB(15, 0, 15, 15),
|
childrenPadding: const EdgeInsets.fromLTRB(15, 0, 15, 15),
|
||||||
// enabled: (lesson.access?.isAccessible ?? false),
|
// enabled: (lesson.access?.isAccessible ?? false),
|
||||||
|
|
@ -130,11 +134,12 @@ class LearnLessonTile extends ViewModelWidget<LearnLessonViewModel> {
|
||||||
|
|
||||||
Widget _buildActionButtons(LearnLessonViewModel viewModel) => Row(
|
Widget _buildActionButtons(LearnLessonViewModel viewModel) => Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: __buildActionButtonChildren(viewModel),
|
children: _buildActionButtonChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> __buildActionButtonChildren(LearnLessonViewModel viewModel) => [
|
List<Widget> _buildActionButtonChildren(LearnLessonViewModel viewModel) => [
|
||||||
_buildPracticeButtonWrapper(viewModel),
|
if (index != viewModel.lessons.length - 1)
|
||||||
|
_buildPracticeButtonWrapper(viewModel),
|
||||||
horizontalSpaceSmall,
|
horizontalSpaceSmall,
|
||||||
_buildLessonButtonWrapper(viewModel)
|
_buildLessonButtonWrapper(viewModel)
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ class LearnPracticeResultCard extends ViewModelWidget<LearnPracticeViewModel> {
|
||||||
_buildColumnWrapper(viewModel);
|
_buildColumnWrapper(viewModel);
|
||||||
|
|
||||||
Widget _buildColumnWrapper(LearnPracticeViewModel viewModel) => SizedBox(
|
Widget _buildColumnWrapper(LearnPracticeViewModel viewModel) => SizedBox(
|
||||||
height: 100,
|
height: 125,
|
||||||
width: double.maxFinite,
|
width: double.maxFinite,
|
||||||
child: _buildColumn(viewModel),
|
child: _buildColumn(viewModel),
|
||||||
);
|
);
|
||||||
|
|
@ -30,7 +30,8 @@ class LearnPracticeResultCard extends ViewModelWidget<LearnPracticeViewModel> {
|
||||||
[_buildQuestion(viewModel), verticalSpaceSmall, _buildRow()];
|
[_buildQuestion(viewModel), verticalSpaceSmall, _buildRow()];
|
||||||
|
|
||||||
Widget _buildQuestion(LearnPracticeViewModel viewModel) => Text(
|
Widget _buildQuestion(LearnPracticeViewModel viewModel) => Text(
|
||||||
answer['sample_text_answer'],
|
answer['question_text'],
|
||||||
|
maxLines: 2,
|
||||||
style: style14DG400,
|
style: style14DG400,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,7 @@ class MiniThumbnail extends StatelessWidget {
|
||||||
Widget build(BuildContext context) => _buildWrapper();
|
Widget build(BuildContext context) => _buildWrapper();
|
||||||
|
|
||||||
Widget _buildWrapper() => SizedBox(
|
Widget _buildWrapper() => SizedBox(
|
||||||
width: 75,
|
width: 80,
|
||||||
height: double.maxFinite,
|
|
||||||
child: _buildLeadingClipper(),
|
child: _buildLeadingClipper(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -40,15 +39,17 @@ class MiniThumbnail extends StatelessWidget {
|
||||||
: _buildNetworkImage();
|
: _buildNetworkImage();
|
||||||
|
|
||||||
Widget _buildNetworkImage() => CachedNetworkImage(
|
Widget _buildNetworkImage() => CachedNetworkImage(
|
||||||
|
fit: BoxFit.cover,
|
||||||
imageUrl: thumbnail,
|
imageUrl: thumbnail,
|
||||||
fit: BoxFit.fill,
|
|
||||||
width: double.maxFinite,
|
width: double.maxFinite,
|
||||||
|
height: double.maxFinite,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildLocalImage() => Image.asset(
|
Widget _buildLocalImage() => Image.asset(
|
||||||
thumbnail,
|
thumbnail,
|
||||||
fit: BoxFit.fill,
|
fit: BoxFit.cover,
|
||||||
width: double.maxFinite,
|
width: double.maxFinite,
|
||||||
|
height: double.maxFinite,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildPlayButtonWrapper() => Align(
|
Widget _buildPlayButtonWrapper() => Align(
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
name: yimaru_app
|
name: yimaru_app
|
||||||
version: 0.1.13+15
|
version: 0.1.14+16
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
description: A new Flutter project.
|
description: A new Flutter project.
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user