Compare commits
No commits in common. "62c136d8d40f00856334b60cae2208ce5ed4d33e" and "848f0a215e57955d7e176ec77fdca3216733990b" have entirely different histories.
62c136d8d4
...
848f0a215e
|
|
@ -501,7 +501,6 @@ class StackedRouter extends _i1.RouterBase {
|
||||||
return _i37.MaterialPageRoute<dynamic>(
|
return _i37.MaterialPageRoute<dynamic>(
|
||||||
builder: (context) => _i21.LearnLessonDetailView(
|
builder: (context) => _i21.LearnLessonDetailView(
|
||||||
key: args.key,
|
key: args.key,
|
||||||
index: args.index,
|
|
||||||
lesson: args.lesson,
|
lesson: args.lesson,
|
||||||
module: args.module,
|
module: args.module,
|
||||||
hasPractice: args.hasPractice),
|
hasPractice: args.hasPractice),
|
||||||
|
|
@ -1084,7 +1083,6 @@ class ForgetPasswordViewArguments {
|
||||||
class LearnLessonDetailViewArguments {
|
class LearnLessonDetailViewArguments {
|
||||||
const LearnLessonDetailViewArguments({
|
const LearnLessonDetailViewArguments({
|
||||||
this.key,
|
this.key,
|
||||||
required this.index,
|
|
||||||
required this.lesson,
|
required this.lesson,
|
||||||
required this.module,
|
required this.module,
|
||||||
required this.hasPractice,
|
required this.hasPractice,
|
||||||
|
|
@ -1092,8 +1090,6 @@ class LearnLessonDetailViewArguments {
|
||||||
|
|
||||||
final _i37.Key? key;
|
final _i37.Key? key;
|
||||||
|
|
||||||
final int index;
|
|
||||||
|
|
||||||
final _i40.LearnLesson lesson;
|
final _i40.LearnLesson lesson;
|
||||||
|
|
||||||
final _i39.LearnModule module;
|
final _i39.LearnModule module;
|
||||||
|
|
@ -1102,14 +1098,13 @@ class LearnLessonDetailViewArguments {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return '{"key": "$key", "index": "$index", "lesson": "$lesson", "module": "$module", "hasPractice": "$hasPractice"}';
|
return '{"key": "$key", "lesson": "$lesson", "module": "$module", "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 &&
|
return other.key == key &&
|
||||||
other.index == index &&
|
|
||||||
other.lesson == lesson &&
|
other.lesson == lesson &&
|
||||||
other.module == module &&
|
other.module == module &&
|
||||||
other.hasPractice == hasPractice;
|
other.hasPractice == hasPractice;
|
||||||
|
|
@ -1118,7 +1113,6 @@ class LearnLessonDetailViewArguments {
|
||||||
@override
|
@override
|
||||||
int get hashCode {
|
int get hashCode {
|
||||||
return key.hashCode ^
|
return key.hashCode ^
|
||||||
index.hashCode ^
|
|
||||||
lesson.hashCode ^
|
lesson.hashCode ^
|
||||||
module.hashCode ^
|
module.hashCode ^
|
||||||
hasPractice.hashCode;
|
hasPractice.hashCode;
|
||||||
|
|
@ -1845,7 +1839,6 @@ extension NavigatorStateExtension on _i46.NavigationService {
|
||||||
|
|
||||||
Future<dynamic> navigateToLearnLessonDetailView({
|
Future<dynamic> navigateToLearnLessonDetailView({
|
||||||
_i37.Key? key,
|
_i37.Key? key,
|
||||||
required int index,
|
|
||||||
required _i40.LearnLesson lesson,
|
required _i40.LearnLesson lesson,
|
||||||
required _i39.LearnModule module,
|
required _i39.LearnModule module,
|
||||||
required bool hasPractice,
|
required bool hasPractice,
|
||||||
|
|
@ -1857,11 +1850,7 @@ extension NavigatorStateExtension on _i46.NavigationService {
|
||||||
}) async {
|
}) async {
|
||||||
return navigateTo<dynamic>(Routes.learnLessonDetailView,
|
return navigateTo<dynamic>(Routes.learnLessonDetailView,
|
||||||
arguments: LearnLessonDetailViewArguments(
|
arguments: LearnLessonDetailViewArguments(
|
||||||
key: key,
|
key: key, lesson: lesson, module: module, hasPractice: hasPractice),
|
||||||
index: index,
|
|
||||||
lesson: lesson,
|
|
||||||
module: module,
|
|
||||||
hasPractice: hasPractice),
|
|
||||||
id: routerId,
|
id: routerId,
|
||||||
preventDuplicates: preventDuplicates,
|
preventDuplicates: preventDuplicates,
|
||||||
parameters: parameters,
|
parameters: parameters,
|
||||||
|
|
@ -2441,7 +2430,6 @@ extension NavigatorStateExtension on _i46.NavigationService {
|
||||||
|
|
||||||
Future<dynamic> replaceWithLearnLessonDetailView({
|
Future<dynamic> replaceWithLearnLessonDetailView({
|
||||||
_i37.Key? key,
|
_i37.Key? key,
|
||||||
required int index,
|
|
||||||
required _i40.LearnLesson lesson,
|
required _i40.LearnLesson lesson,
|
||||||
required _i39.LearnModule module,
|
required _i39.LearnModule module,
|
||||||
required bool hasPractice,
|
required bool hasPractice,
|
||||||
|
|
@ -2453,11 +2441,7 @@ extension NavigatorStateExtension on _i46.NavigationService {
|
||||||
}) async {
|
}) async {
|
||||||
return replaceWith<dynamic>(Routes.learnLessonDetailView,
|
return replaceWith<dynamic>(Routes.learnLessonDetailView,
|
||||||
arguments: LearnLessonDetailViewArguments(
|
arguments: LearnLessonDetailViewArguments(
|
||||||
key: key,
|
key: key, lesson: lesson, module: module, hasPractice: hasPractice),
|
||||||
index: index,
|
|
||||||
lesson: lesson,
|
|
||||||
module: module,
|
|
||||||
hasPractice: hasPractice),
|
|
||||||
id: routerId,
|
id: routerId,
|
||||||
preventDuplicates: preventDuplicates,
|
preventDuplicates: preventDuplicates,
|
||||||
parameters: parameters,
|
parameters: parameters,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
part 'access.g.dart';
|
part 'access.g.dart';
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class Access {
|
class Access {
|
||||||
final String? reason;
|
final String? reason;
|
||||||
|
|
@ -52,10 +51,12 @@ class Access {
|
||||||
progressPercentPrecise ?? this.progressPercentPrecise,
|
progressPercentPrecise ?? this.progressPercentPrecise,
|
||||||
completedCount: completedCount ?? this.completedCount,
|
completedCount: completedCount ?? this.completedCount,
|
||||||
progressPercent: progressPercent ?? this.progressPercent,
|
progressPercent: progressPercent ?? this.progressPercent,
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
factory Access.fromJson(Map<String, dynamic> json) => _$AccessFromJson(json);
|
factory Access.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$AccessFromJson(json);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$AccessToJson(this);
|
Map<String, dynamic> toJson() => _$AccessToJson(this);
|
||||||
}
|
}
|
||||||
|
|
@ -15,14 +15,16 @@ class CourseProgress {
|
||||||
|
|
||||||
final List<ModuleProgress>? modules;
|
final List<ModuleProgress>? modules;
|
||||||
|
|
||||||
|
|
||||||
@JsonKey(name: 'program_id')
|
@JsonKey(name: 'program_id')
|
||||||
final int? programId;
|
final int? programId;
|
||||||
|
|
||||||
const CourseProgress(
|
|
||||||
{this.id, this.name, this.access, this.modules, this.programId});
|
|
||||||
|
|
||||||
factory CourseProgress.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$CourseProgressFromJson(json);
|
const CourseProgress(
|
||||||
|
{this.id,this.name,this.access,this.modules,this.programId});
|
||||||
|
|
||||||
|
factory CourseProgress.fromJson(Map<String, dynamic> json) => _$CourseProgressFromJson(json);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$CourseProgressToJson(this);
|
Map<String, dynamic> toJson() => _$CourseProgressToJson(this);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ class LearnCourse {
|
||||||
int? sortOrder,
|
int? sortOrder,
|
||||||
int? programId,
|
int? programId,
|
||||||
String? description,
|
String? description,
|
||||||
|
|
||||||
}) {
|
}) {
|
||||||
return LearnCourse(
|
return LearnCourse(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'access.dart';
|
import 'access.dart';
|
||||||
|
|
||||||
part 'learn_lesson.g.dart';
|
part 'learn_lesson.g.dart';
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class LearnLesson {
|
class LearnLesson {
|
||||||
final int? id;
|
final int? id;
|
||||||
|
|
@ -45,6 +44,7 @@ class LearnLesson {
|
||||||
String? videoUrl,
|
String? videoUrl,
|
||||||
String? thumbnail,
|
String? thumbnail,
|
||||||
String? description,
|
String? description,
|
||||||
|
|
||||||
}) {
|
}) {
|
||||||
return LearnLesson(
|
return LearnLesson(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'package:yimaru_app/models/access.dart';
|
import 'package:yimaru_app/models/access.dart';
|
||||||
|
|
||||||
part 'learn_module.g.dart';
|
part 'learn_module.g.dart';
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class LearnModule {
|
class LearnModule {
|
||||||
final int? id;
|
final int? id;
|
||||||
|
|
@ -44,6 +43,7 @@ class LearnModule {
|
||||||
int? programId,
|
int? programId,
|
||||||
int? sortOrder,
|
int? sortOrder,
|
||||||
String? description,
|
String? description,
|
||||||
|
|
||||||
}) {
|
}) {
|
||||||
return LearnModule(
|
return LearnModule(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
|
|
@ -54,6 +54,7 @@ class LearnModule {
|
||||||
sortOrder: sortOrder ?? this.sortOrder,
|
sortOrder: sortOrder ?? this.sortOrder,
|
||||||
programId: programId ?? this.programId,
|
programId: programId ?? this.programId,
|
||||||
description: description ?? this.description,
|
description: description ?? this.description,
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'package:yimaru_app/models/access.dart';
|
import 'package:yimaru_app/models/access.dart';
|
||||||
|
|
||||||
part 'learn_program.g.dart';
|
part 'learn_program.g.dart';
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class LearnProgram {
|
class LearnProgram {
|
||||||
final int? id;
|
final int? id;
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,11 @@ class LessonProgress {
|
||||||
@JsonKey(name: 'module_id')
|
@JsonKey(name: 'module_id')
|
||||||
final int? moduleId;
|
final int? moduleId;
|
||||||
|
|
||||||
const LessonProgress({this.id, this.title, this.access, this.moduleId});
|
|
||||||
|
|
||||||
factory LessonProgress.fromJson(Map<String, dynamic> json) =>
|
const LessonProgress(
|
||||||
_$LessonProgressFromJson(json);
|
{this.id,this.title,this.access,this.moduleId});
|
||||||
|
|
||||||
|
factory LessonProgress.fromJson(Map<String, dynamic> json) => _$LessonProgressFromJson(json);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$LessonProgressToJson(this);
|
Map<String, dynamic> toJson() => _$LessonProgressToJson(this);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,10 @@ class ProgressSummary {
|
||||||
|
|
||||||
final List<CourseProgress>? courses;
|
final List<CourseProgress>? courses;
|
||||||
|
|
||||||
const ProgressSummary({this.id, this.name, this.access, this.courses});
|
const ProgressSummary(
|
||||||
|
{this.id,this.name,this.access,this.courses});
|
||||||
|
|
||||||
factory ProgressSummary.fromJson(Map<String, dynamic> json) =>
|
factory ProgressSummary.fromJson(Map<String, dynamic> json) => _$ProgressSummaryFromJson(json);
|
||||||
_$ProgressSummaryFromJson(json);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$ProgressSummaryToJson(this);
|
Map<String, dynamic> toJson() => _$ProgressSummaryToJson(this);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,7 @@ class AuthenticationService with ListenableServiceMixin {
|
||||||
await _secureService.setString('firstName', data.firstName ?? '');
|
await _secureService.setString('firstName', data.firstName ?? '');
|
||||||
await _secureService.setString('occupation', data.occupation ?? '');
|
await _secureService.setString('occupation', data.occupation ?? '');
|
||||||
|
|
||||||
|
|
||||||
_user = User(
|
_user = User(
|
||||||
email: data.email,
|
email: data.email,
|
||||||
gender: data.gender,
|
gender: data.gender,
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ class LearnService with ListenableServiceMixin {
|
||||||
|
|
||||||
// Initialization
|
// Initialization
|
||||||
learnService() {
|
learnService() {
|
||||||
listenToReactiveValues([_programs, _courses, _lessons, _modules]);
|
listenToReactiveValues([_programs,_courses, _lessons, _modules]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Learn program
|
// Learn program
|
||||||
|
|
@ -148,10 +148,11 @@ class LearnService with ListenableServiceMixin {
|
||||||
);
|
);
|
||||||
}).toList();
|
}).toList();
|
||||||
|
|
||||||
print(
|
print('MY SUMMARIES - COMPLETED COUNT: ${_modules.first.access?.completedCount}');
|
||||||
'MY SUMMARIES - COMPLETED COUNT: ${_modules.first.access?.completedCount}');
|
|
||||||
print('PROGRESS PERCENT: ${_modules.first.access?.progressPercent}');
|
print('PROGRESS PERCENT: ${_modules.first.access?.progressPercent}');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// UPDATE LESSONS
|
/// UPDATE LESSONS
|
||||||
_lessons = _lessons.map((lesson) {
|
_lessons = _lessons.map((lesson) {
|
||||||
return lesson.copyWith(
|
return lesson.copyWith(
|
||||||
|
|
@ -168,6 +169,8 @@ class LearnService with ListenableServiceMixin {
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LearnCourse? getLearnCourseById(int id) {
|
LearnCourse? getLearnCourseById(int id) {
|
||||||
|
|
@ -176,5 +179,7 @@ class LearnService with ListenableServiceMixin {
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -111,5 +111,6 @@ class OnboardingService with ListenableServiceMixin {
|
||||||
_regions = await _apiService.getEthiopiaRegions();
|
_regions = await _apiService.getEthiopiaRegions();
|
||||||
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import 'dart:ui';
|
||||||
|
|
||||||
import 'package:easy_localization/easy_localization.dart' show AssetLoader;
|
import 'package:easy_localization/easy_localization.dart' show AssetLoader;
|
||||||
|
|
||||||
class CodegenLoader extends AssetLoader {
|
class CodegenLoader extends AssetLoader{
|
||||||
const CodegenLoader();
|
const CodegenLoader();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -14,7 +14,7 @@ class CodegenLoader extends AssetLoader {
|
||||||
return Future.value(mapLocales[locale.toString()]);
|
return Future.value(mapLocales[locale.toString()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const Map<String, dynamic> _am = {
|
static const Map<String,dynamic> _am = {
|
||||||
"loading": "በመጫን ላይ",
|
"loading": "በመጫን ላይ",
|
||||||
"welcome_back": "እንኳን በደህና ተመለሱ",
|
"welcome_back": "እንኳን በደህና ተመለሱ",
|
||||||
"checking_user_info": "የተጠቃሚ መረጃን በማረጋገጥ ላይ",
|
"checking_user_info": "የተጠቃሚ መረጃን በማረጋገጥ ላይ",
|
||||||
|
|
@ -38,8 +38,7 @@ class CodegenLoader extends AssetLoader {
|
||||||
"confirm_password": "የይለፍ ቃል ያረጋግጡ",
|
"confirm_password": "የይለፍ ቃል ያረጋግጡ",
|
||||||
"eight_character_minimum": "ቢያንስ 8 ፊደላት",
|
"eight_character_minimum": "ቢያንስ 8 ፊደላት",
|
||||||
"password_match": "የይለፍ ቃሉ ተመሳስሏል",
|
"password_match": "የይለፍ ቃሉ ተመሳስሏል",
|
||||||
"sign_up_agreement":
|
"sign_up_agreement": "‘ይመዝገቡ’ የሚለውን ሲጫኑ በ‘አገልግሎት ውሎች’ እና ‘በግላዊነት ፖሊሲ’ ይስማማሉ።",
|
||||||
"‘ይመዝገቡ’ የሚለውን ሲጫኑ በ‘አገልግሎት ውሎች’ እና ‘በግላዊነት ፖሊሲ’ ይስማማሉ።",
|
|
||||||
"terms_of_services": "የአገልግሎት ውሎች",
|
"terms_of_services": "የአገልግሎት ውሎች",
|
||||||
"and": "እና",
|
"and": "እና",
|
||||||
"privacy_policy": "የግላዊነት ፖሊሲ",
|
"privacy_policy": "የግላዊነት ፖሊሲ",
|
||||||
|
|
@ -138,8 +137,7 @@ class CodegenLoader extends AssetLoader {
|
||||||
"call_our_support": "ከ3 ጠዋት እስከ 12 ማታ ድረስ የድጋፍ ቡድናችንን ይደውሉ",
|
"call_our_support": "ከ3 ጠዋት እስከ 12 ማታ ድረስ የድጋፍ ቡድናችንን ይደውሉ",
|
||||||
"tap_to_call": "ለመደወል ይንኩ",
|
"tap_to_call": "ለመደወል ይንኩ",
|
||||||
"join_telegram": "በቴሌግራም የይማሩ አካዳሚን ይቀላቀሉ",
|
"join_telegram": "በቴሌግራም የይማሩ አካዳሚን ይቀላቀሉ",
|
||||||
"connect_with_support_team":
|
"connect_with_support_team": "ለፈጣን እርዳታ እና የማህበረሰብ ዝማኔዎች፣ በቴሌግራም ከድጋፍ ቡድናችን ጋር ወዲያውኑ ይገናኙ።",
|
||||||
"ለፈጣን እርዳታ እና የማህበረሰብ ዝማኔዎች፣ በቴሌግራም ከድጋፍ ቡድናችን ጋር ወዲያውኑ ይገናኙ።",
|
|
||||||
"open_in_telegram": "በቴሌግራም ይክፈቱ",
|
"open_in_telegram": "በቴሌግራም ይክፈቱ",
|
||||||
"search_for": "ፈልጉት",
|
"search_for": "ፈልጉት",
|
||||||
"current_level": "የአሁኑ ደረጃ",
|
"current_level": "የአሁኑ ደረጃ",
|
||||||
|
|
@ -189,8 +187,7 @@ class CodegenLoader extends AssetLoader {
|
||||||
"evey_one_has_strugle": "ሁሉም ሰው ችግሮች አሉት፣ የአንተን እንጀምር እንፍታ",
|
"evey_one_has_strugle": "ሁሉም ሰው ችግሮች አሉት፣ የአንተን እንጀምር እንፍታ",
|
||||||
"write_your_challenge": "ችግርህን ጻፍ…",
|
"write_your_challenge": "ችግርህን ጻፍ…",
|
||||||
"topic_interest": "በጣም የሚስቡህ ርዕሶች የትኞቹ ናቸው?",
|
"topic_interest": "በጣም የሚስቡህ ርዕሶች የትኞቹ ናቸው?",
|
||||||
"favourite_topic":
|
"favourite_topic": "የምትወዳቸው ርዕሶች አስደሳች እና ከሕይወትህ ጋር የተዛመዱ ትምህርቶችን ለመፍጠር ይረዱናል።",
|
||||||
"የምትወዳቸው ርዕሶች አስደሳች እና ከሕይወትህ ጋር የተዛመዱ ትምህርቶችን ለመፍጠር ይረዱናል።",
|
|
||||||
"your_interest": "ፍላጎትህን ጻፍ…",
|
"your_interest": "ፍላጎትህን ጻፍ…",
|
||||||
"want_quick_assessment": "የእንግሊዝኛ ደረጃህን ለማወቅ ፈጣን ግምገማ ትፈልጋለህ?",
|
"want_quick_assessment": "የእንግሊዝኛ ደረጃህን ለማወቅ ፈጣን ግምገማ ትፈልጋለህ?",
|
||||||
"answer_quick_questions": "የእንግሊዝኛ ችሎታህን ለመረዳት ጥቂት ፈጣን ጥያቄዎችን መልስ።",
|
"answer_quick_questions": "የእንግሊዝኛ ችሎታህን ለመረዳት ጥቂት ፈጣን ጥያቄዎችን መልስ።",
|
||||||
|
|
@ -203,8 +200,8 @@ class CodegenLoader extends AssetLoader {
|
||||||
"ready_to_explore": "የግል ትምህርቶችህን ለማሰስ ዝግጁ ነህ።",
|
"ready_to_explore": "የግል ትምህርቶችህን ለማሰስ ዝግጁ ነህ።",
|
||||||
"finish": "አጠናቅቅ",
|
"finish": "አጠናቅቅ",
|
||||||
"finish_all_practice": "ልምምዱን ለመውሰድ በትምህርቶቹ ውስጥ ያሉትን ሁሉንም ልምምዶች ያጠናቅቁ።"
|
"finish_all_practice": "ልምምዱን ለመውሰድ በትምህርቶቹ ውስጥ ያሉትን ሁሉንም ልምምዶች ያጠናቅቁ።"
|
||||||
};
|
};
|
||||||
static const Map<String, dynamic> _en = {
|
static const Map<String,dynamic> _en = {
|
||||||
"loading": "Loading",
|
"loading": "Loading",
|
||||||
"welcome_back": "Welcome back",
|
"welcome_back": "Welcome back",
|
||||||
"checking_user_info": "Checking user info",
|
"checking_user_info": "Checking user info",
|
||||||
|
|
@ -222,15 +219,13 @@ class CodegenLoader extends AssetLoader {
|
||||||
"login": "Login",
|
"login": "Login",
|
||||||
"register_with_google": "Register with Google",
|
"register_with_google": "Register with Google",
|
||||||
"register_with_phone": "Register with phone number",
|
"register_with_phone": "Register with phone number",
|
||||||
"enter_phone_number":
|
"enter_phone_number": "Enter your phone number. We will send you a confirmation code there.",
|
||||||
"Enter your phone number. We will send you a confirmation code there.",
|
|
||||||
"login_with_email": "Login with email",
|
"login_with_email": "Login with email",
|
||||||
"create_password": "Create password",
|
"create_password": "Create password",
|
||||||
"confirm_password": "Confirm password",
|
"confirm_password": "Confirm password",
|
||||||
"eight_character_minimum": "8 characters minimum",
|
"eight_character_minimum": "8 characters minimum",
|
||||||
"password_match": "password match",
|
"password_match": "password match",
|
||||||
"sign_up_agreement":
|
"sign_up_agreement": "By clicking ‘Sign Up’, you agree to our ‘Terms of Service’ and ‘Privacy Policy’",
|
||||||
"By clicking ‘Sign Up’, you agree to our ‘Terms of Service’ and ‘Privacy Policy’",
|
|
||||||
"terms_of_services": "Terms of Service",
|
"terms_of_services": "Terms of Service",
|
||||||
"and": "and",
|
"and": "and",
|
||||||
"privacy_policy": "Privacy Policy",
|
"privacy_policy": "Privacy Policy",
|
||||||
|
|
@ -241,8 +236,7 @@ class CodegenLoader extends AssetLoader {
|
||||||
"code_sent_to_email": "Code sent to your email",
|
"code_sent_to_email": "Code sent to your email",
|
||||||
"resend_code_in": "Resend code in",
|
"resend_code_in": "Resend code in",
|
||||||
"reset_password": "Reset Password",
|
"reset_password": "Reset Password",
|
||||||
"enter_email_reset_code":
|
"enter_email_reset_code": "Enter your email. We will send you a reset code.",
|
||||||
"Enter your email. We will send you a reset code.",
|
|
||||||
"please_wait": "Please wait",
|
"please_wait": "Please wait",
|
||||||
"reset_code_sent": "Reset code sent successfully",
|
"reset_code_sent": "Reset code sent successfully",
|
||||||
"reset_code": "Reset code",
|
"reset_code": "Reset code",
|
||||||
|
|
@ -283,8 +277,7 @@ class CodegenLoader extends AssetLoader {
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"you_are_speaking": "You're speaking",
|
"you_are_speaking": "You're speaking",
|
||||||
"practice_completed": "Practice completed!",
|
"practice_completed": "Practice completed!",
|
||||||
"great_improvement":
|
"great_improvement": "You sound more confident this time, great improvement",
|
||||||
"You sound more confident this time, great improvement",
|
|
||||||
"practice_again": "Practice again",
|
"practice_again": "Practice again",
|
||||||
"conversation_review": "Conversation review",
|
"conversation_review": "Conversation review",
|
||||||
"result": "Result",
|
"result": "Result",
|
||||||
|
|
@ -331,8 +324,7 @@ class CodegenLoader extends AssetLoader {
|
||||||
"call_our_support": "Call our support team between 9 AM - 6 PM",
|
"call_our_support": "Call our support team between 9 AM - 6 PM",
|
||||||
"tap_to_call": "Tap to call",
|
"tap_to_call": "Tap to call",
|
||||||
"join_telegram": "Join Yimaru Academy on Telegram",
|
"join_telegram": "Join Yimaru Academy on Telegram",
|
||||||
"connect_with_support_team":
|
"connect_with_support_team": "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",
|
|
||||||
"open_in_telegram": "Open in Telegram",
|
"open_in_telegram": "Open in Telegram",
|
||||||
"search_for": "Search for",
|
"search_for": "Search for",
|
||||||
"current_level": "Current Level",
|
"current_level": "Current Level",
|
||||||
|
|
@ -340,22 +332,18 @@ class CodegenLoader extends AssetLoader {
|
||||||
"no_practice_available": "No practice available!",
|
"no_practice_available": "No practice available!",
|
||||||
"begin_module_practice": "Begin Module Practice",
|
"begin_module_practice": "Begin Module Practice",
|
||||||
"lets_practice_lesson": "Let’s Practice",
|
"lets_practice_lesson": "Let’s Practice",
|
||||||
"lets_quickly_review":
|
"lets_quickly_review": "Let’s quickly review what you’ve learned in this module!",
|
||||||
"Let’s quickly review what you’ve learned in this module!",
|
|
||||||
"lets_practice_module": "Let's practice what you just learnt!",
|
"lets_practice_module": "Let's practice what you just learnt!",
|
||||||
"ask_you_few_actions":
|
"ask_you_few_actions": "I’ll ask you a few questions, and you can respond naturally.",
|
||||||
"I’ll ask you a few questions, and you can respond naturally.",
|
|
||||||
"begin_level_practice": "Begin Level Practice",
|
"begin_level_practice": "Begin Level Practice",
|
||||||
"lets_practice_course": "Let’s Practice Course",
|
"lets_practice_course": "Let’s Practice Course",
|
||||||
"lets_quick_review":
|
"lets_quick_review": "Let’s quickly review what you’ve learned in this level!",
|
||||||
"Let’s quickly review what you’ve learned in this level!",
|
|
||||||
"speaking": "is speaking...",
|
"speaking": "is speaking...",
|
||||||
"you_have_finished_practice": "You have finished your practice",
|
"you_have_finished_practice": "You have finished your practice",
|
||||||
"view_results": "View My Results",
|
"view_results": "View My Results",
|
||||||
"sample_answer": "Sample Answer",
|
"sample_answer": "Sample Answer",
|
||||||
"your_answer": "Your Answer",
|
"your_answer": "Your Answer",
|
||||||
"sound_confident":
|
"sound_confident": "You sound more confident this time - great improvement!",
|
||||||
"You sound more confident this time - great improvement!",
|
|
||||||
"you_have_completed": "Yay, you’ve completed",
|
"you_have_completed": "Yay, you’ve completed",
|
||||||
"yes": "Yes",
|
"yes": "Yes",
|
||||||
"no": "No",
|
"no": "No",
|
||||||
|
|
@ -366,20 +354,15 @@ class CodegenLoader extends AssetLoader {
|
||||||
"phone_must_start_with": "Phone number must start with 251",
|
"phone_must_start_with": "Phone number must start with 251",
|
||||||
"phone_must_be": "Phone number must be 12 digits",
|
"phone_must_be": "Phone number must be 12 digits",
|
||||||
"what_should_we_call_you": "What should we call you?",
|
"what_should_we_call_you": "What should we call you?",
|
||||||
"name_for_personalization":
|
"name_for_personalization": "We’ll use your name to personalize your learning journey.",
|
||||||
"We’ll use your name to personalize your learning journey.",
|
|
||||||
"choose_your_gender": "Choose your gender?",
|
"choose_your_gender": "Choose your gender?",
|
||||||
"gender_for_personalization":
|
"gender_for_personalization": "We’ll personalize your learning experience based on your gender.",
|
||||||
"We’ll personalize your learning experience based on your gender.",
|
|
||||||
"age_range": "Which age range are you in?",
|
"age_range": "Which age range are you in?",
|
||||||
"age_for_personalization":
|
"age_for_personalization": "We’ll personalize your learning experience based on your age.",
|
||||||
"We’ll personalize your learning experience based on your age.",
|
|
||||||
"educational_background": "What’s your current educational level?",
|
"educational_background": "What’s your current educational level?",
|
||||||
"education_for_personalization":
|
"education_for_personalization": "This helps us tailor your lessons to your experience.",
|
||||||
"This helps us tailor your lessons to your experience.",
|
|
||||||
"your_occupation": "What’s your occupation?",
|
"your_occupation": "What’s your occupation?",
|
||||||
"occupation_for_personalization":
|
"occupation_for_personalization": "We’ll personalize your learning experience based on your occupation.",
|
||||||
"We’ll personalize your learning experience based on your occupation.",
|
|
||||||
"location": "Where are you from?",
|
"location": "Where are you from?",
|
||||||
"select_country_region": "Select your country and region from the dropdown",
|
"select_country_region": "Select your country and region from the dropdown",
|
||||||
"select_country": "Select country",
|
"select_country": "Select country",
|
||||||
|
|
@ -391,13 +374,10 @@ class CodegenLoader extends AssetLoader {
|
||||||
"evey_one_has_strugle": "Everyone has struggles, let’s start fixing yours",
|
"evey_one_has_strugle": "Everyone has struggles, let’s start fixing yours",
|
||||||
"write_your_challenge": "Write your challenge…",
|
"write_your_challenge": "Write your challenge…",
|
||||||
"topic_interest": "Which topics interest you most?",
|
"topic_interest": "Which topics interest you most?",
|
||||||
"favourite_topic":
|
"favourite_topic": "Your favorite topics help us create fun, relatable lessons.",
|
||||||
"Your favorite topics help us create fun, relatable lessons.",
|
|
||||||
"your_interest": "Write your interest…",
|
"your_interest": "Write your interest…",
|
||||||
"want_quick_assessment":
|
"want_quick_assessment": "Want a quick assessment to know your English level?",
|
||||||
"Want a quick assessment to know your English level?",
|
"answer_quick_questions": "Answer a few quick questions to help us understand your English proficiency.",
|
||||||
"answer_quick_questions":
|
|
||||||
"Answer a few quick questions to help us understand your English proficiency.",
|
|
||||||
"skip": "Skip",
|
"skip": "Skip",
|
||||||
"finish_level": "Finish Level",
|
"finish_level": "Finish Level",
|
||||||
"likely_speaker": "You’re likely speaker of",
|
"likely_speaker": "You’re likely speaker of",
|
||||||
|
|
@ -406,11 +386,7 @@ class CodegenLoader extends AssetLoader {
|
||||||
"welcome_abroad": "Welcome aboard",
|
"welcome_abroad": "Welcome aboard",
|
||||||
"ready_to_explore": "You’re ready to explore your personalized lessons.",
|
"ready_to_explore": "You’re ready to explore your personalized lessons.",
|
||||||
"finish": "Finish",
|
"finish": "Finish",
|
||||||
"finish_all_practice":
|
"finish_all_practice": "Finish all the practices in the lessons to take this practice"
|
||||||
"Finish all the practices in the lessons to take this practice"
|
};
|
||||||
};
|
static const Map<String, Map<String,dynamic>> mapLocales = {"am": _am, "en": _en};
|
||||||
static const Map<String, Map<String, dynamic>> mapLocales = {
|
|
||||||
"am": _am,
|
|
||||||
"en": _en
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -161,8 +161,7 @@ abstract class LocaleKeys {
|
||||||
static const educational_background = 'educational_background';
|
static const educational_background = 'educational_background';
|
||||||
static const education_for_personalization = 'education_for_personalization';
|
static const education_for_personalization = 'education_for_personalization';
|
||||||
static const your_occupation = 'your_occupation';
|
static const your_occupation = 'your_occupation';
|
||||||
static const occupation_for_personalization =
|
static const occupation_for_personalization = 'occupation_for_personalization';
|
||||||
'occupation_for_personalization';
|
|
||||||
static const location = 'location';
|
static const location = 'location';
|
||||||
static const select_country_region = 'select_country_region';
|
static const select_country_region = 'select_country_region';
|
||||||
static const select_country = 'select_country';
|
static const select_country = 'select_country';
|
||||||
|
|
@ -187,4 +186,5 @@ abstract class LocaleKeys {
|
||||||
static const ready_to_explore = 'ready_to_explore';
|
static const ready_to_explore = 'ready_to_explore';
|
||||||
static const finish = 'finish';
|
static const finish = 'finish';
|
||||||
static const finish_all_practice = 'finish_all_practice';
|
static const finish_all_practice = 'finish_all_practice';
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,12 +30,13 @@ class AccountPrivacyView extends StackedView<AccountPrivacyViewModel> {
|
||||||
body: _buildScaffoldContainer(viewModel),
|
body: _buildScaffoldContainer(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildScaffoldContainer(AccountPrivacyViewModel viewModel) =>
|
|
||||||
Container(
|
Widget _buildScaffoldContainer( AccountPrivacyViewModel viewModel) => Container(
|
||||||
decoration: bgDecoration,
|
decoration: bgDecoration,
|
||||||
child: _buildScaffold(viewModel),
|
child: _buildScaffold( viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
Widget _buildScaffold(AccountPrivacyViewModel viewModel) =>
|
Widget _buildScaffold(AccountPrivacyViewModel viewModel) =>
|
||||||
SafeArea(child: _buildBodyWrapper(viewModel));
|
SafeArea(child: _buildBodyWrapper(viewModel));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,12 @@ class ArifPayView extends StackedView<ArifPayViewModel> {
|
||||||
|
|
||||||
const ArifPayView({Key? key, required this.phone}) : super(key: key);
|
const ArifPayView({Key? key, required this.phone}) : super(key: key);
|
||||||
|
|
||||||
|
|
||||||
void _error(ArifPayViewModel viewModel) => viewModel.pop();
|
void _error(ArifPayViewModel viewModel) => viewModel.pop();
|
||||||
|
|
||||||
|
|
||||||
Future<void> _success(ArifPayViewModel viewModel) async {
|
Future<void> _success(ArifPayViewModel viewModel) async {
|
||||||
|
|
||||||
await viewModel.updatePaymentStatus();
|
await viewModel.updatePaymentStatus();
|
||||||
|
|
||||||
await viewModel.replaceWithHome();
|
await viewModel.replaceWithHome();
|
||||||
|
|
@ -46,6 +49,8 @@ class ArifPayView extends StackedView<ArifPayViewModel> {
|
||||||
? const PageLoadingIndicator()
|
? const PageLoadingIndicator()
|
||||||
: _buildScaffold(viewModel);
|
: _buildScaffold(viewModel);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Widget _buildScaffold(ArifPayViewModel viewModel) =>
|
Widget _buildScaffold(ArifPayViewModel viewModel) =>
|
||||||
SafeArea(child: _buildBody(viewModel));
|
SafeArea(child: _buildBody(viewModel));
|
||||||
|
|
||||||
|
|
@ -54,8 +59,7 @@ class ArifPayView extends StackedView<ArifPayViewModel> {
|
||||||
URLRequest(url: WebUri(viewModel.request?.paymentUrl ?? '')),
|
URLRequest(url: WebUri(viewModel.request?.paymentUrl ?? '')),
|
||||||
onUpdateVisitedHistory: (controller, url, androidIsReload) async {
|
onUpdateVisitedHistory: (controller, url, androidIsReload) async {
|
||||||
if (url.toString().contains(kSuccessUrl)) {
|
if (url.toString().contains(kSuccessUrl)) {
|
||||||
showSuccessToast(
|
showSuccessToast('Subscription successful, activation in progress!');
|
||||||
'Subscription successful, activation in progress!');
|
|
||||||
_success(viewModel);
|
_success(viewModel);
|
||||||
} else if (url.toString().contains(kErrorUrl)) {
|
} else if (url.toString().contains(kErrorUrl)) {
|
||||||
_error(viewModel);
|
_error(viewModel);
|
||||||
|
|
|
||||||
|
|
@ -68,11 +68,11 @@ class ArifPayViewModel extends ReactiveViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update payment status
|
// Update payment status
|
||||||
Future<void> updatePaymentStatus() async =>
|
Future<void> updatePaymentStatus() async => await runBusyFuture(_updatePaymentStatus(),
|
||||||
await runBusyFuture(_updatePaymentStatus(),
|
|
||||||
busyObject: StateObjects.learnSubscription);
|
busyObject: StateObjects.learnSubscription);
|
||||||
|
|
||||||
Future<void> _updatePaymentStatus() async {
|
Future<void> _updatePaymentStatus() async {
|
||||||
|
|
||||||
Map<String, dynamic> response = {};
|
Map<String, dynamic> response = {};
|
||||||
|
|
||||||
response = await _apiService.getProfileData(_user?.userId);
|
response = await _apiService.getProfileData(_user?.userId);
|
||||||
|
|
@ -80,6 +80,10 @@ class ArifPayViewModel extends ReactiveViewModel {
|
||||||
if (response['status'] == ResponseStatus.success) {
|
if (response['status'] == ResponseStatus.success) {
|
||||||
User user = response['data'] as User;
|
User user = response['data'] as User;
|
||||||
await _authenticationService.saveUserData(user);
|
await _authenticationService.saveUserData(user);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,8 @@ class CallSupportView extends StackedView<CallSupportViewModel> {
|
||||||
body: _buildScaffoldContainer(viewModel),
|
body: _buildScaffoldContainer(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildScaffoldContainer(CallSupportViewModel viewModel) => Container(
|
Widget _buildScaffoldContainer(CallSupportViewModel viewModel) =>
|
||||||
|
Container(
|
||||||
decoration: bgDecoration,
|
decoration: bgDecoration,
|
||||||
child: _buildScaffold(viewModel),
|
child: _buildScaffold(viewModel),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -16,20 +16,20 @@ class LearnCourseView extends StackedView<LearnCourseViewModel> {
|
||||||
|
|
||||||
const LearnCourseView({Key? key, required this.id}) : super(key: key);
|
const LearnCourseView({Key? key, required this.id}) : super(key: key);
|
||||||
|
|
||||||
Future<void> _onPractice(
|
|
||||||
{required BuildContext context,
|
Future<void> _onPractice({required BuildContext context,
|
||||||
required LearnCourse course,
|
required LearnCourse course,
|
||||||
required LearnCourseViewModel viewModel}) async {
|
required LearnCourseViewModel viewModel}) async {
|
||||||
if (course.access?.completedCount == course.access?.totalCount) {
|
if (course.access?.completedCount == course.access?.totalCount) {
|
||||||
await viewModel.navigateToLearnPractice(
|
await viewModel.navigateToLearnPractice(
|
||||||
id: course.id ?? 0, level: course.name ?? '');
|
id: course.id ?? 0,
|
||||||
|
level: course.name ?? '');
|
||||||
} else {
|
} else {
|
||||||
await _showSheet(context: context, viewModel: viewModel);
|
await _showSheet(context: context, viewModel: viewModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _showSheet(
|
Future<void> _showSheet({required BuildContext context,
|
||||||
{required BuildContext context,
|
|
||||||
required LearnCourseViewModel viewModel}) async =>
|
required LearnCourseViewModel viewModel}) async =>
|
||||||
await showModalBottomSheet(
|
await showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
@ -37,6 +37,7 @@ class LearnCourseView extends StackedView<LearnCourseViewModel> {
|
||||||
builder: (_) => _buildSheet(viewModel),
|
builder: (_) => _buildSheet(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onViewModelReady(LearnCourseViewModel viewModel) async {
|
void onViewModelReady(LearnCourseViewModel viewModel) async {
|
||||||
await viewModel.getLearnCourses(id);
|
await viewModel.getLearnCourses(id);
|
||||||
|
|
@ -48,97 +49,90 @@ class LearnCourseView extends StackedView<LearnCourseViewModel> {
|
||||||
LearnCourseViewModel();
|
LearnCourseViewModel();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget builder(
|
Widget builder(BuildContext context,
|
||||||
BuildContext context,
|
|
||||||
LearnCourseViewModel viewModel,
|
LearnCourseViewModel viewModel,
|
||||||
Widget? child,
|
Widget? child,) =>
|
||||||
) =>
|
_buildScaffoldWrapper(context: context,viewModel: viewModel);
|
||||||
_buildScaffoldWrapper(context: context, viewModel: viewModel);
|
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper(
|
Widget _buildScaffoldWrapper({required BuildContext context,
|
||||||
{required BuildContext context,
|
|
||||||
required LearnCourseViewModel viewModel}) =>
|
required LearnCourseViewModel viewModel}) =>
|
||||||
Scaffold(
|
Scaffold(
|
||||||
backgroundColor: kcBackgroundColor,
|
backgroundColor: kcBackgroundColor,
|
||||||
body: _buildScaffoldContainer(context: context, viewModel: viewModel),
|
body: _buildScaffoldContainer(context: context,viewModel: viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildScaffoldContainer(
|
Widget _buildScaffoldContainer({required BuildContext context,
|
||||||
{required BuildContext context,
|
|
||||||
required LearnCourseViewModel viewModel}) =>
|
required LearnCourseViewModel viewModel}) =>
|
||||||
Container(
|
Container(
|
||||||
decoration: bgDecoration,
|
decoration: bgDecoration,
|
||||||
child: _buildScaffold(context: context, viewModel: viewModel),
|
child: _buildScaffold(context: context,viewModel: viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildScaffold(
|
Widget _buildScaffold({required BuildContext context,
|
||||||
{required BuildContext context,
|
|
||||||
required LearnCourseViewModel viewModel}) =>
|
required LearnCourseViewModel viewModel}) =>
|
||||||
SafeArea(child: _buildBody(context: context, viewModel: viewModel));
|
SafeArea(child: _buildBody(context: context,viewModel: viewModel));
|
||||||
|
|
||||||
Widget _buildBody(
|
Widget _buildBody({required BuildContext context,
|
||||||
{required BuildContext context,
|
|
||||||
required LearnCourseViewModel viewModel}) =>
|
required LearnCourseViewModel viewModel}) =>
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildColumn(context: context, viewModel: viewModel),
|
child: _buildColumn(context: context,viewModel: viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildColumn(
|
Widget _buildColumn({required BuildContext context,
|
||||||
{required BuildContext context,
|
|
||||||
required LearnCourseViewModel viewModel}) =>
|
required LearnCourseViewModel viewModel}) =>
|
||||||
Column(
|
Column(
|
||||||
children: [
|
children: [
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildAppBar(viewModel),
|
_buildAppBar(viewModel),
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildCoursesColumnWrapper(context: context, viewModel: viewModel)
|
_buildCoursesColumnWrapper(context: context,viewModel: viewModel)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAppBar(LearnCourseViewModel viewModel) => SmallAppBar(
|
Widget _buildAppBar(LearnCourseViewModel viewModel) =>
|
||||||
|
SmallAppBar(
|
||||||
onPop: viewModel.pop,
|
onPop: viewModel.pop,
|
||||||
showBackButton: true,
|
showBackButton: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildCoursesColumnWrapper(
|
Widget _buildCoursesColumnWrapper({required BuildContext context,
|
||||||
{required BuildContext context,
|
|
||||||
required LearnCourseViewModel viewModel}) =>
|
required LearnCourseViewModel viewModel}) =>
|
||||||
Expanded(
|
Expanded(child: _buildLevelsColumnScrollView(context: context,viewModel: viewModel));
|
||||||
child: _buildLevelsColumnScrollView(
|
|
||||||
context: context, viewModel: viewModel));
|
|
||||||
|
|
||||||
Widget _buildLevelsColumnScrollView(
|
Widget _buildLevelsColumnScrollView({required BuildContext context,
|
||||||
{required BuildContext context,
|
|
||||||
required LearnCourseViewModel viewModel}) =>
|
required LearnCourseViewModel viewModel}) =>
|
||||||
SingleChildScrollView(
|
SingleChildScrollView(
|
||||||
child: _buildListViewBuilder(context: context, viewModel: viewModel),
|
child: _buildListViewBuilder(context: context,viewModel: viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildListViewBuilder(
|
Widget _buildListViewBuilder({required BuildContext context,
|
||||||
{required BuildContext context,
|
|
||||||
required LearnCourseViewModel viewModel}) =>
|
required LearnCourseViewModel viewModel}) =>
|
||||||
viewModel.busy(StateObjects.learnCourses)
|
viewModel.busy(StateObjects.learnCourses)
|
||||||
? _buildProgressIndicator()
|
? _buildProgressIndicator()
|
||||||
: _buildListView(context: context, viewModel: viewModel);
|
: _buildListView(context: context,viewModel: viewModel);
|
||||||
|
|
||||||
Widget _buildProgressIndicator() => const Center(
|
Widget _buildProgressIndicator() =>
|
||||||
|
const Center(
|
||||||
child: CustomCircularProgressIndicator(color: kcPrimaryColor),
|
child: CustomCircularProgressIndicator(color: kcPrimaryColor),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildListView(
|
Widget _buildListView({required BuildContext context,
|
||||||
{required BuildContext context,
|
|
||||||
required LearnCourseViewModel viewModel}) =>
|
required LearnCourseViewModel viewModel}) =>
|
||||||
ListView.separated(
|
ListView.separated(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: viewModel.courses.length,
|
itemCount: viewModel.courses.length,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
itemBuilder: (context, index) => _buildTile(
|
itemBuilder: (context, index) =>
|
||||||
|
_buildTile(
|
||||||
course: viewModel.courses[index],
|
course: viewModel.courses[index],
|
||||||
onPracticeTap: () async => await _onPractice(
|
|
||||||
|
onPracticeTap: () async =>
|
||||||
|
await _onPractice(
|
||||||
context: context,
|
context: context,
|
||||||
viewModel: viewModel,
|
viewModel: viewModel,
|
||||||
course: viewModel.courses[index]),
|
course: viewModel.courses[index]
|
||||||
|
),
|
||||||
onViewTap: () async =>
|
onViewTap: () async =>
|
||||||
await viewModel.navigateToLearnModule(viewModel.courses[index]),
|
await viewModel.navigateToLearnModule(viewModel.courses[index]),
|
||||||
),
|
),
|
||||||
|
|
@ -156,6 +150,7 @@ class LearnCourseView extends StackedView<LearnCourseViewModel> {
|
||||||
onPracticeTap: onPracticeTap,
|
onPracticeTap: onPracticeTap,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
Widget _buildSheet(LearnCourseViewModel viewModel) =>
|
Widget _buildSheet(LearnCourseViewModel viewModel) =>
|
||||||
FinishPracticeSheet(onTap: viewModel.pop);
|
FinishPracticeSheet(onTap: viewModel.pop);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ import 'package:yimaru_app/ui/widgets/motivation_card.dart';
|
||||||
import '../../common/app_colors.dart';
|
import '../../common/app_colors.dart';
|
||||||
import '../../common/ui_helpers.dart';
|
import '../../common/ui_helpers.dart';
|
||||||
import '../../widgets/custom_circular_progress_indicator.dart';
|
import '../../widgets/custom_circular_progress_indicator.dart';
|
||||||
import '../../widgets/finish_practice_sheet.dart';
|
|
||||||
import '../../widgets/small_app_bar.dart';
|
import '../../widgets/small_app_bar.dart';
|
||||||
import 'learn_lesson_viewmodel.dart';
|
import 'learn_lesson_viewmodel.dart';
|
||||||
|
|
||||||
|
|
@ -24,15 +23,11 @@ class LearnLessonView extends StackedView<LearnLessonViewModel> {
|
||||||
Future<void> _onPractice(
|
Future<void> _onPractice(
|
||||||
{required int index,
|
{required int index,
|
||||||
required LearnLesson lesson,
|
required LearnLesson lesson,
|
||||||
required BuildContext context,
|
|
||||||
required LearnLessonViewModel viewModel}) async {
|
required LearnLessonViewModel viewModel}) async {
|
||||||
if (lesson.access?.isAccessible ?? false) {
|
if (index > 1) {
|
||||||
await viewModel.navigateToLearnPractice(lesson.id ?? 0);
|
|
||||||
} else {
|
|
||||||
await _showSheet(context: context, viewModel: viewModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if (index > 1) {
|
print(index);
|
||||||
|
print(viewModel.user?.subscriptionStatus);
|
||||||
if (viewModel.user?.subscriptionStatus?.toLowerCase() == 'subscribed') {
|
if (viewModel.user?.subscriptionStatus?.toLowerCase() == 'subscribed') {
|
||||||
await viewModel.navigateToLearnPractice(lesson.id ?? 0);
|
await viewModel.navigateToLearnPractice(lesson.id ?? 0);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -40,17 +35,8 @@ class LearnLessonView extends StackedView<LearnLessonViewModel> {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await viewModel.navigateToLearnPractice(lesson.id ?? 0);
|
await viewModel.navigateToLearnPractice(lesson.id ?? 0);
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Future<void> _showSheet(
|
|
||||||
{required BuildContext context,
|
|
||||||
required LearnLessonViewModel viewModel}) async =>
|
|
||||||
await showModalBottomSheet(
|
|
||||||
context: context,
|
|
||||||
backgroundColor: kcTransparent,
|
|
||||||
builder: (_) => _buildSheet(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onViewModelReady(LearnLessonViewModel viewModel) async {
|
void onViewModelReady(LearnLessonViewModel viewModel) async {
|
||||||
|
|
@ -162,7 +148,7 @@ class LearnLessonView extends StackedView<LearnLessonViewModel> {
|
||||||
verticalSpaceLarge,
|
verticalSpaceLarge,
|
||||||
_buildHeader(),
|
_buildHeader(),
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildListViewBuilder(context: context,viewModel: viewModel),
|
_buildListViewBuilder(viewModel),
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildTitle() => Text(
|
Widget _buildTitle() => Text(
|
||||||
|
|
@ -197,18 +183,16 @@ class LearnLessonView extends StackedView<LearnLessonViewModel> {
|
||||||
style: style18DG700,
|
style: style18DG700,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildListViewBuilder( {required BuildContext context,
|
Widget _buildListViewBuilder(LearnLessonViewModel viewModel) =>
|
||||||
required LearnLessonViewModel viewModel}) =>
|
|
||||||
viewModel.busy(StateObjects.learnLessons)
|
viewModel.busy(StateObjects.learnLessons)
|
||||||
? _buildProgressIndicator()
|
? _buildProgressIndicator()
|
||||||
: _buildListView(context: context,viewModel: viewModel);
|
: _buildListView(viewModel);
|
||||||
|
|
||||||
Widget _buildProgressIndicator() => const Center(
|
Widget _buildProgressIndicator() => const Center(
|
||||||
child: CustomCircularProgressIndicator(color: kcPrimaryColor),
|
child: CustomCircularProgressIndicator(color: kcPrimaryColor),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildListView( {required BuildContext context,
|
Widget _buildListView(LearnLessonViewModel viewModel) => ListView.builder(
|
||||||
required LearnLessonViewModel viewModel}) => ListView.builder(
|
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: viewModel.lessons.length,
|
itemCount: viewModel.lessons.length,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
|
@ -217,12 +201,10 @@ required LearnLessonViewModel viewModel}) => ListView.builder(
|
||||||
lesson: viewModel.lessons[index],
|
lesson: viewModel.lessons[index],
|
||||||
onPracticeTap: () async => await _onPractice(
|
onPracticeTap: () async => await _onPractice(
|
||||||
index: index,
|
index: index,
|
||||||
context: context,
|
|
||||||
viewModel: viewModel,
|
viewModel: viewModel,
|
||||||
lesson: viewModel.lessons[index],
|
lesson: viewModel.lessons[index],
|
||||||
),
|
),
|
||||||
onLessonTap: () async => await viewModel.navigateToLearnLessonDetail(
|
onLessonTap: () async => await viewModel.navigateToLearnLessonDetail(
|
||||||
index: index,
|
|
||||||
module: module,
|
module: module,
|
||||||
lesson: viewModel.lessons[index],
|
lesson: viewModel.lessons[index],
|
||||||
hasPractice:
|
hasPractice:
|
||||||
|
|
@ -242,7 +224,4 @@ required LearnLessonViewModel viewModel}) => ListView.builder(
|
||||||
onLessonTap: onLessonTap,
|
onLessonTap: onLessonTap,
|
||||||
onPracticeTap: onPracticeTap,
|
onPracticeTap: onPracticeTap,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildSheet(LearnLessonViewModel viewModel) =>
|
|
||||||
FinishPracticeSheet(onTap: viewModel.pop);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,12 @@ class LearnLessonViewModel extends ReactiveViewModel {
|
||||||
|
|
||||||
final _authenticationService = locator<AuthenticationService>();
|
final _authenticationService = locator<AuthenticationService>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<ListenableServiceMixin> get listenableServices =>
|
List<ListenableServiceMixin> get listenableServices => [_learnService,_authenticationService];
|
||||||
[_learnService, _authenticationService];
|
|
||||||
|
|
||||||
// Current user
|
// Current user
|
||||||
User? get _user => _authenticationService.user;
|
User? get _user => _authenticationService.user;
|
||||||
|
|
@ -46,6 +49,8 @@ class LearnLessonViewModel extends ReactiveViewModel {
|
||||||
// Navigation
|
// Navigation
|
||||||
void pop() => _navigationService.back();
|
void pop() => _navigationService.back();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Future<void> navigateToLearnPractice(int id) async =>
|
Future<void> navigateToLearnPractice(int id) async =>
|
||||||
await _navigationService.navigateToLearnPracticeView(
|
await _navigationService.navigateToLearnPracticeView(
|
||||||
id: id,
|
id: id,
|
||||||
|
|
@ -55,17 +60,15 @@ class LearnLessonViewModel extends ReactiveViewModel {
|
||||||
subtitle: LocaleKeys.ask_you_few_actions.tr(),
|
subtitle: LocaleKeys.ask_you_few_actions.tr(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
Future<void> navigateToLearnSubscription() async =>
|
Future<void> navigateToLearnSubscription() async =>
|
||||||
await _navigationService.navigateToLearnSubscriptionView();
|
await _navigationService.navigateToLearnSubscriptionView();
|
||||||
|
|
||||||
Future<void> navigateToLearnLessonDetail(
|
Future<void> navigateToLearnLessonDetail(
|
||||||
{
|
{required bool hasPractice,
|
||||||
required int index,
|
|
||||||
required bool hasPractice,
|
|
||||||
required LearnLesson lesson,
|
required LearnLesson lesson,
|
||||||
required LearnModule module}) async =>
|
required LearnModule module}) async =>
|
||||||
await _navigationService.navigateToLearnLessonDetailView(
|
await _navigationService.navigateToLearnLessonDetailView(
|
||||||
index: index,
|
|
||||||
lesson: lesson, module: module, hasPractice: hasPractice);
|
lesson: lesson, module: module, hasPractice: hasPractice);
|
||||||
|
|
||||||
// Remote api call
|
// Remote api call
|
||||||
|
|
@ -85,7 +88,6 @@ class LearnLessonViewModel extends ReactiveViewModel {
|
||||||
LearnModule? getUpdatedLearnModule(int id) {
|
LearnModule? getUpdatedLearnModule(int id) {
|
||||||
return _learnService.getLearnModuleById(id);
|
return _learnService.getLearnModuleById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Refresh image
|
//Refresh image
|
||||||
Future<void> refreshLessonImages(List<LearnLesson> lessons) async {
|
Future<void> refreshLessonImages(List<LearnLesson> lessons) async {
|
||||||
for (final lesson in lessons) {
|
for (final lesson in lessons) {
|
||||||
|
|
|
||||||
|
|
@ -15,33 +15,20 @@ 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 int index;
|
|
||||||
final bool hasPractice;
|
final bool hasPractice;
|
||||||
final LearnModule module;
|
final LearnModule module;
|
||||||
final LearnLesson lesson;
|
final LearnLesson lesson;
|
||||||
|
|
||||||
const LearnLessonDetailView(
|
const LearnLessonDetailView(
|
||||||
{Key? key,
|
{Key? key,
|
||||||
required this.index,
|
|
||||||
required this.lesson,
|
required this.lesson,
|
||||||
required this.module,
|
required this.module,
|
||||||
required this.hasPractice})
|
required this.hasPractice})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
Future<void> _onPractice(
|
Future<void> _navigate(LearnLessonDetailViewModel viewModel) async {
|
||||||
{required LearnLesson lesson,
|
|
||||||
required LearnLessonDetailViewModel viewModel}) async {
|
|
||||||
await viewModel.pause();
|
await viewModel.pause();
|
||||||
await viewModel.navigateToLearnPractice(lesson.id ?? 0);
|
await viewModel.navigateToLearnPractice(lesson.id ?? 0);
|
||||||
/*if (index > 1) {
|
|
||||||
if (viewModel.user?.subscriptionStatus?.toLowerCase() == 'subscribed') {
|
|
||||||
await viewModel.navigateToLearnPractice(lesson.id ?? 0);
|
|
||||||
} else {
|
|
||||||
await viewModel.navigateToLearnSubscription();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
await viewModel.navigateToLearnPractice(lesson.id ?? 0);
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -206,7 +193,6 @@ class LearnLessonDetailView extends StackedView<LearnLessonDetailViewModel> {
|
||||||
foregroundColor: kcWhite,
|
foregroundColor: kcWhite,
|
||||||
backgroundColor: kcPrimaryColor,
|
backgroundColor: kcPrimaryColor,
|
||||||
text: LocaleKeys.take_practice.tr(),
|
text: LocaleKeys.take_practice.tr(),
|
||||||
onTap: () async =>
|
onTap: () async => await _navigate(viewModel),
|
||||||
await _onPractice(lesson: lesson, viewModel: viewModel),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,7 @@ import 'package:yimaru_app/ui/common/enmus.dart';
|
||||||
import '../../../app/app.locator.dart';
|
import '../../../app/app.locator.dart';
|
||||||
import '../../../app/app.router.dart';
|
import '../../../app/app.router.dart';
|
||||||
import '../../../models/learn_lesson.dart';
|
import '../../../models/learn_lesson.dart';
|
||||||
import '../../../models/user.dart';
|
|
||||||
import '../../../services/api_service.dart';
|
import '../../../services/api_service.dart';
|
||||||
import '../../../services/authentication_service.dart';
|
|
||||||
import '../../../services/learn_service.dart';
|
import '../../../services/learn_service.dart';
|
||||||
import '../../../services/status_checker_service.dart';
|
import '../../../services/status_checker_service.dart';
|
||||||
import '../../../services/vimeo_service.dart';
|
import '../../../services/vimeo_service.dart';
|
||||||
|
|
@ -28,16 +26,8 @@ class LearnLessonDetailViewModel extends ReactiveViewModel {
|
||||||
|
|
||||||
final _navigationService = locator<NavigationService>();
|
final _navigationService = locator<NavigationService>();
|
||||||
|
|
||||||
final _authenticationService = locator<AuthenticationService>();
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<ListenableServiceMixin> get listenableServices =>
|
List<ListenableServiceMixin> get listenableServices => [_learnService];
|
||||||
[_learnService, _authenticationService];
|
|
||||||
|
|
||||||
// Current user
|
|
||||||
User? get _user => _authenticationService.user;
|
|
||||||
|
|
||||||
User? get user => _user;
|
|
||||||
|
|
||||||
// Learn lessons
|
// Learn lessons
|
||||||
List<LearnLesson> get _lessons => _learnService.lessons;
|
List<LearnLesson> get _lessons => _learnService.lessons;
|
||||||
|
|
@ -131,9 +121,6 @@ class LearnLessonDetailViewModel extends ReactiveViewModel {
|
||||||
// Navigation
|
// Navigation
|
||||||
void pop() => _navigationService.back();
|
void pop() => _navigationService.back();
|
||||||
|
|
||||||
Future<void> navigateToLearnSubscription() async =>
|
|
||||||
await _navigationService.navigateToLearnSubscriptionView();
|
|
||||||
|
|
||||||
Future<void> navigateToLearnPractice(int id) async =>
|
Future<void> navigateToLearnPractice(int id) async =>
|
||||||
await _navigationService.navigateToLearnPracticeView(
|
await _navigationService.navigateToLearnPracticeView(
|
||||||
id: id,
|
id: id,
|
||||||
|
|
|
||||||
|
|
@ -151,14 +151,11 @@ class LearnModuleView extends StackedView<LearnModuleViewModel> {
|
||||||
style: style14P400,
|
style: style14P400,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildOverallProgress(LearnModuleViewModel viewModel) =>
|
Widget _buildOverallProgress(LearnModuleViewModel viewModel) => OverallProgress(
|
||||||
OverallProgress(
|
|
||||||
indicatorBackgroundColor: kcWhite,
|
indicatorBackgroundColor: kcWhite,
|
||||||
backgroundColor: kcPrimaryColor.withOpacity(0.1),
|
backgroundColor: kcPrimaryColor.withOpacity(0.1),
|
||||||
progress: (viewModel.getUpdatedLearnCourse(course.id ?? 0) ?? course)
|
progress:
|
||||||
.access
|
(viewModel.getUpdatedLearnCourse(course.id ?? 0) ?? course).access?.progressPercent ?? 0,
|
||||||
?.progressPercent ??
|
|
||||||
0,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildListViewBuilder(
|
Widget _buildListViewBuilder(
|
||||||
|
|
|
||||||
|
|
@ -86,4 +86,5 @@ class LearnModuleViewModel extends ReactiveViewModel {
|
||||||
|
|
||||||
String getModuleImage(LearnModule module) =>
|
String getModuleImage(LearnModule module) =>
|
||||||
getReadableUrl(_refreshedIcons[module.id] ?? '') ?? '';
|
getReadableUrl(_refreshedIcons[module.id] ?? '') ?? '';
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -169,6 +169,7 @@ class InteractLearnPracticeScreen
|
||||||
Widget _buildSpeakingIndicator(LearnPracticeViewModel viewModel) =>
|
Widget _buildSpeakingIndicator(LearnPracticeViewModel viewModel) =>
|
||||||
WaveWrapper(height: 200, child: _buildSpinnerState(viewModel));
|
WaveWrapper(height: 200, child: _buildSpinnerState(viewModel));
|
||||||
|
|
||||||
|
|
||||||
Widget _buildSpinnerState(LearnPracticeViewModel viewModel) =>
|
Widget _buildSpinnerState(LearnPracticeViewModel viewModel) =>
|
||||||
viewModel.busy(StateObjects.recordLearnPracticeAnswer) ||
|
viewModel.busy(StateObjects.recordLearnPracticeAnswer) ||
|
||||||
viewModel.busy(StateObjects.finishLearnPracticeQuestion)
|
viewModel.busy(StateObjects.finishLearnPracticeQuestion)
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ class LearnPracticeAppreciationScreen
|
||||||
extends ViewModelWidget<LearnPracticeViewModel> {
|
extends ViewModelWidget<LearnPracticeViewModel> {
|
||||||
const LearnPracticeAppreciationScreen({super.key});
|
const LearnPracticeAppreciationScreen({super.key});
|
||||||
|
|
||||||
|
|
||||||
Future<void> _cancel(LearnPracticeViewModel viewModel) async {
|
Future<void> _cancel(LearnPracticeViewModel viewModel) async {
|
||||||
await viewModel.stopRecording();
|
await viewModel.stopRecording();
|
||||||
viewModel.pop();
|
viewModel.pop();
|
||||||
|
|
|
||||||
|
|
@ -163,10 +163,13 @@ class LearnPracticeDescriptionScreen
|
||||||
Widget _buildImage(LearnPracticeViewModel viewModel) => CachedNetworkImage(
|
Widget _buildImage(LearnPracticeViewModel viewModel) => CachedNetworkImage(
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
width: double.maxFinite,
|
width: double.maxFinite,
|
||||||
|
|
||||||
imageUrl:
|
imageUrl:
|
||||||
getReadableUrl(viewModel.practices.first.storyImage ?? '') ?? '',
|
getReadableUrl( viewModel.practices.first.storyImage ?? '') ?? '',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Widget _buildContinueButtonWrapper(LearnPracticeViewModel viewModel) =>
|
Widget _buildContinueButtonWrapper(LearnPracticeViewModel viewModel) =>
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 50),
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
|
|
|
||||||
|
|
@ -126,8 +126,7 @@ class OnboardingView extends StackedView<OnboardingViewModel>
|
||||||
|
|
||||||
Widget _buildOccupationForm() => const OccupationFormScreen();
|
Widget _buildOccupationForm() => const OccupationFormScreen();
|
||||||
|
|
||||||
Widget _buildCountryRegionForm() =>
|
Widget _buildCountryRegionForm() => CountryRegionFormScreen(regionController: regionController);
|
||||||
CountryRegionFormScreen(regionController: regionController);
|
|
||||||
|
|
||||||
Widget _buildLearningGoalForm() => const LearningGoalFormScreen();
|
Widget _buildLearningGoalForm() => const LearningGoalFormScreen();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ class LearningGoalFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void _pop(OnboardingViewModel viewModel) {
|
void _pop(OnboardingViewModel viewModel) {
|
||||||
viewModel.resetLearningGoalFormScreen();
|
viewModel.resetLearningGoalFormScreen();
|
||||||
viewModel.goBack();
|
viewModel.goBack();
|
||||||
|
|
@ -69,17 +70,20 @@ class LearningGoalFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
||||||
Widget build(BuildContext context, OnboardingViewModel viewModel) =>
|
Widget build(BuildContext context, OnboardingViewModel viewModel) =>
|
||||||
_buildScaffoldWrapper(viewModel);
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper(OnboardingViewModel viewModel) => Scaffold(
|
Widget _buildScaffoldWrapper(OnboardingViewModel viewModel) =>
|
||||||
|
Scaffold(
|
||||||
backgroundColor: kcBackgroundColor,
|
backgroundColor: kcBackgroundColor,
|
||||||
body: _buildScaffold(viewModel),
|
body: _buildScaffold(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildScaffold(OnboardingViewModel viewModel) => Column(
|
Widget _buildScaffold(OnboardingViewModel viewModel) =>
|
||||||
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: _buildScaffoldChildren(viewModel),
|
children: _buildScaffoldChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildScaffoldChildren(OnboardingViewModel viewModel) => [
|
List<Widget> _buildScaffoldChildren(OnboardingViewModel viewModel) =>
|
||||||
|
[
|
||||||
_buildAppBar(viewModel),
|
_buildAppBar(viewModel),
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildExpandedBody(viewModel)
|
_buildExpandedBody(viewModel)
|
||||||
|
|
@ -93,12 +97,14 @@ class LearningGoalFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
||||||
child: _buildBodyWrapper(viewModel),
|
child: _buildBodyWrapper(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildBodyWrapper(OnboardingViewModel viewModel) => Padding(
|
Widget _buildBodyWrapper(OnboardingViewModel viewModel) =>
|
||||||
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildBody(viewModel),
|
child: _buildBody(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildBody(OnboardingViewModel viewModel) => Column(
|
Widget _buildBody(OnboardingViewModel viewModel) =>
|
||||||
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: _buildBodyChildren(viewModel),
|
children: _buildBodyChildren(viewModel),
|
||||||
|
|
@ -107,20 +113,23 @@ class LearningGoalFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
||||||
List<Widget> _buildBodyChildren(OnboardingViewModel viewModel) =>
|
List<Widget> _buildBodyChildren(OnboardingViewModel viewModel) =>
|
||||||
[_buildUpperColumn(viewModel), _buildContinueButtonWrapper(viewModel)];
|
[_buildUpperColumn(viewModel), _buildContinueButtonWrapper(viewModel)];
|
||||||
|
|
||||||
Widget _buildUpperColumn(OnboardingViewModel viewModel) => Column(
|
Widget _buildUpperColumn(OnboardingViewModel viewModel) =>
|
||||||
|
Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: _buildUpperColumnChildren(viewModel),
|
children: _buildUpperColumnChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildUpperColumnChildren(OnboardingViewModel viewModel) => [
|
List<Widget> _buildUpperColumnChildren(OnboardingViewModel viewModel) =>
|
||||||
|
[
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildTitle(viewModel),
|
_buildTitle(viewModel),
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildLearningGoals(viewModel)
|
_buildLearningGoals(viewModel)
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildAppBar(OnboardingViewModel viewModel) => LargeAppBar(
|
Widget _buildAppBar(OnboardingViewModel viewModel) =>
|
||||||
|
LargeAppBar(
|
||||||
showBackButton: true,
|
showBackButton: true,
|
||||||
showLanguageSelection: true,
|
showLanguageSelection: true,
|
||||||
onPop: () => _pop(viewModel),
|
onPop: () => _pop(viewModel),
|
||||||
|
|
@ -130,7 +139,8 @@ class LearningGoalFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
||||||
: viewModel.selectedLanguage['code'],
|
: viewModel.selectedLanguage['code'],
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildTitle(OnboardingViewModel viewModel) => Text.rich(
|
Widget _buildTitle(OnboardingViewModel viewModel) =>
|
||||||
|
Text.rich(
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text:
|
text:
|
||||||
'${LocaleKeys.hello.tr()} ${viewModel.userData['first_name']},',
|
'${LocaleKeys.hello.tr()} ${viewModel.userData['first_name']},',
|
||||||
|
|
@ -143,36 +153,39 @@ class LearningGoalFormScreen extends ViewModelWidget<OnboardingViewModel> {
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildLearningGoals(OnboardingViewModel viewModel) => ListView.builder(
|
Widget _buildLearningGoals(OnboardingViewModel viewModel) =>
|
||||||
|
ListView.builder(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: 3, // viewModel.learningGoals.length,
|
itemCount: 3,// viewModel.learningGoals.length,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
itemBuilder: (context, index) => _buildLearningGoal(
|
itemBuilder: (context, index) =>
|
||||||
|
_buildLearningGoal(
|
||||||
icon: getIcon(index),
|
icon: getIcon(index),
|
||||||
title: getTitles(index),
|
title: getTitles(index),
|
||||||
subtitle: getSubtitle(index),
|
subtitle: getSubtitle(index),
|
||||||
selected:
|
selected:
|
||||||
viewModel.isSelectedLearningGoal(viewModel.learningGoals[index]),
|
viewModel.isSelectedLearningGoal(viewModel.learningGoals[index]),
|
||||||
onTap: () =>
|
onTap: () =>
|
||||||
viewModel.setSelectedLearningGoal(viewModel.learningGoals[index]),
|
viewModel.setSelectedLearningGoal(
|
||||||
|
viewModel.learningGoals[index]),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildLearningGoal(
|
Widget _buildLearningGoal({required String title,
|
||||||
{required String title,
|
|
||||||
required bool selected,
|
required bool selected,
|
||||||
required IconData icon,
|
required IconData icon,
|
||||||
required String subtitle,
|
required String subtitle,
|
||||||
required GestureTapCallback onTap}) =>
|
required GestureTapCallback onTap}) =>
|
||||||
CustomLargeRadioButton(
|
CustomLargeRadioButton(
|
||||||
icon: icon,
|
icon:icon,
|
||||||
title: title,
|
title: title,
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
subtitle: subtitle,
|
subtitle: subtitle,
|
||||||
selected: selected,
|
selected: selected,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildContinueButtonWrapper(OnboardingViewModel viewModel) => Padding(
|
Widget _buildContinueButtonWrapper(OnboardingViewModel viewModel) =>
|
||||||
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 50),
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
child: _buildContinueButton(viewModel),
|
child: _buildContinueButton(viewModel),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,8 @@ class PrivacyPolicyView extends StackedView<PrivacyPolicyViewModel> {
|
||||||
body: _buildScaffoldContainer(viewModel),
|
body: _buildScaffoldContainer(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildScaffoldContainer(PrivacyPolicyViewModel viewModel) => Container(
|
Widget _buildScaffoldContainer(PrivacyPolicyViewModel viewModel) =>
|
||||||
|
Container(
|
||||||
decoration: bgDecoration,
|
decoration: bgDecoration,
|
||||||
child: _buildScaffold(viewModel),
|
child: _buildScaffold(viewModel),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -55,10 +55,8 @@ class ProfileView extends StackedView<ProfileViewModel> {
|
||||||
body: _buildScaffoldContainer(context: context, viewModel: viewModel),
|
body: _buildScaffoldContainer(context: context, viewModel: viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildScaffoldContainer(
|
Widget _buildScaffoldContainer( {required BuildContext context,
|
||||||
{required BuildContext context,
|
required ProfileViewModel viewModel}) => Container(
|
||||||
required ProfileViewModel viewModel}) =>
|
|
||||||
Container(
|
|
||||||
decoration: bgDecoration,
|
decoration: bgDecoration,
|
||||||
child: _buildScaffold(context: context, viewModel: viewModel),
|
child: _buildScaffold(context: context, viewModel: viewModel),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
|
||||||
'birth_day': DateFormat('yyyy-MM-dd').format(DateTime.now()),
|
'birth_day': DateFormat('yyyy-MM-dd').format(DateTime.now()),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
viewModel.addUserData(data);
|
viewModel.addUserData(data);
|
||||||
|
|
||||||
await viewModel.updateProfile();
|
await viewModel.updateProfile();
|
||||||
|
|
@ -109,7 +110,7 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onViewModelReady(ProfileDetailViewModel viewModel) async {
|
void onViewModelReady(ProfileDetailViewModel viewModel) async{
|
||||||
await viewModel.getProfileDetailFields();
|
await viewModel.getProfileDetailFields();
|
||||||
_onModelReady(viewModel);
|
_onModelReady(viewModel);
|
||||||
syncFormWithViewModel(viewModel);
|
syncFormWithViewModel(viewModel);
|
||||||
|
|
@ -143,14 +144,14 @@ class ProfileDetailView extends StackedView<ProfileDetailViewModel>
|
||||||
? const PageLoadingIndicator()
|
? const PageLoadingIndicator()
|
||||||
: _buildScaffoldContainer(context: context, viewModel: viewModel);
|
: _buildScaffoldContainer(context: context, viewModel: viewModel);
|
||||||
|
|
||||||
Widget _buildScaffoldContainer(
|
|
||||||
{required BuildContext context,
|
Widget _buildScaffoldContainer( {required BuildContext context,
|
||||||
required ProfileDetailViewModel viewModel}) =>
|
required ProfileDetailViewModel viewModel}) => Container(
|
||||||
Container(
|
|
||||||
decoration: bgDecoration,
|
decoration: bgDecoration,
|
||||||
child: _buildScaffoldStack(context: context, viewModel: viewModel),
|
child: _buildScaffoldStack(context: context, viewModel: viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
Widget _buildScaffoldStack(
|
Widget _buildScaffoldStack(
|
||||||
{required BuildContext context,
|
{required BuildContext context,
|
||||||
required ProfileDetailViewModel viewModel}) =>
|
required ProfileDetailViewModel viewModel}) =>
|
||||||
|
|
|
||||||
|
|
@ -164,6 +164,7 @@ class ProfileDetailViewModel extends ReactiveViewModel
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void setRegionFocus() {
|
void setRegionFocus() {
|
||||||
_focusRegion = true;
|
_focusRegion = true;
|
||||||
rebuildUi();
|
rebuildUi();
|
||||||
|
|
@ -257,8 +258,7 @@ class ProfileDetailViewModel extends ReactiveViewModel
|
||||||
|
|
||||||
// Profile detail fields
|
// Profile detail fields
|
||||||
Future<void> getProfileDetailFields() async =>
|
Future<void> getProfileDetailFields() async =>
|
||||||
await runBusyFuture(_getProfileDetailFields(),
|
await runBusyFuture(_getProfileDetailFields(),busyObject: StateObjects.profileDetail);
|
||||||
busyObject: StateObjects.profileDetail);
|
|
||||||
|
|
||||||
Future<void> _getProfileDetailFields() async {
|
Future<void> _getProfileDetailFields() async {
|
||||||
await _onboardingService.getProfileDetailFields();
|
await _onboardingService.getProfileDetailFields();
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,8 @@ class SupportView extends StackedView<SupportViewModel> {
|
||||||
body: _buildScaffoldContainer(viewModel),
|
body: _buildScaffoldContainer(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildScaffoldContainer(SupportViewModel viewModel) => Container(
|
Widget _buildScaffoldContainer(SupportViewModel viewModel) =>
|
||||||
|
Container(
|
||||||
decoration: bgDecoration,
|
decoration: bgDecoration,
|
||||||
child: _buildScaffold(viewModel),
|
child: _buildScaffold(viewModel),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ class TelegramSupportView extends StackedView<TelegramSupportViewModel> {
|
||||||
body: _buildScaffoldContainer(viewModel),
|
body: _buildScaffoldContainer(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
Widget _buildScaffoldContainer(TelegramSupportViewModel viewModel) =>
|
Widget _buildScaffoldContainer(TelegramSupportViewModel viewModel) =>
|
||||||
Container(
|
Container(
|
||||||
decoration: bgDecoration,
|
decoration: bgDecoration,
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,8 @@ class LearnCourseTile extends ViewModelWidget<LearnCourseViewModel> {
|
||||||
Widget build(BuildContext context, LearnCourseViewModel viewModel) =>
|
Widget build(BuildContext context, LearnCourseViewModel viewModel) =>
|
||||||
_buildExpansionTileCard(viewModel);
|
_buildExpansionTileCard(viewModel);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Widget _buildExpansionTileCard(LearnCourseViewModel viewModel) => Container(
|
Widget _buildExpansionTileCard(LearnCourseViewModel viewModel) => Container(
|
||||||
margin: const EdgeInsets.only(bottom: 15),
|
margin: const EdgeInsets.only(bottom: 15),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
|
|
||||||
|
|
@ -27,14 +27,12 @@ class LearnLessonTile extends ViewModelWidget<LearnLessonViewModel> {
|
||||||
required this.index,
|
required this.index,
|
||||||
required this.lesson});
|
required this.lesson});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, LearnLessonViewModel viewModel) =>
|
Widget build(BuildContext context, LearnLessonViewModel viewModel) =>
|
||||||
_buildContainerWrapper(viewModel);
|
_buildContainer(viewModel);
|
||||||
|
|
||||||
Widget _buildContainerWrapper(LearnLessonViewModel viewModel)=> GestureDetector(
|
|
||||||
onTap: !(lesson.access?.isAccessible ?? false) ? onPracticeTap:null,
|
|
||||||
child: _buildContainer(viewModel),
|
|
||||||
);
|
|
||||||
Widget _buildContainer(LearnLessonViewModel viewModel) => Container(
|
Widget _buildContainer(LearnLessonViewModel viewModel) => Container(
|
||||||
width: double.maxFinite,
|
width: double.maxFinite,
|
||||||
margin: const EdgeInsets.only(bottom: 15),
|
margin: const EdgeInsets.only(bottom: 15),
|
||||||
|
|
@ -82,8 +80,8 @@ class LearnLessonTile extends ViewModelWidget<LearnLessonViewModel> {
|
||||||
children: _buildExpansionTileChildren(viewModel),
|
children: _buildExpansionTileChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildLeadingWrapper(LearnLessonViewModel viewModel) =>
|
Widget _buildLeadingWrapper(LearnLessonViewModel viewModel) => MiniThumbnail(
|
||||||
MiniThumbnail(thumbnail: getReadableUrl(lesson.thumbnail ?? '') ?? '');
|
thumbnail: getReadableUrl(lesson.thumbnail ?? '') ?? '');
|
||||||
|
|
||||||
Widget _buildTitle() => Text(
|
Widget _buildTitle() => Text(
|
||||||
lesson.title ?? '',
|
lesson.title ?? '',
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@ class LearnModuleTile extends ViewModelWidget<LearnModuleViewModel> {
|
||||||
const LearnModuleTile(
|
const LearnModuleTile(
|
||||||
{super.key, this.onModuleTap, this.onPracticeTap, required this.module});
|
{super.key, this.onModuleTap, this.onPracticeTap, required this.module});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, LearnModuleViewModel viewModel) =>
|
Widget build(BuildContext context, LearnModuleViewModel viewModel) =>
|
||||||
_buildExpansionTileCard(context: context, viewModel: viewModel);
|
_buildExpansionTileCard(context: context, viewModel: viewModel);
|
||||||
|
|
@ -84,17 +86,20 @@ class LearnModuleTile extends ViewModelWidget<LearnModuleViewModel> {
|
||||||
child: _buildIconClipper(viewModel),
|
child: _buildIconClipper(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildIconClipper(LearnModuleViewModel viewModel) => ClipRRect(
|
Widget _buildIconClipper(LearnModuleViewModel viewModel)=> ClipRRect(
|
||||||
child: _buildIcon(viewModel),
|
child: _buildIcon(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildIcon(LearnModuleViewModel viewModel) => CachedNetworkImage(
|
Widget _buildIcon(LearnModuleViewModel viewModel) =>
|
||||||
|
CachedNetworkImage(
|
||||||
width: 25,
|
width: 25,
|
||||||
height: 25,
|
height: 25,
|
||||||
cacheKey: viewModel.getModuleImage(module),
|
cacheKey: viewModel.getModuleImage(module),
|
||||||
imageUrl: viewModel.getModuleImage(module),
|
imageUrl: viewModel.getModuleImage(module),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Widget _buildTitleWrapper() => Padding(
|
Widget _buildTitleWrapper() => Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||||
child: _buildTitle(),
|
child: _buildTitle(),
|
||||||
|
|
@ -209,6 +214,8 @@ class LearnModuleTile extends ViewModelWidget<LearnModuleViewModel> {
|
||||||
text: LocaleKeys.take_practice.tr(),
|
text: LocaleKeys.take_practice.tr(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Widget _buildContainerShaderState() => !(module.access?.isAccessible ?? false)
|
Widget _buildContainerShaderState() => !(module.access?.isAccessible ?? false)
|
||||||
? _buildContainerShader()
|
? _buildContainerShader()
|
||||||
: Container();
|
: Container();
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,6 @@ class OverallProgressWrapper extends StatelessWidget {
|
||||||
|
|
||||||
Widget _buildSubtitle() => Text(
|
Widget _buildSubtitle() => Text(
|
||||||
LocaleKeys.keep_up_the_great_work.tr(),
|
LocaleKeys.keep_up_the_great_work.tr(),
|
||||||
style: style14DG400,
|
style:style14DG400 ,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
name: yimaru_app
|
name: yimaru_app
|
||||||
version: 0.1.28+30
|
version: 0.1.27+29
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
description: A new Flutter project.
|
description: A new Flutter project.
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user