feat(course): Add lesson level duolingo practice.
This commit is contained in:
parent
3fc9635207
commit
813720db89
|
|
@ -194,5 +194,17 @@
|
||||||
"completed_practices": "Completed Practices",
|
"completed_practices": "Completed Practices",
|
||||||
"total_practices": "Total Practices",
|
"total_practices": "Total Practices",
|
||||||
"progress_percentage": "Progress Percentage",
|
"progress_percentage": "Progress Percentage",
|
||||||
"notifications": "Notifications"
|
"notifications": "Notifications",
|
||||||
|
"choose_course_to_improve":"Choose a course to improve your professional or exam skills.",
|
||||||
|
"start_course":"Start course",
|
||||||
|
"english_proficiency":"English Proficiency Exams",
|
||||||
|
"select_your_target_exam":"Select your target exam and start preparing",
|
||||||
|
"take_mock_exam":"Take Mock Exam",
|
||||||
|
"course_detail":"Course Detail",
|
||||||
|
"continue_course":"Continue Course",
|
||||||
|
"module_detail":"Module Detail",
|
||||||
|
"continue_module":"Continue Module",
|
||||||
|
"play_video":"Play Video",
|
||||||
|
"practice_test":"Practice Test",
|
||||||
|
"start_assessment":"Start assessment"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,7 @@ import 'package:yimaru_app/ui/views/payment/payment_view.dart';
|
||||||
import 'package:yimaru_app/ui/views/notification/notification_view.dart';
|
import 'package:yimaru_app/ui/views/notification/notification_view.dart';
|
||||||
import 'package:yimaru_app/services/in_app_notification_service.dart';
|
import 'package:yimaru_app/services/in_app_notification_service.dart';
|
||||||
import 'package:yimaru_app/services/push_notification_service.dart';
|
import 'package:yimaru_app/services/push_notification_service.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/course_practice_view.dart';
|
||||||
// @stacked-import
|
// @stacked-import
|
||||||
|
|
||||||
@StackedApp(
|
@StackedApp(
|
||||||
|
|
@ -100,6 +101,7 @@ import 'package:yimaru_app/services/push_notification_service.dart';
|
||||||
MaterialRoute(page: LearnCourseView),
|
MaterialRoute(page: LearnCourseView),
|
||||||
MaterialRoute(page: PaymentView),
|
MaterialRoute(page: PaymentView),
|
||||||
MaterialRoute(page: NotificationView),
|
MaterialRoute(page: NotificationView),
|
||||||
|
MaterialRoute(page: CoursePracticeView),
|
||||||
// @stacked-route
|
// @stacked-route
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
47
lib/models/course_practice.dart
Normal file
47
lib/models/course_practice.dart
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
part 'course_practice.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class CoursePractice {
|
||||||
|
final int? id;
|
||||||
|
|
||||||
|
final String? title;
|
||||||
|
|
||||||
|
@JsonKey(name: 'lesson_id')
|
||||||
|
final int? lessonId;
|
||||||
|
|
||||||
|
@JsonKey(name: 'persona_id')
|
||||||
|
final int? personaId;
|
||||||
|
|
||||||
|
@JsonKey(name: 'quick_tips')
|
||||||
|
final String? quickTips;
|
||||||
|
|
||||||
|
@JsonKey(name: 'story_image')
|
||||||
|
final String? storyImage;
|
||||||
|
|
||||||
|
@JsonKey(name: 'publish_status')
|
||||||
|
final String? publishStatus;
|
||||||
|
|
||||||
|
@JsonKey(name: 'question_set_id')
|
||||||
|
final int? questionSetId;
|
||||||
|
|
||||||
|
@JsonKey(name: 'story_description')
|
||||||
|
final String? storyDescription;
|
||||||
|
|
||||||
|
const CoursePractice(
|
||||||
|
{this.id,
|
||||||
|
this.title,
|
||||||
|
this.lessonId,
|
||||||
|
this.quickTips,
|
||||||
|
this.storyImage,
|
||||||
|
this.personaId,
|
||||||
|
this.publishStatus,
|
||||||
|
this.questionSetId,
|
||||||
|
this.storyDescription});
|
||||||
|
|
||||||
|
factory CoursePractice.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$CoursePracticeFromJson(json);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => _$CoursePracticeToJson(this);
|
||||||
|
}
|
||||||
33
lib/models/course_practice.g.dart
Normal file
33
lib/models/course_practice.g.dart
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'course_practice.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
CoursePractice _$CoursePracticeFromJson(Map<String, dynamic> json) =>
|
||||||
|
CoursePractice(
|
||||||
|
id: (json['id'] as num?)?.toInt(),
|
||||||
|
title: json['title'] as String?,
|
||||||
|
lessonId: (json['lesson_id'] as num?)?.toInt(),
|
||||||
|
quickTips: json['quick_tips'] as String?,
|
||||||
|
storyImage: json['story_image'] as String?,
|
||||||
|
personaId: (json['persona_id'] as num?)?.toInt(),
|
||||||
|
publishStatus: json['publish_status'] as String?,
|
||||||
|
questionSetId: (json['question_set_id'] as num?)?.toInt(),
|
||||||
|
storyDescription: json['story_description'] as String?,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$CoursePracticeToJson(CoursePractice instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'title': instance.title,
|
||||||
|
'lesson_id': instance.lessonId,
|
||||||
|
'persona_id': instance.personaId,
|
||||||
|
'quick_tips': instance.quickTips,
|
||||||
|
'story_image': instance.storyImage,
|
||||||
|
'publish_status': instance.publishStatus,
|
||||||
|
'question_set_id': instance.questionSetId,
|
||||||
|
'story_description': instance.storyDescription,
|
||||||
|
};
|
||||||
35
lib/models/course_question.dart
Normal file
35
lib/models/course_question.dart
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
import 'package:yimaru_app/models/dynamic_payload.dart';
|
||||||
|
|
||||||
|
part 'course_question.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class CourseQuestion {
|
||||||
|
final int? id;
|
||||||
|
|
||||||
|
final int? points;
|
||||||
|
|
||||||
|
final String? status;
|
||||||
|
|
||||||
|
@JsonKey(name: 'question_type')
|
||||||
|
final String? questionType;
|
||||||
|
|
||||||
|
@JsonKey(name: 'difficulty_level')
|
||||||
|
final String? difficultyLevel;
|
||||||
|
|
||||||
|
@JsonKey(name: 'dynamic_payload')
|
||||||
|
final DynamicPayload? dynamicPayload;
|
||||||
|
|
||||||
|
const CourseQuestion(
|
||||||
|
{this.id,
|
||||||
|
this.status,
|
||||||
|
this.points,
|
||||||
|
this.questionType,
|
||||||
|
this.difficultyLevel,
|
||||||
|
this.dynamicPayload});
|
||||||
|
|
||||||
|
factory CourseQuestion.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$CourseQuestionFromJson(json);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => _$CourseQuestionToJson(this);
|
||||||
|
}
|
||||||
30
lib/models/course_question.g.dart
Normal file
30
lib/models/course_question.g.dart
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'course_question.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
CourseQuestion _$CourseQuestionFromJson(Map<String, dynamic> json) =>
|
||||||
|
CourseQuestion(
|
||||||
|
id: (json['id'] as num?)?.toInt(),
|
||||||
|
status: json['status'] as String?,
|
||||||
|
points: (json['points'] as num?)?.toInt(),
|
||||||
|
questionType: json['question_type'] as String?,
|
||||||
|
difficultyLevel: json['difficulty_level'] as String?,
|
||||||
|
dynamicPayload: json['dynamic_payload'] == null
|
||||||
|
? null
|
||||||
|
: DynamicPayload.fromJson(
|
||||||
|
json['dynamic_payload'] as Map<String, dynamic>),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$CourseQuestionToJson(CourseQuestion instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'points': instance.points,
|
||||||
|
'status': instance.status,
|
||||||
|
'question_type': instance.questionType,
|
||||||
|
'difficulty_level': instance.difficultyLevel,
|
||||||
|
'dynamic_payload': instance.dynamicPayload,
|
||||||
|
};
|
||||||
21
lib/models/dynamic_item.dart
Normal file
21
lib/models/dynamic_item.dart
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
part 'dynamic_item.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class DynamicItem {
|
||||||
|
final String id;
|
||||||
|
final String kind;
|
||||||
|
final dynamic value;
|
||||||
|
|
||||||
|
DynamicItem({
|
||||||
|
required this.id,
|
||||||
|
required this.kind,
|
||||||
|
required this.value,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory DynamicItem.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$DynamicItemFromJson(json);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => _$DynamicItemToJson(this);
|
||||||
|
}
|
||||||
20
lib/models/dynamic_item.g.dart
Normal file
20
lib/models/dynamic_item.g.dart
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'dynamic_item.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
DynamicItem _$DynamicItemFromJson(Map<String, dynamic> json) => DynamicItem(
|
||||||
|
id: json['id'] as String,
|
||||||
|
kind: json['kind'] as String,
|
||||||
|
value: json['value'],
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$DynamicItemToJson(DynamicItem instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'kind': instance.kind,
|
||||||
|
'value': instance.value,
|
||||||
|
};
|
||||||
19
lib/models/dynamic_payload.dart
Normal file
19
lib/models/dynamic_payload.dart
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
import 'dynamic_item.dart';
|
||||||
|
|
||||||
|
part 'dynamic_payload.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable()
|
||||||
|
class DynamicPayload {
|
||||||
|
final List<DynamicItem>? stimulus;
|
||||||
|
|
||||||
|
final List<DynamicItem>? response;
|
||||||
|
|
||||||
|
const DynamicPayload({this.stimulus, this.response});
|
||||||
|
|
||||||
|
factory DynamicPayload.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$DynamicPayloadFromJson(json);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => _$DynamicPayloadToJson(this);
|
||||||
|
}
|
||||||
23
lib/models/dynamic_payload.g.dart
Normal file
23
lib/models/dynamic_payload.g.dart
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'dynamic_payload.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
DynamicPayload _$DynamicPayloadFromJson(Map<String, dynamic> json) =>
|
||||||
|
DynamicPayload(
|
||||||
|
stimulus: (json['stimulus'] as List<dynamic>?)
|
||||||
|
?.map((e) => DynamicItem.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList(),
|
||||||
|
response: (json['response'] as List<dynamic>?)
|
||||||
|
?.map((e) => DynamicItem.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$DynamicPayloadToJson(DynamicPayload instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'stimulus': instance.stimulus,
|
||||||
|
'response': instance.response,
|
||||||
|
};
|
||||||
|
|
@ -14,6 +14,8 @@ import 'package:yimaru_app/ui/common/app_constants.dart';
|
||||||
|
|
||||||
import '../app/app.locator.dart';
|
import '../app/app.locator.dart';
|
||||||
import '../models/course_module.dart';
|
import '../models/course_module.dart';
|
||||||
|
import '../models/course_practice.dart';
|
||||||
|
import '../models/course_question.dart';
|
||||||
import '../models/course_unit.dart';
|
import '../models/course_unit.dart';
|
||||||
import '../models/field_option.dart';
|
import '../models/field_option.dart';
|
||||||
import '../models/learn_course.dart';
|
import '../models/learn_course.dart';
|
||||||
|
|
@ -903,7 +905,7 @@ class ApiService {
|
||||||
print('Here');
|
print('Here');
|
||||||
|
|
||||||
final Response response = await _service.dio.get(
|
final Response response = await _service.dio.get(
|
||||||
'$kBaseUrl/api/$kApiVersionUrl/$kQuestionSetsUrl/$id/$kQuestionsUrl');
|
'$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kQuestionSetsUrl/$id/$kQuestionsUrl');
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
var data = response.data;
|
var data = response.data;
|
||||||
|
|
@ -1144,6 +1146,55 @@ class ApiService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Course practices
|
||||||
|
Future<List<CoursePractice>> getCoursePractices(int id) async {
|
||||||
|
try {
|
||||||
|
List<CoursePractice> practices = [];
|
||||||
|
|
||||||
|
final Response response = await _service.dio.get(
|
||||||
|
'$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kExamPrepUrl/$kLessonsUrl/$id/$kPracticesUrl');
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
var data = response.data;
|
||||||
|
var decodedData = data['data']['practices'] as List;
|
||||||
|
practices = decodedData.map(
|
||||||
|
(e) {
|
||||||
|
return CoursePractice.fromJson(e);
|
||||||
|
},
|
||||||
|
).toList();
|
||||||
|
return practices;
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
} catch (e) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get course questions
|
||||||
|
Future<List<CourseQuestion>> getCourseQuestions(int id) async {
|
||||||
|
try {
|
||||||
|
List<CourseQuestion> questions = [];
|
||||||
|
print('Here');
|
||||||
|
|
||||||
|
final Response response = await _service.dio.get(
|
||||||
|
'$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kQuestionSetsUrl/$id/$kQuestionsUrl');
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
var data = response.data;
|
||||||
|
var decodedData = data['data'] as List;
|
||||||
|
questions = decodedData.map(
|
||||||
|
(e) {
|
||||||
|
return CourseQuestion.fromJson(e);
|
||||||
|
},
|
||||||
|
).toList();
|
||||||
|
return questions;
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
} catch (e) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check update
|
// Check update
|
||||||
Future<Map<String, dynamic>> checkUpdate(Map<String, dynamic> data) async {
|
Future<Map<String, dynamic>> checkUpdate(Map<String, dynamic> data) async {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ import '../models/course_catalog.dart';
|
||||||
import '../models/course_lesson.dart';
|
import '../models/course_lesson.dart';
|
||||||
import '../models/course_module.dart';
|
import '../models/course_module.dart';
|
||||||
import '../models/course_unit.dart';
|
import '../models/course_unit.dart';
|
||||||
|
import '../models/refresh_object.dart';
|
||||||
|
import '../ui/common/enmus.dart';
|
||||||
|
|
||||||
class CourseService with ListenableServiceMixin {
|
class CourseService with ListenableServiceMixin {
|
||||||
// Dependency injection
|
// Dependency injection
|
||||||
|
|
@ -76,4 +78,16 @@ class CourseService with ListenableServiceMixin {
|
||||||
_lessons.sort((a, b) => (a.sortOrder ?? 0).compareTo(b.sortOrder ?? 0));
|
_lessons.sort((a, b) => (a.sortOrder ?? 0).compareTo(b.sortOrder ?? 0));
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<String?> refreshObject(String url) async {
|
||||||
|
Map<String, dynamic> data = {'reference': url};
|
||||||
|
Map<String, dynamic> response = await _apiService.refreshObject(data);
|
||||||
|
|
||||||
|
if (response['status'] == ResponseStatus.success) {
|
||||||
|
RefreshObject object = response['data'] as RefreshObject;
|
||||||
|
|
||||||
|
return object.url ?? '';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,130 +7,132 @@ Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
||||||
await locator<PushNotificationService>().setupFlutterNotifications();
|
await locator<PushNotificationService>().setupFlutterNotifications();
|
||||||
await locator<PushNotificationService>().showNotification(message);
|
await locator<PushNotificationService>().showNotification(message);
|
||||||
}
|
}
|
||||||
class PushNotificationService { final _messaging = FirebaseMessaging.instance;
|
|
||||||
|
|
||||||
bool _isFlutterLocalNotificationInitialized = false;
|
class PushNotificationService {
|
||||||
|
final _messaging = FirebaseMessaging.instance;
|
||||||
|
|
||||||
final _localNotifications = FlutterLocalNotificationsPlugin();
|
bool _isFlutterLocalNotificationInitialized = false;
|
||||||
|
|
||||||
Future<void> initialize() async {
|
final _localNotifications = FlutterLocalNotificationsPlugin();
|
||||||
// Initialize FCM token
|
|
||||||
await updateFCMToken();
|
|
||||||
|
|
||||||
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
|
Future<void> initialize() async {
|
||||||
|
// Initialize FCM token
|
||||||
|
await updateFCMToken();
|
||||||
|
|
||||||
// Request permission
|
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
|
||||||
await _requestPermission();
|
|
||||||
|
|
||||||
// setup message handle
|
// Request permission
|
||||||
await _setupMessageHandler();
|
await _requestPermission();
|
||||||
|
|
||||||
// Subscribe to all devices
|
// setup message handle
|
||||||
subscribeToTopic('yimaru');
|
await _setupMessageHandler();
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _requestPermission() async {
|
// Subscribe to all devices
|
||||||
await _messaging.requestPermission(
|
subscribeToTopic('yimaru');
|
||||||
alert: true,
|
|
||||||
badge: true,
|
|
||||||
sound: true,
|
|
||||||
carPlay: false,
|
|
||||||
provisional: false,
|
|
||||||
announcement: false,
|
|
||||||
criticalAlert: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> setupFlutterNotifications() async {
|
|
||||||
if (_isFlutterLocalNotificationInitialized) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Android setup
|
Future<void> _requestPermission() async {
|
||||||
const channel = AndroidNotificationChannel(
|
await _messaging.requestPermission(
|
||||||
'yimaru', // id
|
alert: true,
|
||||||
'Yimaru', // title
|
badge: true,
|
||||||
importance: Importance.high,
|
sound: true,
|
||||||
);
|
carPlay: false,
|
||||||
|
provisional: false,
|
||||||
|
announcement: false,
|
||||||
|
criticalAlert: false);
|
||||||
|
}
|
||||||
|
|
||||||
await _localNotifications
|
Future<void> setupFlutterNotifications() async {
|
||||||
.resolvePlatformSpecificImplementation<
|
if (_isFlutterLocalNotificationInitialized) {
|
||||||
AndroidFlutterLocalNotificationsPlugin>()
|
return;
|
||||||
?.createNotificationChannel(channel);
|
}
|
||||||
|
|
||||||
const initializationSettingsAndroid =
|
// Android setup
|
||||||
AndroidInitializationSettings('@mipmap/ic_launcher');
|
const channel = AndroidNotificationChannel(
|
||||||
|
'yimaru', // id
|
||||||
// IOS setup
|
'Yimaru', // title
|
||||||
const initializationSettingsDarwin = DarwinInitializationSettings();
|
importance: Importance.high,
|
||||||
|
|
||||||
const initializationSettings = InitializationSettings(
|
|
||||||
android: initializationSettingsAndroid,
|
|
||||||
iOS: initializationSettingsDarwin);
|
|
||||||
|
|
||||||
// Flutter notification setup
|
|
||||||
await _localNotifications.initialize(
|
|
||||||
settings: initializationSettings,
|
|
||||||
onDidReceiveNotificationResponse: (NotificationResponse response) {
|
|
||||||
if (response.payload == 'Page') {
|
|
||||||
// navigatorKey.currentState?.pushNamed('RouteName');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
_isFlutterLocalNotificationInitialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> showNotification(RemoteMessage message) async {
|
|
||||||
RemoteNotification? notification = message.notification;
|
|
||||||
AndroidNotification? android = message.notification?.android;
|
|
||||||
|
|
||||||
if (notification != null && android != null) {
|
|
||||||
await _localNotifications.show(
|
|
||||||
id: notification.hashCode,
|
|
||||||
title: notification.title,
|
|
||||||
body: notification.body,
|
|
||||||
notificationDetails: const NotificationDetails(
|
|
||||||
android: AndroidNotificationDetails('yimaru', 'Yimaru',
|
|
||||||
enableVibration: true,
|
|
||||||
priority: Priority.high,
|
|
||||||
icon: '@mipmap/ic_launcher',
|
|
||||||
importance: Importance.high),
|
|
||||||
iOS: DarwinNotificationDetails(
|
|
||||||
presentAlert: true, presentBadge: true, presentSound: true)),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await _localNotifications
|
||||||
|
.resolvePlatformSpecificImplementation<
|
||||||
|
AndroidFlutterLocalNotificationsPlugin>()
|
||||||
|
?.createNotificationChannel(channel);
|
||||||
|
|
||||||
|
const initializationSettingsAndroid =
|
||||||
|
AndroidInitializationSettings('@mipmap/ic_launcher');
|
||||||
|
|
||||||
|
// IOS setup
|
||||||
|
const initializationSettingsDarwin = DarwinInitializationSettings();
|
||||||
|
|
||||||
|
const initializationSettings = InitializationSettings(
|
||||||
|
android: initializationSettingsAndroid,
|
||||||
|
iOS: initializationSettingsDarwin);
|
||||||
|
|
||||||
|
// Flutter notification setup
|
||||||
|
await _localNotifications.initialize(
|
||||||
|
settings: initializationSettings,
|
||||||
|
onDidReceiveNotificationResponse: (NotificationResponse response) {
|
||||||
|
if (response.payload == 'Page') {
|
||||||
|
// navigatorKey.currentState?.pushNamed('RouteName');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
_isFlutterLocalNotificationInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> showNotification(RemoteMessage message) async {
|
||||||
|
RemoteNotification? notification = message.notification;
|
||||||
|
AndroidNotification? android = message.notification?.android;
|
||||||
|
|
||||||
|
if (notification != null && android != null) {
|
||||||
|
await _localNotifications.show(
|
||||||
|
id: notification.hashCode,
|
||||||
|
title: notification.title,
|
||||||
|
body: notification.body,
|
||||||
|
notificationDetails: const NotificationDetails(
|
||||||
|
android: AndroidNotificationDetails('yimaru', 'Yimaru',
|
||||||
|
enableVibration: true,
|
||||||
|
priority: Priority.high,
|
||||||
|
icon: '@mipmap/ic_launcher',
|
||||||
|
importance: Importance.high),
|
||||||
|
iOS: DarwinNotificationDetails(
|
||||||
|
presentAlert: true, presentBadge: true, presentSound: true)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _setupMessageHandler() async {
|
||||||
|
// Foreground message
|
||||||
|
FirebaseMessaging.onMessage
|
||||||
|
.listen((RemoteMessage message) => showNotification(message));
|
||||||
|
|
||||||
|
// Background message
|
||||||
|
FirebaseMessaging.onMessageOpenedApp.listen(_handleBackgroundMessage);
|
||||||
|
|
||||||
|
// Opened app
|
||||||
|
final initialMessage = await _messaging.getInitialMessage();
|
||||||
|
|
||||||
|
if (initialMessage != null) {
|
||||||
|
_handleBackgroundMessage(initialMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleBackgroundMessage(RemoteMessage message) {
|
||||||
|
if (message.data['type'] == 'Page') {
|
||||||
|
// navigatorKey.currentState?.pushNamed('RouteName');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> subscribeToTopic(String topic) async {
|
||||||
|
await FirebaseMessaging.instance.subscribeToTopic(topic);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> updateFCMToken() async {
|
||||||
|
// print('DEVICE TOKEN: ${await _messaging.getToken()}');
|
||||||
|
_messaging.onTokenRefresh.listen((newToken) {
|
||||||
|
// updateTokenOnServer(newToken);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _setupMessageHandler() async {
|
|
||||||
// Foreground message
|
|
||||||
FirebaseMessaging.onMessage
|
|
||||||
.listen((RemoteMessage message) => showNotification(message));
|
|
||||||
|
|
||||||
// Background message
|
|
||||||
FirebaseMessaging.onMessageOpenedApp.listen(_handleBackgroundMessage);
|
|
||||||
|
|
||||||
// Opened app
|
|
||||||
final initialMessage = await _messaging.getInitialMessage();
|
|
||||||
|
|
||||||
if (initialMessage != null) {
|
|
||||||
_handleBackgroundMessage(initialMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _handleBackgroundMessage(RemoteMessage message) {
|
|
||||||
if (message.data['type'] == 'Page') {
|
|
||||||
// navigatorKey.currentState?.pushNamed('RouteName');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> subscribeToTopic(String topic) async {
|
|
||||||
await FirebaseMessaging.instance.subscribeToTopic(topic);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> updateFCMToken() async {
|
|
||||||
// print('DEVICE TOKEN: ${await _messaging.getToken()}');
|
|
||||||
_messaging.onTokenRefresh.listen((newToken) {
|
|
||||||
// updateTokenOnServer(newToken);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ String kLevelsUrl = 'levels';
|
||||||
|
|
||||||
String kCoursesUrl = 'courses';
|
String kCoursesUrl = 'courses';
|
||||||
|
|
||||||
|
|
||||||
String kModulesUrl = 'modules';
|
String kModulesUrl = 'modules';
|
||||||
|
|
||||||
String kLessonsUrl = 'lessons';
|
String kLessonsUrl = 'lessons';
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@ enum LearnPractices { course, module, lesson }
|
||||||
// Voice recording state
|
// Voice recording state
|
||||||
enum VoiceRecordingState { pending, recording }
|
enum VoiceRecordingState { pending, recording }
|
||||||
|
|
||||||
// // Levels
|
// Course practice
|
||||||
// enum ProficiencyLevels { a1, a2, b1, b2, none }
|
enum CoursePractices { courseCatalog, unit, lesson }
|
||||||
|
|
||||||
// Progress status
|
// Progress status
|
||||||
enum ProgressStatuses { pending, started, completed }
|
enum ProgressStatuses { pending, started, completed }
|
||||||
|
|
@ -67,10 +67,13 @@ enum StateObjects {
|
||||||
learnPracticeAnswer,
|
learnPracticeAnswer,
|
||||||
loginWithPhoneNumber,
|
loginWithPhoneNumber,
|
||||||
assessmentQuestions,
|
assessmentQuestions,
|
||||||
|
coursePracticeReview,
|
||||||
learnPracticeQuestion,
|
learnPracticeQuestion,
|
||||||
completeLearnPractice,
|
completeLearnPractice,
|
||||||
coursePracticeQuestion,
|
coursePracticeQuestion,
|
||||||
coursePracticeQuestions,
|
coursePracticeQuestions,
|
||||||
recordLearnPracticeAnswer,
|
recordLearnPracticeAnswer,
|
||||||
finishLearnPracticeQuestion
|
recordCoursePracticeAnswer,
|
||||||
|
finishLearnPracticeQuestion,
|
||||||
|
finishCoursePracticeQuestion
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,24 @@ import 'dart:ui';
|
||||||
|
|
||||||
import 'app_colors.dart';
|
import 'app_colors.dart';
|
||||||
|
|
||||||
|
// Seconds to minutes
|
||||||
|
String getSecondsToMinutes(int totalSeconds) {
|
||||||
|
final minutes = totalSeconds ~/ 60;
|
||||||
|
final seconds = totalSeconds % 60;
|
||||||
|
|
||||||
|
return '${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minutes
|
||||||
|
int getMinutes(int totalSeconds) {
|
||||||
|
return totalSeconds ~/ 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seconds
|
||||||
|
int getSeconds(int totalSeconds) {
|
||||||
|
return totalSeconds % 60;
|
||||||
|
}
|
||||||
|
|
||||||
// Split full name
|
// Split full name
|
||||||
Map<String, String> splitFullName(String fullName) {
|
Map<String, String> splitFullName(String fullName) {
|
||||||
final parts = fullName.trim().split(RegExp(r'\s+'));
|
final parts = fullName.trim().split(RegExp(r'\s+'));
|
||||||
|
|
|
||||||
|
|
@ -236,6 +236,11 @@ TextStyle style14B400 = const TextStyle(
|
||||||
color: kcBlue,
|
color: kcBlue,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
TextStyle style0Ts = const TextStyle(
|
||||||
|
fontSize: 0,
|
||||||
|
color: kcTransparent,
|
||||||
|
);
|
||||||
|
|
||||||
TextStyle style14P600 = const TextStyle(
|
TextStyle style14P600 = const TextStyle(
|
||||||
color: kcPrimaryColor,
|
color: kcPrimaryColor,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
|
|
@ -333,11 +338,8 @@ TextStyle style14LG400 = const TextStyle(
|
||||||
color: kcLightGrey,
|
color: kcLightGrey,
|
||||||
);
|
);
|
||||||
|
|
||||||
TextStyle style12W600 = const TextStyle(
|
TextStyle style12W600 =
|
||||||
fontSize: 12,
|
const TextStyle(fontSize: 12, color: kcWhite, fontWeight: FontWeight.w600);
|
||||||
color: kcWhite,
|
|
||||||
fontWeight: FontWeight.w600
|
|
||||||
);
|
|
||||||
|
|
||||||
TextStyle style14MG400 = const TextStyle(
|
TextStyle style14MG400 = const TextStyle(
|
||||||
color: kcMediumGrey,
|
color: kcMediumGrey,
|
||||||
|
|
|
||||||
|
|
@ -51,8 +51,8 @@ class CourseView extends StackedView<CourseViewModel> {
|
||||||
Widget _buildAppBar(CourseViewModel viewModel) => ProfileAppBar(
|
Widget _buildAppBar(CourseViewModel viewModel) => ProfileAppBar(
|
||||||
name: viewModel.user?.firstName,
|
name: viewModel.user?.firstName,
|
||||||
profileImage: viewModel.user?.profilePicture,
|
profileImage: viewModel.user?.profilePicture,
|
||||||
unreadCount: viewModel.unreadCount.toString(),
|
unreadCount: viewModel.unreadCount.toString(),
|
||||||
onTap: () async => await viewModel.navigateToNotification(),
|
onTap: () async => await viewModel.navigateToNotification(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildCategoryColumnWrapper(CourseViewModel viewModel) =>
|
Widget _buildCategoryColumnWrapper(CourseViewModel viewModel) =>
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,9 @@ class CourseViewModel extends ReactiveViewModel {
|
||||||
|
|
||||||
final _inAppNotificationService = locator<InAppNotificationService>();
|
final _inAppNotificationService = locator<InAppNotificationService>();
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<ListenableServiceMixin> get listenableServices =>
|
List<ListenableServiceMixin> get listenableServices =>
|
||||||
[_authenticationService,_inAppNotificationService];
|
[_authenticationService, _inAppNotificationService];
|
||||||
|
|
||||||
// Current user
|
// Current user
|
||||||
User? get _user => _authenticationService.user;
|
User? get _user => _authenticationService.user;
|
||||||
|
|
@ -55,6 +54,4 @@ class CourseViewModel extends ReactiveViewModel {
|
||||||
|
|
||||||
Future<void> navigateToCourseCatalog() async =>
|
Future<void> navigateToCourseCatalog() async =>
|
||||||
await _navigationService.navigateToCourseCatalogView();
|
await _navigationService.navigateToCourseCatalogView();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,8 @@ class CourseModuleViewModel extends ReactiveViewModel {
|
||||||
Future<void> navigateToCourseLessonDetail(CourseLesson lesson) async =>
|
Future<void> navigateToCourseLessonDetail(CourseLesson lesson) async =>
|
||||||
await _navigationService.navigateToCourseLessonDetailView(lesson: lesson);
|
await _navigationService.navigateToCourseLessonDetailView(lesson: lesson);
|
||||||
|
|
||||||
|
Future<void> navigateToCoursePractice(int id) async =>
|
||||||
|
await _navigationService.navigateToCoursePracticeView(id: id, practice: CoursePractices.lesson,);
|
||||||
// Remote api call
|
// Remote api call
|
||||||
|
|
||||||
// Course modules
|
// Course modules
|
||||||
|
|
|
||||||
309
lib/ui/views/course_practice/course_practice_view.dart
Normal file
309
lib/ui/views/course_practice/course_practice_view.dart
Normal file
|
|
@ -0,0 +1,309 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:stacked/stacked.dart';
|
||||||
|
import 'package:stacked/stacked_annotations.dart';
|
||||||
|
import 'package:yimaru_app/models/course_question.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_listening_practice_1_review.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_listening_practice_2_review.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_practices_screens.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_finish_screen.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_intro_screen.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_listening_practice_1_question.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_listening_practice_2_question.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_listening_practice_3_question.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_retake_screen.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_speaking_practice_1_answer.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_speaking_practice_1_question.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_speaking_practice_1_review.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_speaking_practice_2_answer.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_speaking_practice_2_question.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_speaking_practice_2_review.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_speaking_practice_3_answer.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_speaking_practice_3_question.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_speaking_practice_3_review.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_speaking_practice_4_answer.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_speaking_practice_4_question.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_speaking_practice_4_review.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_writing_practice_1_question.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_writing_practice_1_review.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_writing_practice_2_answer.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_writing_practice_2_question.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_writing_practice_2_review.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_writing_practice_3_question.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_writing_practice_3_review.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_writing_practice_4_question.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/screens/duolingo_writing_practice_4_review.dart';
|
||||||
|
|
||||||
|
import '../../common/app_colors.dart';
|
||||||
|
import '../../common/validators/form_validator.dart';
|
||||||
|
import '../../widgets/cancel_practice_sheet.dart';
|
||||||
|
import '../../widgets/page_loading_indicator.dart';
|
||||||
|
import '../../widgets/practice_loading_screen.dart';
|
||||||
|
import 'course_practice_view.form.dart';
|
||||||
|
import 'course_practice_viewmodel.dart';
|
||||||
|
|
||||||
|
@FormView(fields: [
|
||||||
|
FormTextField(name: 'practice', validator: FormValidator.validateForm),
|
||||||
|
])
|
||||||
|
class CoursePracticeView extends StackedView<CoursePracticeViewModel> with $CoursePracticeView {
|
||||||
|
final int id;
|
||||||
|
|
||||||
|
final CoursePractices practice;
|
||||||
|
|
||||||
|
const CoursePracticeView({
|
||||||
|
Key? key,
|
||||||
|
required this.id,
|
||||||
|
required this.practice,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
Future<void> _cancel(CoursePracticeViewModel viewModel) async {
|
||||||
|
await viewModel.stopRecording();
|
||||||
|
viewModel.pop();
|
||||||
|
viewModel.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _showSheet(
|
||||||
|
{required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) async =>
|
||||||
|
await showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
backgroundColor: kcTransparent,
|
||||||
|
builder: (_) => _buildSheet(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildQuestionScreen(CoursePracticeViewModel viewModel) {
|
||||||
|
String type =
|
||||||
|
viewModel.questions[viewModel.currentQuestion].id.toString() ?? '';
|
||||||
|
if (type == '636') {
|
||||||
|
return const DuolingoSpeakingPractice1Question();
|
||||||
|
} else if (type == 'Read, Then Speak') {
|
||||||
|
return const DuolingoSpeakingPractice2Question();
|
||||||
|
} else if (type == 'Speaking Sample') {
|
||||||
|
return const DuolingoSpeakingPractice3Question();
|
||||||
|
} else if (type == 'Interactive Speaking') {
|
||||||
|
return const DuolingoSpeakingPractice4Question();
|
||||||
|
} else if (type == 'Write About the Photo') {
|
||||||
|
return DuolingoWritingPractice1Question(
|
||||||
|
practiceController: practiceController);
|
||||||
|
} else if (type == 'Writing Sample') {
|
||||||
|
return DuolingoWritingPractice2Question(
|
||||||
|
practiceController: practiceController);
|
||||||
|
} else if (type == 'Interactive Writing Part 1') {
|
||||||
|
return DuolingoWritingPractice3Question(
|
||||||
|
practiceController: practiceController);
|
||||||
|
} else if (type == 'Interactive Writing Part 2') {
|
||||||
|
return DuolingoWritingPractice4Question(
|
||||||
|
practiceController: practiceController);
|
||||||
|
} else if (type == 'Listen and Type') {
|
||||||
|
return DuolingoListeningPractice1Question(
|
||||||
|
practiceController: practiceController);
|
||||||
|
} else if (type == 'Interactive Listening - Part 1') {
|
||||||
|
return DuolingoListeningPractice2Question(
|
||||||
|
practiceController: practiceController);
|
||||||
|
} else if (type == 'Interactive Listening - Part 2') {
|
||||||
|
return DuolingoListeningPractice3Question(
|
||||||
|
practiceController: practiceController);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildAnswerScreen(CoursePracticeViewModel viewModel) {
|
||||||
|
String type =
|
||||||
|
viewModel.questions[viewModel.currentQuestion].id.toString() ?? '';
|
||||||
|
|
||||||
|
if (type == '636') {
|
||||||
|
return const DuolingoSpeakingPractice1Answer();
|
||||||
|
} else if (type == 'Read, Then Speak') {
|
||||||
|
return const DuolingoSpeakingPractice2Answer();
|
||||||
|
} else if (type == 'Speaking Sample') {
|
||||||
|
return const DuolingoSpeakingPractice3Answer();
|
||||||
|
} else if (type == 'Interactive Speaking') {
|
||||||
|
return const DuolingoSpeakingPractice4Answer();
|
||||||
|
} else if (type == 'Write About the Photo') {
|
||||||
|
return Container();
|
||||||
|
} else if (type == 'Writing Sample') {
|
||||||
|
return DuolingoWritingPractice2Answer(
|
||||||
|
practiceController: practiceController);
|
||||||
|
} else if (type == 'Interactive Writing Part 1') {
|
||||||
|
return Container();
|
||||||
|
} else if (type == 'Interactive Writing Part 2') {
|
||||||
|
return Container();
|
||||||
|
} else if (type == 'Listen and Type') {
|
||||||
|
return Container();
|
||||||
|
} else if (type == 'Interactive Listening - Part 1') {
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildReviewScreen(CoursePracticeViewModel viewModel) {
|
||||||
|
String type =
|
||||||
|
viewModel.questions[viewModel.currentQuestion].id.toString() ?? '';
|
||||||
|
|
||||||
|
if (type == '636') {
|
||||||
|
return const DuolingoSpeakingPractice1Review();
|
||||||
|
} else if (type == 'Read, Then Speak') {
|
||||||
|
return const DuolingoSpeakingPractice2Review();
|
||||||
|
} else if (type == 'Speaking Sample') {
|
||||||
|
return const DuolingoSpeakingPractice3Review();
|
||||||
|
} else if (type == 'Interactive Speaking') {
|
||||||
|
return const DuolingoSpeakingPractice4Review();
|
||||||
|
} else if (type == 'Write About the Photo') {
|
||||||
|
return DuolingoWritingPractice1Review(
|
||||||
|
practiceController: practiceController);
|
||||||
|
} else if (type == 'Writing Sample') {
|
||||||
|
return DuolingoWritingPractice2Review(
|
||||||
|
practiceController: practiceController);
|
||||||
|
} else if (type == 'Interactive Writing Part 1') {
|
||||||
|
return DuolingoWritingPractice3Review(
|
||||||
|
practiceController: practiceController);
|
||||||
|
} else if (type == 'Interactive Writing Part 2') {
|
||||||
|
return DuolingoWritingPractice4Review(
|
||||||
|
practiceController: practiceController);
|
||||||
|
} else if (type == 'Listen and Type') {
|
||||||
|
return DuolingoListeningPractice1Review(
|
||||||
|
practiceController: practiceController);
|
||||||
|
} else if (type == 'Interactive Listening - Part 1') {
|
||||||
|
return DuolingoListeningPractice2Review(
|
||||||
|
practiceController: practiceController);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onViewModelReady(CoursePracticeViewModel viewModel) async {
|
||||||
|
await viewModel.getCoursePractices(id: id, practice: practice);
|
||||||
|
super.onViewModelReady(viewModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
CoursePracticeViewModel viewModelBuilder(BuildContext context) =>
|
||||||
|
CoursePracticeViewModel();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget builder(
|
||||||
|
BuildContext context,
|
||||||
|
CoursePracticeViewModel viewModel,
|
||||||
|
Widget? child,
|
||||||
|
) =>
|
||||||
|
_buildPracticeScreensWrapper(context: context, viewModel: viewModel);
|
||||||
|
|
||||||
|
Widget _buildPracticeScreensWrapper(
|
||||||
|
{required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) =>
|
||||||
|
PopScope(
|
||||||
|
canPop: false,
|
||||||
|
onPopInvokedWithResult: (didPop, data) {
|
||||||
|
if (!didPop) {
|
||||||
|
Future.microtask(() async =>
|
||||||
|
await _showSheet(context: context, viewModel: viewModel));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: _buildScaffoldWrapper(viewModel));
|
||||||
|
|
||||||
|
Widget _buildSheet(CoursePracticeViewModel viewModel) => CancelPracticeSheet(
|
||||||
|
onClose: viewModel.pop,
|
||||||
|
onContinue: viewModel.pop,
|
||||||
|
user: viewModel.user?.firstName ?? '',
|
||||||
|
onCancel: () async => await _cancel(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||||
|
backgroundColor: kcBackgroundColor,
|
||||||
|
body: _buildBodyState(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildBodyState(CoursePracticeViewModel viewModel) =>
|
||||||
|
viewModel.busy(StateObjects.coursePractice)
|
||||||
|
? const PageLoadingIndicator()
|
||||||
|
: viewModel.practices.isEmpty || viewModel.questions.isEmpty
|
||||||
|
? _buildPageLoadingIndicator(viewModel)
|
||||||
|
: _buildBody(viewModel);
|
||||||
|
|
||||||
|
Widget _buildPageLoadingIndicator(CoursePracticeViewModel viewModel) =>
|
||||||
|
PracticeLoadingScreen(
|
||||||
|
isLoading: viewModel.busy(StateObjects.coursePractice),
|
||||||
|
onTap: () async =>
|
||||||
|
await viewModel.getCoursePractices(id: id, practice: practice),
|
||||||
|
onPop: viewModel.practices.isEmpty || viewModel.questions.isEmpty
|
||||||
|
? viewModel.pop
|
||||||
|
: null,
|
||||||
|
isEmpty: viewModel.practices.isEmpty || viewModel.questions.isEmpty,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildBody(CoursePracticeViewModel viewModel) => IndexedStack(
|
||||||
|
index: viewModel.currentPage, children: _buildBodyChildren(viewModel));
|
||||||
|
|
||||||
|
List<Widget> _buildBodyChildren(CoursePracticeViewModel viewModel) => [
|
||||||
|
if (practice != CoursePractices.lesson)
|
||||||
|
_buildDuolingoAssessmentsScreen(),
|
||||||
|
_buildQuestionSetView(viewModel)
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildQuestionSetView(CoursePracticeViewModel viewModel) =>
|
||||||
|
PageView.builder(
|
||||||
|
itemCount: viewModel.questions.length,
|
||||||
|
controller: viewModel.practiceController,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
itemBuilder: (cotext, index) => _buildQuestionView(
|
||||||
|
index: index + 1,
|
||||||
|
viewModel: viewModel,
|
||||||
|
question: viewModel.questions[index]),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildQuestionView(
|
||||||
|
{required int index,
|
||||||
|
required CourseQuestion question,
|
||||||
|
required CoursePracticeViewModel viewModel}) =>
|
||||||
|
PageView(
|
||||||
|
controller: viewModel.questionController,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
children: _buildScreens(
|
||||||
|
index: index, viewModel: viewModel, question: question),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildScreens(
|
||||||
|
{required int index,
|
||||||
|
required CourseQuestion question,
|
||||||
|
required CoursePracticeViewModel viewModel}) =>
|
||||||
|
[
|
||||||
|
_buildDuolingoIntroScreen(viewModel),
|
||||||
|
_buildDuolingoQuestionScreen(viewModel),
|
||||||
|
_buildDuolingoAnswerScreen(viewModel),
|
||||||
|
_buildDuolingoReviewScreen(viewModel),
|
||||||
|
_buildDuolingoRetakeScreen(viewModel),
|
||||||
|
_buildDuolingoFinishScreen(viewModel),
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildDuolingoAssessmentsScreen() => const DuolingoPracticesScreens();
|
||||||
|
|
||||||
|
Widget _buildDuolingoIntroScreen(CoursePracticeViewModel viewModel) =>
|
||||||
|
DuolingoIntroScreen(
|
||||||
|
type: viewModel.selectedQuestionParam['type'],
|
||||||
|
title: viewModel.selectedQuestionParam['intro_title'],
|
||||||
|
subtitle: viewModel.selectedQuestionParam['intro_subtitle']);
|
||||||
|
|
||||||
|
Widget _buildDuolingoQuestionScreen(CoursePracticeViewModel viewModel) =>
|
||||||
|
_buildQuestionScreen(viewModel);
|
||||||
|
|
||||||
|
Widget _buildDuolingoAnswerScreen(CoursePracticeViewModel viewModel) =>
|
||||||
|
_buildAnswerScreen(viewModel);
|
||||||
|
|
||||||
|
Widget _buildDuolingoReviewScreen(CoursePracticeViewModel viewModel) =>
|
||||||
|
_buildReviewScreen(viewModel);
|
||||||
|
|
||||||
|
Widget _buildDuolingoRetakeScreen(CoursePracticeViewModel viewModel) =>
|
||||||
|
DuolingoRetakeScreen(
|
||||||
|
title: viewModel.selectedQuestionParam['outro_title'],
|
||||||
|
subtitle: viewModel.selectedQuestionParam['outro_subtitle']);
|
||||||
|
|
||||||
|
Widget _buildDuolingoFinishScreen(CoursePracticeViewModel viewModel) =>
|
||||||
|
DuolingoFinishScreen(
|
||||||
|
title: viewModel.selectedQuestionParam['outro_title'],
|
||||||
|
subtitle: viewModel.selectedQuestionParam['outro_subtitle']);
|
||||||
|
}
|
||||||
180
lib/ui/views/course_practice/course_practice_view.form.dart
Normal file
180
lib/ui/views/course_practice/course_practice_view.form.dart
Normal file
|
|
@ -0,0 +1,180 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// dart format width=80
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// StackedFormGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
// ignore_for_file: public_member_api_docs, constant_identifier_names, non_constant_identifier_names,unnecessary_this
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:stacked/stacked.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/validators/form_validator.dart';
|
||||||
|
|
||||||
|
const bool _autoTextFieldValidation = true;
|
||||||
|
|
||||||
|
const String PracticeValueKey = 'practice';
|
||||||
|
|
||||||
|
final Map<String, TextEditingController>
|
||||||
|
_CoursePracticeViewTextEditingControllers = {};
|
||||||
|
|
||||||
|
final Map<String, FocusNode> _CoursePracticeViewFocusNodes = {};
|
||||||
|
|
||||||
|
final Map<String, String? Function(String?)?>
|
||||||
|
_CoursePracticeViewTextValidations = {
|
||||||
|
PracticeValueKey: FormValidator.validateForm,
|
||||||
|
};
|
||||||
|
|
||||||
|
mixin $CoursePracticeView {
|
||||||
|
TextEditingController get practiceController =>
|
||||||
|
_getFormTextEditingController(PracticeValueKey);
|
||||||
|
|
||||||
|
FocusNode get practiceFocusNode => _getFormFocusNode(PracticeValueKey);
|
||||||
|
|
||||||
|
TextEditingController _getFormTextEditingController(
|
||||||
|
String key, {
|
||||||
|
String? initialValue,
|
||||||
|
}) {
|
||||||
|
if (_CoursePracticeViewTextEditingControllers.containsKey(key)) {
|
||||||
|
return _CoursePracticeViewTextEditingControllers[key]!;
|
||||||
|
}
|
||||||
|
|
||||||
|
_CoursePracticeViewTextEditingControllers[key] =
|
||||||
|
TextEditingController(text: initialValue);
|
||||||
|
return _CoursePracticeViewTextEditingControllers[key]!;
|
||||||
|
}
|
||||||
|
|
||||||
|
FocusNode _getFormFocusNode(String key) {
|
||||||
|
if (_CoursePracticeViewFocusNodes.containsKey(key)) {
|
||||||
|
return _CoursePracticeViewFocusNodes[key]!;
|
||||||
|
}
|
||||||
|
_CoursePracticeViewFocusNodes[key] = FocusNode();
|
||||||
|
return _CoursePracticeViewFocusNodes[key]!;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Registers a listener on every generated controller that calls [model.setData()]
|
||||||
|
/// with the latest textController values
|
||||||
|
void syncFormWithViewModel(FormStateHelper model) {
|
||||||
|
practiceController.addListener(() => _updateFormData(model));
|
||||||
|
|
||||||
|
_updateFormData(model, forceValidate: _autoTextFieldValidation);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Registers a listener on every generated controller that calls [model.setData()]
|
||||||
|
/// with the latest textController values
|
||||||
|
@Deprecated(
|
||||||
|
'Use syncFormWithViewModel instead.'
|
||||||
|
'This feature was deprecated after 3.1.0.',
|
||||||
|
)
|
||||||
|
void listenToFormUpdated(FormViewModel model) {
|
||||||
|
practiceController.addListener(() => _updateFormData(model));
|
||||||
|
|
||||||
|
_updateFormData(model, forceValidate: _autoTextFieldValidation);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Updates the formData on the FormViewModel
|
||||||
|
void _updateFormData(FormStateHelper model, {bool forceValidate = false}) {
|
||||||
|
model.setData(
|
||||||
|
model.formValueMap
|
||||||
|
..addAll({
|
||||||
|
PracticeValueKey: practiceController.text,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (_autoTextFieldValidation || forceValidate) {
|
||||||
|
updateValidationData(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool validateFormFields(FormViewModel model) {
|
||||||
|
_updateFormData(model, forceValidate: true);
|
||||||
|
return model.isFormValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calls dispose on all the generated controllers and focus nodes
|
||||||
|
void disposeForm() {
|
||||||
|
// The dispose function for a TextEditingController sets all listeners to null
|
||||||
|
|
||||||
|
for (var controller in _CoursePracticeViewTextEditingControllers.values) {
|
||||||
|
controller.dispose();
|
||||||
|
}
|
||||||
|
for (var focusNode in _CoursePracticeViewFocusNodes.values) {
|
||||||
|
focusNode.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_CoursePracticeViewTextEditingControllers.clear();
|
||||||
|
_CoursePracticeViewFocusNodes.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ValueProperties on FormStateHelper {
|
||||||
|
bool get hasAnyValidationMessage => this
|
||||||
|
.fieldsValidationMessages
|
||||||
|
.values
|
||||||
|
.any((validation) => validation != null);
|
||||||
|
|
||||||
|
bool get isFormValid {
|
||||||
|
if (!_autoTextFieldValidation) this.validateForm();
|
||||||
|
|
||||||
|
return !hasAnyValidationMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
String? get practiceValue => this.formValueMap[PracticeValueKey] as String?;
|
||||||
|
|
||||||
|
set practiceValue(String? value) {
|
||||||
|
this.setData(
|
||||||
|
this.formValueMap..addAll({PracticeValueKey: value}),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (_CoursePracticeViewTextEditingControllers.containsKey(
|
||||||
|
PracticeValueKey)) {
|
||||||
|
_CoursePracticeViewTextEditingControllers[PracticeValueKey]?.text =
|
||||||
|
value ?? '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get hasPractice =>
|
||||||
|
this.formValueMap.containsKey(PracticeValueKey) &&
|
||||||
|
(practiceValue?.isNotEmpty ?? false);
|
||||||
|
|
||||||
|
bool get hasPracticeValidationMessage =>
|
||||||
|
this.fieldsValidationMessages[PracticeValueKey]?.isNotEmpty ?? false;
|
||||||
|
|
||||||
|
String? get practiceValidationMessage =>
|
||||||
|
this.fieldsValidationMessages[PracticeValueKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Methods on FormStateHelper {
|
||||||
|
void setPracticeValidationMessage(String? validationMessage) =>
|
||||||
|
this.fieldsValidationMessages[PracticeValueKey] = validationMessage;
|
||||||
|
|
||||||
|
/// Clears text input fields on the Form
|
||||||
|
void clearForm() {
|
||||||
|
practiceValue = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validates text input fields on the Form
|
||||||
|
void validateForm() {
|
||||||
|
this.setValidationMessages({
|
||||||
|
PracticeValueKey: getValidationMessage(PracticeValueKey),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the validation message for the given key
|
||||||
|
String? getValidationMessage(String key) {
|
||||||
|
final validatorForKey = _CoursePracticeViewTextValidations[key];
|
||||||
|
if (validatorForKey == null) return null;
|
||||||
|
|
||||||
|
String? validationMessageForKey = validatorForKey(
|
||||||
|
_CoursePracticeViewTextEditingControllers[key]?.text,
|
||||||
|
);
|
||||||
|
|
||||||
|
return validationMessageForKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Updates the fieldsValidationMessages on the FormViewModel
|
||||||
|
void updateValidationData(FormStateHelper model) =>
|
||||||
|
model.setValidationMessages({
|
||||||
|
PracticeValueKey: getValidationMessage(PracticeValueKey),
|
||||||
|
});
|
||||||
536
lib/ui/views/course_practice/course_practice_viewmodel.dart
Normal file
536
lib/ui/views/course_practice/course_practice_viewmodel.dart
Normal file
|
|
@ -0,0 +1,536 @@
|
||||||
|
import 'package:audioplayers/audioplayers.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:stacked/stacked.dart';
|
||||||
|
import 'package:stacked_services/stacked_services.dart';
|
||||||
|
import 'package:waveform_recorder/waveform_recorder.dart';
|
||||||
|
import 'package:yimaru_app/models/course_practice.dart';
|
||||||
|
|
||||||
|
import '../../../app/app.locator.dart';
|
||||||
|
import '../../../models/course_question.dart';
|
||||||
|
import '../../../models/user.dart';
|
||||||
|
import '../../../services/api_service.dart';
|
||||||
|
import '../../../services/audio_player_service.dart';
|
||||||
|
import '../../../services/authentication_service.dart';
|
||||||
|
import '../../../services/course_service.dart';
|
||||||
|
import '../../../services/status_checker_service.dart';
|
||||||
|
import '../../../services/voice_recorder_service.dart';
|
||||||
|
import '../../common/app_colors.dart';
|
||||||
|
import '../../common/enmus.dart';
|
||||||
|
import '../../common/helper_functions.dart';
|
||||||
|
|
||||||
|
class CoursePracticeViewModel extends ReactiveViewModel
|
||||||
|
with FormStateHelper
|
||||||
|
implements FormViewModel {
|
||||||
|
// Dependency injection
|
||||||
|
|
||||||
|
final _apiService = locator<ApiService>();
|
||||||
|
|
||||||
|
final _dialogService = locator<DialogService>();
|
||||||
|
|
||||||
|
final _courseService = locator<CourseService>();
|
||||||
|
|
||||||
|
final _statusChecker = locator<StatusCheckerService>();
|
||||||
|
|
||||||
|
final _navigationService = locator<NavigationService>();
|
||||||
|
|
||||||
|
final _audioPlayerService = locator<AudioPlayerService>();
|
||||||
|
|
||||||
|
final _voiceRecorderService = locator<VoiceRecorderService>();
|
||||||
|
|
||||||
|
final _authenticationService = locator<AuthenticationService>();
|
||||||
|
|
||||||
|
CoursePracticeViewModel() {
|
||||||
|
_listenToAudio();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<ListenableServiceMixin> get listenableServices => [
|
||||||
|
_courseService,
|
||||||
|
_audioPlayerService,
|
||||||
|
_voiceRecorderService,
|
||||||
|
_authenticationService
|
||||||
|
];
|
||||||
|
|
||||||
|
// User
|
||||||
|
User? get _user => _authenticationService.user;
|
||||||
|
|
||||||
|
User? get user => _user;
|
||||||
|
|
||||||
|
// AudioPlayer
|
||||||
|
AudioPlayer get _player => _audioPlayerService.player;
|
||||||
|
|
||||||
|
AudioPlayer get player => _player;
|
||||||
|
|
||||||
|
Duration _duration = Duration.zero;
|
||||||
|
|
||||||
|
Duration _position = Duration.zero;
|
||||||
|
|
||||||
|
Duration get position => _position;
|
||||||
|
|
||||||
|
Duration get duration => _duration;
|
||||||
|
|
||||||
|
double get progress {
|
||||||
|
if (_duration.inMilliseconds == 0) return 0;
|
||||||
|
return _position.inMilliseconds / _duration.inMilliseconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Voice recorder
|
||||||
|
String? _recordedAudio;
|
||||||
|
|
||||||
|
String? get recordedAudio => _recordedAudio;
|
||||||
|
|
||||||
|
WaveformRecorderController get _waveController =>
|
||||||
|
_voiceRecorderService.waveController;
|
||||||
|
|
||||||
|
WaveformRecorderController get waveController => _waveController;
|
||||||
|
|
||||||
|
// Voice recorder state
|
||||||
|
VoiceRecordingState get _recordingState =>
|
||||||
|
_voiceRecorderService.recordingState;
|
||||||
|
|
||||||
|
VoiceRecordingState get recordingState => _recordingState;
|
||||||
|
|
||||||
|
// Busy object
|
||||||
|
String? _busyObject;
|
||||||
|
|
||||||
|
String? get busyObject => _busyObject;
|
||||||
|
|
||||||
|
Voice? _playing;
|
||||||
|
|
||||||
|
Voice? get playing => _playing;
|
||||||
|
|
||||||
|
// Speaking state
|
||||||
|
bool _isSpeaking = false;
|
||||||
|
|
||||||
|
bool get isSpeaking => _isSpeaking;
|
||||||
|
|
||||||
|
// Course practices
|
||||||
|
bool _focusPractice = false;
|
||||||
|
|
||||||
|
bool get focusPractice => _focusPractice;
|
||||||
|
|
||||||
|
List<CoursePractice> _practices = [];
|
||||||
|
|
||||||
|
List<CoursePractice> get practices => _practices;
|
||||||
|
|
||||||
|
// Practice questions
|
||||||
|
String? _refreshedUrl;
|
||||||
|
|
||||||
|
String? get refreshedUrl => _refreshedUrl;
|
||||||
|
|
||||||
|
int _currentQuestion = 0;
|
||||||
|
|
||||||
|
int get currentQuestion => _currentQuestion;
|
||||||
|
|
||||||
|
List<CourseQuestion> _questions = [];
|
||||||
|
|
||||||
|
List<CourseQuestion> get questions => _questions;
|
||||||
|
|
||||||
|
final List<Map<String, dynamic>> _questionParams = [
|
||||||
|
{
|
||||||
|
'label': 'Speaking 01',
|
||||||
|
'type': DuolingoAssessments.speaking,
|
||||||
|
'intro_title': 'Speak About the Photo',
|
||||||
|
'outro_title': 'Speaking Practice Completed',
|
||||||
|
'outro_subtitle': 'You’ve finished this speaking session. Great work!',
|
||||||
|
'intro_subtitle':
|
||||||
|
'Prepare to speak for at least 30 seconds about the photo you are shown'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': 'Speaking 02',
|
||||||
|
'intro_title': 'Read, Then Speak',
|
||||||
|
'type': DuolingoAssessments.speaking,
|
||||||
|
'outro_title': 'Speaking Practice Completed',
|
||||||
|
'intro_subtitle': 'You will speak about the given topic',
|
||||||
|
'outro_subtitle': 'You’ve finished this speaking session. Great work!',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': 'Speaking 03',
|
||||||
|
'intro_title': 'Speaking Sample',
|
||||||
|
'type': DuolingoAssessments.speaking,
|
||||||
|
'outro_title': 'Speaking Practice Completed',
|
||||||
|
'intro_subtitle': 'You’ll speak for 1–3 minutes about a given topic.',
|
||||||
|
'outro_subtitle': 'You’ve finished this speaking session. Great work!',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': 'Speaking 04',
|
||||||
|
'type': DuolingoAssessments.speaking,
|
||||||
|
'intro_title': 'Interactive Speaking',
|
||||||
|
'outro_title': 'Speaking Practice Completed',
|
||||||
|
'intro_subtitle': ' You’ll answer a series of short questions.',
|
||||||
|
'outro_subtitle': 'You’ve finished this speaking session. Great work!',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': 'Writing 05',
|
||||||
|
'type': DuolingoAssessments.writing,
|
||||||
|
'intro_title': 'Write About the Photo',
|
||||||
|
'outro_title': 'Writing Practice Completed',
|
||||||
|
'outro_subtitle': 'You’ve finished this writing session. Great work!',
|
||||||
|
'intro_subtitle':
|
||||||
|
'You will see a picture and write a short description based on what you observe. Focus on clear, simple sentences.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': 'Writing 06',
|
||||||
|
'intro_title': 'Writing Sample',
|
||||||
|
'type': DuolingoAssessments.writing,
|
||||||
|
'outro_title': 'Writing Practice Completed',
|
||||||
|
'outro_subtitle': 'You’ve finished this writing session. Great work!',
|
||||||
|
'intro_subtitle':
|
||||||
|
'You will write a longer response based on a given question. Your writing will be shared with institutions as part of your score.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': 'Writing 07',
|
||||||
|
'type': DuolingoAssessments.writing,
|
||||||
|
'outro_title': 'Writing Practice Completed',
|
||||||
|
'intro_title': 'Interactive Writing Part 1',
|
||||||
|
'outro_subtitle': 'You’ve finished this writing session. Great work!',
|
||||||
|
'intro_subtitle':
|
||||||
|
' You will write short and simple sentences.
Focus on basic ideas and clear meaning.
Write naturally and manage your time.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': 'Writing 08',
|
||||||
|
'type': DuolingoAssessments.writing,
|
||||||
|
'intro_title': 'Interactive Writing Part 2',
|
||||||
|
'outro_title': 'Writing Practice Completed',
|
||||||
|
'outro_subtitle': 'You’ve finished this writing session. Great work!',
|
||||||
|
'intro_subtitle':
|
||||||
|
' You will continue writing on a related idea.
Add more details using clear sentences.
Stay focused and complete your response within the time.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': 'Listening 09',
|
||||||
|
'intro_title': 'Listen and Type',
|
||||||
|
'type': DuolingoAssessments.listening,
|
||||||
|
'outro_title': 'Listening Practice Completed',
|
||||||
|
'intro_subtitle':
|
||||||
|
'You will hear a short audio clip. Type exactly what you hear.',
|
||||||
|
'outro_subtitle': 'You’ve finished this Listening session. Great work!',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': 'Listening 10',
|
||||||
|
'type': DuolingoAssessments.listening,
|
||||||
|
'outro_title': 'Listening Practice Completed',
|
||||||
|
'intro_title': 'Interactive Listening - Part 1',
|
||||||
|
'intro_subtitle': ' Listen carefully and complete the missing words.',
|
||||||
|
'outro_subtitle': 'You’ve finished this Listening session. Great work!',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': 'Listening 11',
|
||||||
|
'type': DuolingoAssessments.listening,
|
||||||
|
'outro_title': 'Listening Practice Completed',
|
||||||
|
'intro_title': 'Interactive Listening - Part 2',
|
||||||
|
'intro_subtitle': 'Listen and choose the correct option.',
|
||||||
|
'outro_subtitle': 'You’ve finished this Listening session. Great work!',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': 'Assessment 12',
|
||||||
|
'type': DuolingoAssessments.listening,
|
||||||
|
'title': 'Interactive Listening - Part 3',
|
||||||
|
'outro_title': 'Listening Practice Completed',
|
||||||
|
'subtitle': 'Write a summary of the conversation you just had',
|
||||||
|
'outro_subtitle': 'You’ve finished this Listening session. Great work!',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': 'Reading 13',
|
||||||
|
'intro_title': 'Read and Select',
|
||||||
|
'type': DuolingoAssessments.reading,
|
||||||
|
'intro_subtitle':
|
||||||
|
'Read the sentence and select the option that correctly completes the meaning.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': 'Reading 14',
|
||||||
|
'intro_title': 'Fill in the blank',
|
||||||
|
'type': DuolingoAssessments.reading,
|
||||||
|
'intro_subtitle': 'Complete the sentences by filling in the missing words'
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
List<Map<String, dynamic>> get questionParams => _questionParams;
|
||||||
|
|
||||||
|
// Selected question param
|
||||||
|
Map<String, dynamic> _selectedQuestionParam = {
|
||||||
|
'label': 'Speaking 01',
|
||||||
|
'intro_title': 'Speak About the Photo',
|
||||||
|
'type': DuolingoAssessments.speaking,
|
||||||
|
'outro_title': 'Speaking Practice Completed',
|
||||||
|
'outro_subtitle': 'You’ve finished this speaking session. Great work!',
|
||||||
|
'intro_subtitle':
|
||||||
|
'Prepare to speak for at least 30 seconds about the photo you are shown',
|
||||||
|
};
|
||||||
|
|
||||||
|
Map<String, dynamic> get selectedQuestionParam => _selectedQuestionParam;
|
||||||
|
|
||||||
|
// Practice answers
|
||||||
|
final List<Map<String, dynamic>> _answers = [];
|
||||||
|
|
||||||
|
List<Map<String, dynamic>> get answers => _answers;
|
||||||
|
|
||||||
|
// Next button state
|
||||||
|
bool _buttonActive = false;
|
||||||
|
|
||||||
|
bool get buttonActive => _buttonActive;
|
||||||
|
|
||||||
|
// In-app navigation
|
||||||
|
int _currentPage = 0;
|
||||||
|
|
||||||
|
int get currentPage => _currentPage;
|
||||||
|
|
||||||
|
final PageController _practiceController = PageController();
|
||||||
|
|
||||||
|
PageController get practiceController => _practiceController;
|
||||||
|
|
||||||
|
final PageController _questionController = PageController();
|
||||||
|
|
||||||
|
PageController get questionController => _questionController;
|
||||||
|
|
||||||
|
// Practice
|
||||||
|
void setPracticeFocus() {
|
||||||
|
_focusPractice = true;
|
||||||
|
rebuildUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Speaking state
|
||||||
|
void setSpeakingState() {
|
||||||
|
_isSpeaking = !_isSpeaking;
|
||||||
|
rebuildUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next button
|
||||||
|
void setNextButton() {
|
||||||
|
_buttonActive = true;
|
||||||
|
rebuildUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Question param
|
||||||
|
Future<void> setQuestionParam(String type) async {
|
||||||
|
print('FIRST QUESTION: $type');
|
||||||
|
if (type == '636') {
|
||||||
|
// await refreshQuestionUrl(_questions[_currentQuestion]);
|
||||||
|
_selectedQuestionParam = _questionParams.elementAt(0);
|
||||||
|
} else if (type == '') {
|
||||||
|
_selectedQuestionParam = _questionParams.elementAt(0);
|
||||||
|
} else if (type == '') {
|
||||||
|
_selectedQuestionParam = _questionParams.elementAt(0);
|
||||||
|
} else if (type == '') {
|
||||||
|
_selectedQuestionParam = _questionParams.elementAt(0);
|
||||||
|
} else if (type == '') {
|
||||||
|
_selectedQuestionParam = _questionParams.elementAt(0);
|
||||||
|
} else if (type == '') {
|
||||||
|
_selectedQuestionParam = _questionParams.elementAt(0);
|
||||||
|
} else if (type == '') {
|
||||||
|
_selectedQuestionParam = _questionParams.elementAt(0);
|
||||||
|
} else if (type == '') {
|
||||||
|
_selectedQuestionParam = _questionParams.elementAt(0);
|
||||||
|
} else if (type == '') {
|
||||||
|
_selectedQuestionParam = _questionParams.elementAt(0);
|
||||||
|
} else if (type == '') {
|
||||||
|
_selectedQuestionParam = _questionParams.elementAt(0);
|
||||||
|
} else if (type == '') {
|
||||||
|
_selectedQuestionParam = _questionParams.elementAt(0);
|
||||||
|
} else if (type == '') {
|
||||||
|
_selectedQuestionParam = _questionParams.elementAt(0);
|
||||||
|
} else if (type == '') {
|
||||||
|
_selectedQuestionParam = _questionParams.elementAt(0);
|
||||||
|
} else {
|
||||||
|
_selectedQuestionParam = _questionParams.elementAt(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Voice recorder
|
||||||
|
Future<void> stopRecording() async {
|
||||||
|
if (_voiceRecorderService.waveController.isRecording) {
|
||||||
|
await _voiceRecorderService.stopRecording();
|
||||||
|
_recordedAudio = await _voiceRecorderService.getRecordedAudio();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> startRecording() async => await runBusyFuture(_startRecording(),
|
||||||
|
busyObject: StateObjects.recordCoursePracticeAnswer);
|
||||||
|
|
||||||
|
Future<void> _startRecording() async =>
|
||||||
|
await _voiceRecorderService.startRecording();
|
||||||
|
|
||||||
|
// Play practice audio
|
||||||
|
void _listenToAudio() {
|
||||||
|
_audioPlayerService.durationStream.listen((dur) {
|
||||||
|
if (dur.inMilliseconds > 0) {
|
||||||
|
_duration = dur;
|
||||||
|
rebuildUi();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_audioPlayerService.positionStream.listen((pos) {
|
||||||
|
_position = pos;
|
||||||
|
rebuildUi();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> playVoicePrompt(CourseQuestion question) async =>
|
||||||
|
await runBusyFuture(_playVoicePrompt(question),
|
||||||
|
busyObject: StateObjects.coursePracticeQuestion);
|
||||||
|
|
||||||
|
Future<void> _playVoicePrompt(CourseQuestion question) async {
|
||||||
|
_questionController.jumpToPage(1);
|
||||||
|
await _audioPlayerService
|
||||||
|
.playUrl(question.dynamicPayload?.stimulus?.first.value ?? '');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> replayVoicePrompt(CourseQuestion question) async {
|
||||||
|
await _audioPlayerService
|
||||||
|
.playUrl(question.dynamicPayload?.stimulus?.first.value ?? '');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> playResult(Voice voice) async {
|
||||||
|
setBusyObject(voice);
|
||||||
|
await playAudio(voice);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> playAudio(Voice voice) async =>
|
||||||
|
await runBusyFuture(_playAudio(voice),
|
||||||
|
busyObject: StateObjects.coursePracticeReview);
|
||||||
|
|
||||||
|
Future<void> _playAudio(Voice voice) async {
|
||||||
|
if (voice == Voice.recorded) {
|
||||||
|
print('RECORDED: ${_recordedAudio ?? ''}');
|
||||||
|
await _audioPlayerService.playLocal(_recordedAudio ?? '');
|
||||||
|
} else {
|
||||||
|
String url = await getRefreshedUrl(_questions[currentQuestion]
|
||||||
|
.dynamicPayload
|
||||||
|
?.response
|
||||||
|
?.last
|
||||||
|
.value ??
|
||||||
|
'') ??
|
||||||
|
'';
|
||||||
|
print('REFRESHED: ${_questions[currentQuestion]
|
||||||
|
.dynamicPayload
|
||||||
|
?.response
|
||||||
|
?.last
|
||||||
|
.value ??
|
||||||
|
''}');
|
||||||
|
await _audioPlayerService.playUrl(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> pauseAudio() async {
|
||||||
|
await _audioPlayerService.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set busy object
|
||||||
|
void setBusyObject(Voice playing) {
|
||||||
|
_playing = playing;
|
||||||
|
rebuildUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dialogue
|
||||||
|
Future<bool?> showAbortDialog() async {
|
||||||
|
DialogResponse? response = await _dialogService.showDialog(
|
||||||
|
cancelTitle: 'No',
|
||||||
|
title: 'Recording',
|
||||||
|
buttonTitle: 'Yes',
|
||||||
|
barrierDismissible: true,
|
||||||
|
cancelTitleColor: kcDarkGrey,
|
||||||
|
buttonTitleColor: kcPrimaryColor,
|
||||||
|
description: 'Are you sure you want to stop recording?',
|
||||||
|
);
|
||||||
|
return response?.confirmed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In-app navigation
|
||||||
|
void nextScreen() {
|
||||||
|
_questionController.nextPage(
|
||||||
|
curve: Curves.easeInOutCubic,
|
||||||
|
duration: const Duration(milliseconds: 350),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nextPage() {
|
||||||
|
_currentPage++;
|
||||||
|
rebuildUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
void goTo(int page) {
|
||||||
|
_currentPage = page;
|
||||||
|
rebuildUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
void goBack() {
|
||||||
|
if (_currentPage == 0) {
|
||||||
|
pop();
|
||||||
|
} else {
|
||||||
|
_currentPage--;
|
||||||
|
rebuildUi();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> nextQuestion(
|
||||||
|
{required int index, required CourseQuestion question}) async =>
|
||||||
|
await runBusyFuture(_nextQuestion(index: index, question: question),
|
||||||
|
busyObject: StateObjects.finishCoursePracticeQuestion);
|
||||||
|
|
||||||
|
Future<void> _nextQuestion(
|
||||||
|
{required int index, required CourseQuestion question}) async {
|
||||||
|
await stopRecording();
|
||||||
|
_answers.add({
|
||||||
|
'busy_object': question.id.toString(),
|
||||||
|
'recorded_voice_answer': await _voiceRecorderService.getRecordedAudio(),
|
||||||
|
'sample_text_answer':
|
||||||
|
question.dynamicPayload?.response?.first.value ?? '',
|
||||||
|
'sample_voice_answer':
|
||||||
|
question.dynamicPayload?.stimulus?.first.value ?? '',
|
||||||
|
});
|
||||||
|
if (index != _questions.length) {
|
||||||
|
_practiceController.nextPage(
|
||||||
|
curve: Curves.easeInOutCubic,
|
||||||
|
duration: const Duration(milliseconds: 350),
|
||||||
|
);
|
||||||
|
await playVoicePrompt(_questions[index]);
|
||||||
|
} else {
|
||||||
|
goTo(3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Navigation
|
||||||
|
void pop() => _navigationService.back();
|
||||||
|
|
||||||
|
// Remote api call
|
||||||
|
|
||||||
|
// Learn practice
|
||||||
|
Future<void> getCoursePractices(
|
||||||
|
{required int id, required CoursePractices practice}) async =>
|
||||||
|
await runBusyFuture(_getCoursePractices(id: id, practice: practice),
|
||||||
|
busyObject: StateObjects.coursePractice);
|
||||||
|
|
||||||
|
Future<void> _getCoursePractices(
|
||||||
|
{required int id, required CoursePractices practice}) async {
|
||||||
|
if (await _statusChecker.checkConnection()) {
|
||||||
|
if (practice == CoursePractices.courseCatalog) {
|
||||||
|
_practices = await _apiService.getCoursePractices(id);
|
||||||
|
// await _getLearnPracticeQuestions(_practices.first.questionSetId ?? 0);
|
||||||
|
} else if (practice == CoursePractices.unit) {
|
||||||
|
_practices = await _apiService.getCoursePractices(id);
|
||||||
|
// await _getLearnPracticeQuestions(_practices.first.questionSetId ?? 0);
|
||||||
|
} else {
|
||||||
|
_practices = await _apiService.getCoursePractices(id);
|
||||||
|
await _getLearnPracticeQuestions(_practices.first.questionSetId ?? 0);
|
||||||
|
await setQuestionParam(
|
||||||
|
_questions[_currentQuestion].id.toString() ?? '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _getLearnPracticeQuestions(int id) async {
|
||||||
|
_questions = await _apiService.getCourseQuestions(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh url
|
||||||
|
Future<String?> getRefreshedUrl(String value) async {
|
||||||
|
final String? refreshedUrl = await _courseService.refreshObject(value);
|
||||||
|
|
||||||
|
if (refreshedUrl != null) {
|
||||||
|
return refreshedUrl;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String getReadableImage(String image)=> getReadableUrl(image) ?? '';
|
||||||
|
}
|
||||||
144
lib/ui/views/course_practice/screens/duolingo_finish_screen.dart
Normal file
144
lib/ui/views/course_practice/screens/duolingo_finish_screen.dart
Normal file
|
|
@ -0,0 +1,144 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:stacked/stacked.dart';
|
||||||
|
|
||||||
|
import '../../../common/app_colors.dart';
|
||||||
|
import '../../../common/ui_helpers.dart';
|
||||||
|
import '../../../widgets/cancel_practice_sheet.dart';
|
||||||
|
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||||
|
import '../../../widgets/custom_elevated_button.dart';
|
||||||
|
import '../course_practice_viewmodel.dart';
|
||||||
|
|
||||||
|
class DuolingoFinishScreen extends ViewModelWidget<CoursePracticeViewModel> {
|
||||||
|
final String title;
|
||||||
|
final String subtitle;
|
||||||
|
|
||||||
|
const DuolingoFinishScreen(
|
||||||
|
{super.key, required this.title, required this.subtitle});
|
||||||
|
|
||||||
|
Future<void> _cancel(CoursePracticeViewModel viewModel) async {
|
||||||
|
await viewModel.stopRecording();
|
||||||
|
viewModel.pop();
|
||||||
|
viewModel.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _showSheet(
|
||||||
|
{required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) async =>
|
||||||
|
await showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
backgroundColor: kcTransparent,
|
||||||
|
builder: (cxt) => _buildSheet(context: context, viewModel: viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||||
|
_buildScaffoldWrapper(context: context, viewModel: viewModel);
|
||||||
|
|
||||||
|
Widget _buildScaffoldWrapper({required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) => Scaffold(
|
||||||
|
backgroundColor: kcBackgroundColor,
|
||||||
|
body: _buildSafeWrapper(context: context, viewModel: viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSafeWrapper({required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) =>
|
||||||
|
SafeArea(child: _buildScaffold(context: context, viewModel: viewModel));
|
||||||
|
|
||||||
|
Widget _buildScaffold({required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: _buildScaffoldChildren(context: context, viewModel: viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildScaffoldChildren({required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) => [
|
||||||
|
_buildAppBar(context: context, viewModel: viewModel),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildExpandedBody(viewModel)
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildExpandedBody(CoursePracticeViewModel viewModel) =>
|
||||||
|
Expanded(child: _buildBodyWrapper(viewModel));
|
||||||
|
|
||||||
|
Widget _buildBodyWrapper(CoursePracticeViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: _buildBody(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildBody(CoursePracticeViewModel viewModel) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: _buildBodyChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildBodyChildren(CoursePracticeViewModel viewModel) =>
|
||||||
|
[_buildUpperColumn(viewModel), _buildContinueButtonWrapper(viewModel)];
|
||||||
|
|
||||||
|
Widget _buildUpperColumn(CoursePracticeViewModel viewModel) => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: _buildUpperColumnChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildUpperColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||||
|
verticalSpaceMassive,
|
||||||
|
_buildIcon(),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildTitle(),
|
||||||
|
verticalSpaceSmall,
|
||||||
|
_buildSubtitle(),
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildAppBar({required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) =>
|
||||||
|
DuolingoPracticeAppBar(
|
||||||
|
onClose: () async =>
|
||||||
|
await _showSheet(context: context, viewModel: viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSheet(
|
||||||
|
{required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) =>
|
||||||
|
CancelPracticeSheet(
|
||||||
|
onClose: viewModel.pop,
|
||||||
|
onContinue: viewModel.pop,
|
||||||
|
user: viewModel.user?.firstName ?? '',
|
||||||
|
onCancel: () async => await _cancel(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildIcon() => SvgPicture.asset(
|
||||||
|
'assets/icons/complete.svg',
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildTitle() => Text(
|
||||||
|
title,
|
||||||
|
style: style25DG600,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSubtitle() => Text(
|
||||||
|
subtitle,
|
||||||
|
style: style14MG400,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
|
child: _buildContinueButton(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildContinueButton(CoursePracticeViewModel viewModel) =>
|
||||||
|
CustomElevatedButton(
|
||||||
|
height: 55,
|
||||||
|
text: 'Continue',
|
||||||
|
borderRadius: 12,
|
||||||
|
onTap: viewModel.pop,
|
||||||
|
backgroundColor: kcWhite,
|
||||||
|
borderColor: kcPrimaryColor,
|
||||||
|
foregroundColor: kcPrimaryColor,
|
||||||
|
);
|
||||||
|
}
|
||||||
158
lib/ui/views/course_practice/screens/duolingo_intro_screen.dart
Normal file
158
lib/ui/views/course_practice/screens/duolingo_intro_screen.dart
Normal file
|
|
@ -0,0 +1,158 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:iconsax/iconsax.dart';
|
||||||
|
import 'package:stacked/stacked.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/duolingo/duolingo_viewmodel.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/duolingo_practice_app_bar.dart';
|
||||||
|
|
||||||
|
import '../../../common/app_colors.dart';
|
||||||
|
import '../../../common/ui_helpers.dart';
|
||||||
|
import '../../../widgets/cancel_practice_sheet.dart';
|
||||||
|
import '../../../widgets/custom_elevated_button.dart';
|
||||||
|
import '../../../widgets/wave_wrapper.dart';
|
||||||
|
import '../course_practice_viewmodel.dart';
|
||||||
|
|
||||||
|
class DuolingoIntroScreen extends ViewModelWidget<CoursePracticeViewModel> {
|
||||||
|
final String title;
|
||||||
|
final String subtitle;
|
||||||
|
final DuolingoAssessments type;
|
||||||
|
|
||||||
|
const DuolingoIntroScreen(
|
||||||
|
{super.key,
|
||||||
|
required this.type,
|
||||||
|
required this.title,
|
||||||
|
required this.subtitle});
|
||||||
|
|
||||||
|
IconData _getIcon() {
|
||||||
|
if (type == DuolingoAssessments.speaking) {
|
||||||
|
return Icons.waves;
|
||||||
|
} else if (type == DuolingoAssessments.writing) {
|
||||||
|
return Iconsax.pen_add;
|
||||||
|
} else if (type == DuolingoAssessments.listening) {
|
||||||
|
return Icons.hearing;
|
||||||
|
} else {
|
||||||
|
return Icons.book;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _cancel(CoursePracticeViewModel viewModel) async {
|
||||||
|
await viewModel.stopRecording();
|
||||||
|
viewModel.pop();
|
||||||
|
viewModel.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _showSheet(
|
||||||
|
{required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) async =>
|
||||||
|
await showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
backgroundColor: kcTransparent,
|
||||||
|
builder: (cxt) => _buildSheet(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||||
|
_buildScaffoldWrapper(context:context, viewModel:viewModel);
|
||||||
|
|
||||||
|
Widget _buildScaffoldWrapper( {required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) => Scaffold(
|
||||||
|
backgroundColor: kcBackgroundColor,
|
||||||
|
body: _buildScaffold(context:context, viewModel:viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildScaffold( {required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) =>
|
||||||
|
SafeArea(child: _buildBodyColumnWrapper(context:context, viewModel:viewModel));
|
||||||
|
|
||||||
|
Widget _buildBodyColumnWrapper( {required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: _buildBodyColumn(context:context, viewModel:viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildBodyColumn( {required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) => Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: _buildBodyColumnChildren(context:context, viewModel:viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildBodyColumnChildren( {required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) => [
|
||||||
|
_buildAppBarWrapper(context:context, viewModel:viewModel),
|
||||||
|
_buildSpeakingIndicatorWrapper(viewModel),
|
||||||
|
_buildContinueButtonWrapper(viewModel)
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildAppBarWrapper( {required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) => Column(
|
||||||
|
children: [
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildAppBar(context:context, viewModel:viewModel),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildAppBar( {required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) => DuolingoPracticeAppBar(
|
||||||
|
title: 'Speaking practice',
|
||||||
|
onClose: () async =>
|
||||||
|
await _showSheet(context: context, viewModel: viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSheet(CoursePracticeViewModel viewModel) => CancelPracticeSheet(
|
||||||
|
onClose: viewModel.pop,
|
||||||
|
onContinue: viewModel.pop,
|
||||||
|
user: viewModel.user?.firstName ?? '',
|
||||||
|
onCancel: () async => await _cancel(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSpeakingIndicatorWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: _buildSpeakingIndicatorChildren(),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildSpeakingIndicatorChildren() => [
|
||||||
|
_buildSpeakingIconWrapper(),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildTitle(),
|
||||||
|
_buildSubtitle(),
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildTitle() => Text(
|
||||||
|
title,
|
||||||
|
style: style18DG700,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSubtitle() => Text(
|
||||||
|
subtitle,
|
||||||
|
style: style14DG400,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSpeakingIconWrapper() =>
|
||||||
|
WaveWrapper(height: 125, child: _buildSpeakingIcon());
|
||||||
|
|
||||||
|
Widget _buildSpeakingIcon() => Icon(
|
||||||
|
_getIcon(),
|
||||||
|
size: 30,
|
||||||
|
color: kcPrimaryColor,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
|
child: _buildContinueButton(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildContinueButton(CoursePracticeViewModel viewModel) =>
|
||||||
|
CustomElevatedButton(
|
||||||
|
height: 55,
|
||||||
|
borderRadius: 12,
|
||||||
|
foregroundColor: kcWhite,
|
||||||
|
text: 'Start Preparation',
|
||||||
|
onTap: viewModel.nextScreen,
|
||||||
|
backgroundColor: kcPrimaryColor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,82 +1,84 @@
|
||||||
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/views/course_practice/course_practice_view.form.dart';
|
||||||
import 'package:yimaru_app/ui/views/duolingo/duolingo_view.form.dart';
|
import 'package:yimaru_app/ui/views/duolingo/duolingo_view.form.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/listenable_assessment_card.dart';
|
import 'package:yimaru_app/ui/widgets/listenable_practice_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_elevated_button.dart';
|
import '../../../widgets/custom_elevated_button.dart';
|
||||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||||
import '../duolingo_viewmodel.dart';
|
import '../course_practice_viewmodel.dart';
|
||||||
|
|
||||||
class DuolingoListeningAssessment1Question
|
class DuolingoListeningPractice1Question
|
||||||
extends ViewModelWidget<DuolingoViewModel> {
|
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||||
final TextEditingController assessmentController;
|
final TextEditingController practiceController;
|
||||||
|
|
||||||
const DuolingoListeningAssessment1Question(
|
const DuolingoListeningPractice1Question(
|
||||||
{super.key, required this.assessmentController});
|
{super.key, required this.practiceController});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||||
_buildScaffoldWrapper(viewModel);
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||||
backgroundColor: kcBackgroundColor,
|
backgroundColor: kcBackgroundColor,
|
||||||
body: _buildScaffold(viewModel),
|
body: _buildScaffold(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||||
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
||||||
|
|
||||||
Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding(
|
Widget _buildBodyColumnWrapper(CoursePracticeViewModel viewModel) => Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildBodyColumn(viewModel),
|
child: _buildBodyColumn(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: _buildBodyColumnChildren(viewModel),
|
children: _buildBodyColumnChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||||
_buildAppBarWrapper(viewModel),
|
_buildAppBarWrapper(viewModel),
|
||||||
_buildQuestionWrapper(viewModel),
|
_buildQuestionWrapper(viewModel),
|
||||||
_buildContinueButtonWrapper(viewModel)
|
_buildContinueButtonWrapper(viewModel)
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||||
children: [
|
children: [
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildAppBar(viewModel),
|
_buildAppBar(viewModel),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||||
title: 'Listening Assessment',
|
DuolingoPracticeAppBar(
|
||||||
|
title: 'Listening practice',
|
||||||
onClose: () => viewModel.goTo(0),
|
onClose: () => viewModel.goTo(0),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildQuestionWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildQuestionWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: _buildQuestionChildren(viewModel),
|
children: _buildQuestionChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildQuestionChildren(DuolingoViewModel viewModel) => [
|
List<Widget> _buildQuestionChildren(CoursePracticeViewModel viewModel) => [
|
||||||
_buildTitle(),
|
_buildTitle(),
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildQuestion(),
|
_buildQuestion(),
|
||||||
verticalSpaceLarge,
|
verticalSpaceLarge,
|
||||||
_buildLabel(),
|
_buildLabel(),
|
||||||
verticalSpaceSmall,
|
verticalSpaceSmall,
|
||||||
_buildAssessmentFormField(viewModel),
|
_buildPracticeFormField(viewModel),
|
||||||
if (viewModel.hasAssessmentValidationMessage &&
|
if (viewModel.hasPracticeValidationMessage &&
|
||||||
viewModel.focusAssessment)
|
viewModel.focusPractice)
|
||||||
verticalSpaceTiny,
|
verticalSpaceTiny,
|
||||||
if (viewModel.hasAssessmentValidationMessage &&
|
if (viewModel.hasPracticeValidationMessage &&
|
||||||
viewModel.focusAssessment)
|
viewModel.focusPractice)
|
||||||
_buildAssessmentWrapper(viewModel),
|
_buildPracticeWrapper(viewModel),
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildTitle() => Text(
|
Widget _buildTitle() => Text(
|
||||||
|
|
@ -85,7 +87,7 @@ class DuolingoListeningAssessment1Question
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildQuestion() => const ListenableAssessmentCard();
|
Widget _buildQuestion() => const ListenablePracticeCard();
|
||||||
|
|
||||||
Widget _buildLabel() => Text(
|
Widget _buildLabel() => Text(
|
||||||
'Your Answer',
|
'Your Answer',
|
||||||
|
|
@ -93,34 +95,35 @@ class DuolingoListeningAssessment1Question
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAssessmentFormField(DuolingoViewModel viewModel) =>
|
Widget _buildPracticeFormField(CoursePracticeViewModel viewModel) =>
|
||||||
TextFormField(
|
TextFormField(
|
||||||
maxLines: 5,
|
maxLines: 5,
|
||||||
maxLength: 250,
|
maxLength: 250,
|
||||||
controller: assessmentController,
|
controller: practiceController,
|
||||||
onTap: viewModel.setAssessmentFocus,
|
onTap: viewModel.setPracticeFocus,
|
||||||
decoration: inputDecoration(
|
decoration: inputDecoration(
|
||||||
focus: true,
|
focus: true,
|
||||||
hint: 'Start writing here...',
|
hint: 'Start writing here...',
|
||||||
filled: assessmentController.text.isNotEmpty),
|
filled: practiceController.text.isNotEmpty),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAssessmentWrapper(DuolingoViewModel viewModel) =>
|
Widget _buildPracticeWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
viewModel.hasAssessmentValidationMessage
|
viewModel.hasPracticeValidationMessage
|
||||||
? _buildAssessmentValidator(viewModel)
|
? _buildPracticeValidator(viewModel)
|
||||||
: Container();
|
: Container();
|
||||||
|
|
||||||
Widget _buildAssessmentValidator(DuolingoViewModel viewModel) => Text(
|
Widget _buildPracticeValidator(CoursePracticeViewModel viewModel) => Text(
|
||||||
viewModel.assessmentValidationMessage!,
|
viewModel.practiceValidationMessage!,
|
||||||
style: style12R700,
|
style: style12R700,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding(
|
Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 50),
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
child: _buildContinueButton(viewModel),
|
child: _buildContinueButton(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildContinueButton(DuolingoViewModel viewModel) =>
|
Widget _buildContinueButton(CoursePracticeViewModel viewModel) =>
|
||||||
CustomElevatedButton(
|
CustomElevatedButton(
|
||||||
height: 55,
|
height: 55,
|
||||||
text: 'Submit',
|
text: 'Submit',
|
||||||
|
|
@ -1,48 +1,48 @@
|
||||||
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/widgets/duolingo_assessment_review_section.dart';
|
import 'package:yimaru_app/ui/widgets/duolingo_practice_review_section.dart';
|
||||||
|
|
||||||
import '../../../common/app_colors.dart';
|
import '../../../common/app_colors.dart';
|
||||||
import '../../../common/ui_helpers.dart';
|
import '../../../common/ui_helpers.dart';
|
||||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||||
import '../../../widgets/listenable_assessment_card.dart';
|
import '../../../widgets/listenable_practice_card.dart';
|
||||||
import '../duolingo_viewmodel.dart';
|
import '../course_practice_viewmodel.dart';
|
||||||
|
|
||||||
class DuolingoListeningAssessment1Review
|
class DuolingoListeningPractice1Review
|
||||||
extends ViewModelWidget<DuolingoViewModel> {
|
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||||
final TextEditingController assessmentController;
|
final TextEditingController practiceController;
|
||||||
|
|
||||||
const DuolingoListeningAssessment1Review(
|
const DuolingoListeningPractice1Review(
|
||||||
{super.key, required this.assessmentController});
|
{super.key, required this.practiceController});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||||
_buildScaffoldWrapper(viewModel);
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||||
backgroundColor: kcBackgroundColor,
|
backgroundColor: kcBackgroundColor,
|
||||||
body: _buildScaffold(viewModel),
|
body: _buildScaffold(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||||
SafeArea(child: _buildBodyColumn(viewModel));
|
SafeArea(child: _buildBodyColumn(viewModel));
|
||||||
|
|
||||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: _buildBodyColumnChildren(viewModel),
|
children: _buildBodyColumnChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||||
_buildAppBarIndenter(viewModel),
|
_buildAppBarIndenter(viewModel),
|
||||||
_buildExpandedBody(viewModel),
|
_buildExpandedBody(viewModel),
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildAppBarIndenter(DuolingoViewModel viewModel) => Padding(
|
Widget _buildAppBarIndenter(CoursePracticeViewModel viewModel) => Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildAppBarWrapper(viewModel),
|
child: _buildAppBarWrapper(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||||
children: [
|
children: [
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildAppBar(viewModel),
|
_buildAppBar(viewModel),
|
||||||
|
|
@ -50,20 +50,22 @@ class DuolingoListeningAssessment1Review
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||||
|
DuolingoPracticeAppBar(
|
||||||
title: 'Feedback',
|
title: 'Feedback',
|
||||||
onClose: () => viewModel.goTo(0),
|
onClose: () => viewModel.goTo(0),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildExpandedBody(DuolingoViewModel viewModel) =>
|
Widget _buildExpandedBody(CoursePracticeViewModel viewModel) =>
|
||||||
Expanded(child: _buildBodyScroller(viewModel));
|
Expanded(child: _buildBodyScroller(viewModel));
|
||||||
|
|
||||||
Widget _buildBodyScroller(DuolingoViewModel viewModel) =>
|
Widget _buildBodyScroller(CoursePracticeViewModel viewModel) =>
|
||||||
SingleChildScrollView(
|
SingleChildScrollView(
|
||||||
child: _buildQuestionSectionWrapper(viewModel),
|
child: _buildQuestionSectionWrapper(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildQuestionSectionWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildQuestionSectionWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
|
Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
|
@ -71,7 +73,7 @@ class DuolingoListeningAssessment1Review
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildQuestionQuestionSectionChildren(
|
List<Widget> _buildQuestionQuestionSectionChildren(
|
||||||
DuolingoViewModel viewModel) =>
|
CoursePracticeViewModel viewModel) =>
|
||||||
[
|
[
|
||||||
_buildTitleWrapper(),
|
_buildTitleWrapper(),
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
|
|
@ -100,7 +102,7 @@ class DuolingoListeningAssessment1Review
|
||||||
child: _buildQuestion(),
|
child: _buildQuestion(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildQuestion() => const ListenableAssessmentCard();
|
Widget _buildQuestion() => const ListenablePracticeCard();
|
||||||
|
|
||||||
Widget _buildLabelWrapper() => Padding(
|
Widget _buildLabelWrapper() => Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
|
@ -113,24 +115,24 @@ class DuolingoListeningAssessment1Review
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAssessmentFormFieldWrapper(DuolingoViewModel viewModel) =>
|
Widget _buildAssessmentFormFieldWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildAssessmentFormField(viewModel),
|
child: _buildAssessmentFormField(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAssessmentFormField(DuolingoViewModel viewModel) =>
|
Widget _buildAssessmentFormField(CoursePracticeViewModel viewModel) =>
|
||||||
TextFormField(
|
TextFormField(
|
||||||
maxLines: 5,
|
maxLines: 5,
|
||||||
maxLength: 250,
|
maxLength: 250,
|
||||||
controller: assessmentController,
|
controller: practiceController,
|
||||||
onTap: viewModel.setAssessmentFocus,
|
onTap: viewModel.setPracticeFocus,
|
||||||
decoration: inputDecoration(
|
decoration: inputDecoration(
|
||||||
focus: true,
|
focus: true,
|
||||||
hint: 'Start writing here...',
|
hint: 'Start writing here...',
|
||||||
filled: assessmentController.text.isNotEmpty),
|
filled: practiceController.text.isNotEmpty),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAssessmentReviewSection(DuolingoViewModel viewModel) =>
|
Widget _buildAssessmentReviewSection(CoursePracticeViewModel viewModel) =>
|
||||||
DuolingoAssessmentReviewSection(onTap: () => viewModel.goTo(5));
|
DuolingoPracticeReviewSection(onTap: () => viewModel.goTo(5));
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,153 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:stacked/stacked.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/listenable_practice_card.dart';
|
||||||
|
|
||||||
|
import '../../../common/app_colors.dart';
|
||||||
|
import '../../../common/ui_helpers.dart';
|
||||||
|
import '../../../widgets/custom_elevated_button.dart';
|
||||||
|
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||||
|
import '../course_practice_viewmodel.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/course_practice_view.form.dart';
|
||||||
|
|
||||||
|
|
||||||
|
class DuolingoListeningPractice2Question
|
||||||
|
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||||
|
final TextEditingController practiceController;
|
||||||
|
|
||||||
|
const DuolingoListeningPractice2Question(
|
||||||
|
{super.key, required this.practiceController});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||||
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
|
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||||
|
backgroundColor: kcBackgroundColor,
|
||||||
|
body: _buildScaffold(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||||
|
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
||||||
|
|
||||||
|
Widget _buildBodyColumnWrapper(CoursePracticeViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: _buildBodyColumn(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: _buildBodyColumnChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||||
|
_buildAppBarWrapper(viewModel),
|
||||||
|
_buildQuestionWrapper(viewModel),
|
||||||
|
_buildContinueButtonWrapper(viewModel)
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||||
|
children: [
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildAppBar(viewModel),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||||
|
DuolingoPracticeAppBar(
|
||||||
|
title: 'Listening practice',
|
||||||
|
onClose: () => viewModel.goTo(0),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildQuestionWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: _buildQuestionChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildQuestionChildren(CoursePracticeViewModel viewModel) => [
|
||||||
|
_buildTitle(),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildQuestion(),
|
||||||
|
verticalSpaceLarge,
|
||||||
|
_buildLabel('1. Focus of the conference:'),
|
||||||
|
verticalSpaceTiny,
|
||||||
|
_buildPracticeFormField(viewModel),
|
||||||
|
if (viewModel.hasPracticeValidationMessage &&
|
||||||
|
viewModel.focusPractice)
|
||||||
|
verticalSpaceTiny,
|
||||||
|
if (viewModel.hasPracticeValidationMessage &&
|
||||||
|
viewModel.focusPractice)
|
||||||
|
_buildPracticeWrapper(viewModel),
|
||||||
|
verticalSpaceSmall,
|
||||||
|
_buildLabel('2. Number of presentations:'),
|
||||||
|
verticalSpaceTiny,
|
||||||
|
_buildPracticeFormField(viewModel),
|
||||||
|
if (viewModel.hasPracticeValidationMessage &&
|
||||||
|
viewModel.focusPractice)
|
||||||
|
verticalSpaceTiny,
|
||||||
|
if (viewModel.hasPracticeValidationMessage &&
|
||||||
|
viewModel.focusPractice)
|
||||||
|
_buildPracticeWrapper(viewModel),
|
||||||
|
verticalSpaceSmall,
|
||||||
|
_buildLabel('3. Keynote speaker’s field:'),
|
||||||
|
verticalSpaceTiny,
|
||||||
|
_buildPracticeFormField(viewModel),
|
||||||
|
if (viewModel.hasPracticeValidationMessage &&
|
||||||
|
viewModel.focusPractice)
|
||||||
|
verticalSpaceTiny,
|
||||||
|
if (viewModel.hasPracticeValidationMessage &&
|
||||||
|
viewModel.focusPractice)
|
||||||
|
_buildPracticeWrapper(viewModel),
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildTitle() => Text(
|
||||||
|
'Listen to the audio message and type your response.',
|
||||||
|
style: style18DG700,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildQuestion() => const ListenablePracticeCard();
|
||||||
|
|
||||||
|
Widget _buildLabel(String question) => Text(
|
||||||
|
question,
|
||||||
|
style: style14MG400,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildPracticeFormField(CoursePracticeViewModel viewModel) =>
|
||||||
|
TextFormField(
|
||||||
|
controller: practiceController,
|
||||||
|
onTap: viewModel.setPracticeFocus,
|
||||||
|
decoration: inputDecoration(
|
||||||
|
focus: true,
|
||||||
|
hint: 'Start writing here...',
|
||||||
|
filled: practiceController.text.isNotEmpty),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildPracticeWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
|
viewModel.hasPracticeValidationMessage
|
||||||
|
? _buildPracticeValidator(viewModel)
|
||||||
|
: Container();
|
||||||
|
|
||||||
|
Widget _buildPracticeValidator(CoursePracticeViewModel viewModel) => Text(
|
||||||
|
viewModel.practiceValidationMessage!,
|
||||||
|
style: style12R700,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
|
child: _buildContinueButton(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildContinueButton(CoursePracticeViewModel viewModel) =>
|
||||||
|
CustomElevatedButton(
|
||||||
|
height: 55,
|
||||||
|
text: 'Submit',
|
||||||
|
borderRadius: 12,
|
||||||
|
foregroundColor: kcWhite,
|
||||||
|
onTap: () => viewModel.goTo(4),
|
||||||
|
backgroundColor: kcPrimaryColor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,48 +1,48 @@
|
||||||
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/widgets/duolingo_assessment_review_section.dart';
|
import 'package:yimaru_app/ui/widgets/duolingo_practice_review_section.dart';
|
||||||
|
|
||||||
import '../../../common/app_colors.dart';
|
import '../../../common/app_colors.dart';
|
||||||
import '../../../common/ui_helpers.dart';
|
import '../../../common/ui_helpers.dart';
|
||||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||||
import '../../../widgets/listenable_assessment_card.dart';
|
import '../../../widgets/listenable_practice_card.dart';
|
||||||
import '../duolingo_viewmodel.dart';
|
import '../course_practice_viewmodel.dart';
|
||||||
|
|
||||||
class DuolingoListeningAssessment2Review
|
class DuolingoListeningPractice2Review
|
||||||
extends ViewModelWidget<DuolingoViewModel> {
|
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||||
final TextEditingController assessmentController;
|
final TextEditingController practiceController;
|
||||||
|
|
||||||
const DuolingoListeningAssessment2Review(
|
const DuolingoListeningPractice2Review(
|
||||||
{super.key, required this.assessmentController});
|
{super.key, required this.practiceController});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||||
_buildScaffoldWrapper(viewModel);
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||||
backgroundColor: kcBackgroundColor,
|
backgroundColor: kcBackgroundColor,
|
||||||
body: _buildScaffold(viewModel),
|
body: _buildScaffold(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||||
SafeArea(child: _buildBodyColumn(viewModel));
|
SafeArea(child: _buildBodyColumn(viewModel));
|
||||||
|
|
||||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: _buildBodyColumnChildren(viewModel),
|
children: _buildBodyColumnChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||||
_buildAppBarIndenter(viewModel),
|
_buildAppBarIndenter(viewModel),
|
||||||
_buildExpandedBody(viewModel),
|
_buildExpandedBody(viewModel),
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildAppBarIndenter(DuolingoViewModel viewModel) => Padding(
|
Widget _buildAppBarIndenter(CoursePracticeViewModel viewModel) => Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildAppBarWrapper(viewModel),
|
child: _buildAppBarWrapper(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||||
children: [
|
children: [
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildAppBar(viewModel),
|
_buildAppBar(viewModel),
|
||||||
|
|
@ -50,20 +50,22 @@ class DuolingoListeningAssessment2Review
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||||
|
DuolingoPracticeAppBar(
|
||||||
title: 'Feedback',
|
title: 'Feedback',
|
||||||
onClose: () => viewModel.goTo(0),
|
onClose: () => viewModel.goTo(0),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildExpandedBody(DuolingoViewModel viewModel) =>
|
Widget _buildExpandedBody(CoursePracticeViewModel viewModel) =>
|
||||||
Expanded(child: _buildBodyScroller(viewModel));
|
Expanded(child: _buildBodyScroller(viewModel));
|
||||||
|
|
||||||
Widget _buildBodyScroller(DuolingoViewModel viewModel) =>
|
Widget _buildBodyScroller(CoursePracticeViewModel viewModel) =>
|
||||||
SingleChildScrollView(
|
SingleChildScrollView(
|
||||||
child: _buildQuestionSectionWrapper(viewModel),
|
child: _buildQuestionSectionWrapper(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildQuestionSectionWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildQuestionSectionWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
|
Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
|
@ -71,7 +73,7 @@ class DuolingoListeningAssessment2Review
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildQuestionQuestionSectionChildren(
|
List<Widget> _buildQuestionQuestionSectionChildren(
|
||||||
DuolingoViewModel viewModel) =>
|
CoursePracticeViewModel viewModel) =>
|
||||||
[
|
[
|
||||||
_buildTitleWrapper(),
|
_buildTitleWrapper(),
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
|
|
@ -79,17 +81,17 @@ class DuolingoListeningAssessment2Review
|
||||||
verticalSpaceLarge,
|
verticalSpaceLarge,
|
||||||
_buildLabelWrapper('1. Focus of the conference:'),
|
_buildLabelWrapper('1. Focus of the conference:'),
|
||||||
verticalSpaceTiny,
|
verticalSpaceTiny,
|
||||||
_buildAssessmentFormFieldWrapper(viewModel),
|
_buildPracticeFormFieldWrapper(viewModel),
|
||||||
verticalSpaceSmall,
|
verticalSpaceSmall,
|
||||||
_buildLabelWrapper('2. Number of presentations:'),
|
_buildLabelWrapper('2. Number of presentations:'),
|
||||||
verticalSpaceTiny,
|
verticalSpaceTiny,
|
||||||
_buildAssessmentFormFieldWrapper(viewModel),
|
_buildPracticeFormFieldWrapper(viewModel),
|
||||||
verticalSpaceSmall,
|
verticalSpaceSmall,
|
||||||
_buildLabelWrapper('3. Keynote speaker’s field:'),
|
_buildLabelWrapper('3. Keynote speaker’s field:'),
|
||||||
verticalSpaceTiny,
|
verticalSpaceTiny,
|
||||||
_buildAssessmentFormFieldWrapper(viewModel),
|
_buildPracticeFormFieldWrapper(viewModel),
|
||||||
verticalSpaceLarge,
|
verticalSpaceLarge,
|
||||||
_buildAssessmentReviewSection(viewModel)
|
_buildPracticeReviewSection(viewModel)
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildTitleWrapper() => Padding(
|
Widget _buildTitleWrapper() => Padding(
|
||||||
|
|
@ -108,7 +110,7 @@ class DuolingoListeningAssessment2Review
|
||||||
child: _buildQuestion(),
|
child: _buildQuestion(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildQuestion() => const ListenableAssessmentCard();
|
Widget _buildQuestion() => const ListenablePracticeCard();
|
||||||
|
|
||||||
Widget _buildLabelWrapper(String question) => Padding(
|
Widget _buildLabelWrapper(String question) => Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
|
@ -121,22 +123,22 @@ class DuolingoListeningAssessment2Review
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAssessmentFormFieldWrapper(DuolingoViewModel viewModel) =>
|
Widget _buildPracticeFormFieldWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildAssessmentFormField(viewModel),
|
child: _buildPracticeFormField(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAssessmentFormField(DuolingoViewModel viewModel) =>
|
Widget _buildPracticeFormField(CoursePracticeViewModel viewModel) =>
|
||||||
TextFormField(
|
TextFormField(
|
||||||
controller: assessmentController,
|
controller: practiceController,
|
||||||
onTap: viewModel.setAssessmentFocus,
|
onTap: viewModel.setPracticeFocus,
|
||||||
decoration: inputDecoration(
|
decoration: inputDecoration(
|
||||||
focus: true,
|
focus: true,
|
||||||
hint: 'Start writing here...',
|
hint: 'Start writing here...',
|
||||||
filled: assessmentController.text.isNotEmpty),
|
filled: practiceController.text.isNotEmpty),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAssessmentReviewSection(DuolingoViewModel viewModel) =>
|
Widget _buildPracticeReviewSection(CoursePracticeViewModel viewModel) =>
|
||||||
DuolingoAssessmentReviewSection(onTap: () => viewModel.goTo(5));
|
DuolingoPracticeReviewSection(onTap: () => viewModel.goTo(5));
|
||||||
}
|
}
|
||||||
|
|
@ -1,85 +1,86 @@
|
||||||
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/widgets/dwarf_tile.dart';
|
import 'package:yimaru_app/ui/widgets/dwarf_tile.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/listenable_assessment_card.dart';
|
import 'package:yimaru_app/ui/widgets/listenable_practice_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_elevated_button.dart';
|
import '../../../widgets/custom_elevated_button.dart';
|
||||||
import '../../../widgets/custom_small_radio_button.dart';
|
import '../../../widgets/custom_small_radio_button.dart';
|
||||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||||
import '../duolingo_viewmodel.dart';
|
import '../course_practice_viewmodel.dart';
|
||||||
|
|
||||||
class DuolingoListeningAssessment3Question
|
class DuolingoListeningPractice3Question
|
||||||
extends ViewModelWidget<DuolingoViewModel> {
|
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||||
final TextEditingController assessmentController;
|
final TextEditingController practiceController;
|
||||||
|
|
||||||
const DuolingoListeningAssessment3Question(
|
const DuolingoListeningPractice3Question(
|
||||||
{super.key, required this.assessmentController});
|
{super.key, required this.practiceController});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||||
_buildScaffoldWrapper(viewModel);
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||||
backgroundColor: kcBackgroundColor,
|
backgroundColor: kcBackgroundColor,
|
||||||
body: _buildScaffold(viewModel),
|
body: _buildScaffold(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||||
SafeArea(child: _buildBodyColumn(viewModel));
|
SafeArea(child: _buildBodyColumn(viewModel));
|
||||||
|
|
||||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: _buildBodyColumnChildren(viewModel),
|
children: _buildBodyColumnChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||||
_buildAppBarWrapper(viewModel),
|
_buildAppBarWrapper(viewModel),
|
||||||
_buildExpandedBody(viewModel),
|
_buildExpandedBody(viewModel),
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: _buildAppBarChildren(viewModel),
|
children: _buildAppBarChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildAppBarChildren(DuolingoViewModel viewModel) => [
|
List<Widget> _buildAppBarChildren(CoursePracticeViewModel viewModel) => [
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildAppBarIndenter(viewModel),
|
_buildAppBarIndenter(viewModel),
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildAppBarIndenter(DuolingoViewModel viewModel) => Padding(
|
Widget _buildAppBarIndenter(CoursePracticeViewModel viewModel) => Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildAppBar(viewModel),
|
child: _buildAppBar(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||||
title: 'Listening Assessment',
|
DuolingoPracticeAppBar(
|
||||||
|
title: 'Listening practice',
|
||||||
onClose: () => viewModel.goTo(0),
|
onClose: () => viewModel.goTo(0),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildExpandedBody(DuolingoViewModel viewModel) =>
|
Widget _buildExpandedBody(CoursePracticeViewModel viewModel) =>
|
||||||
Expanded(child: _buildBodyScroller(viewModel));
|
Expanded(child: _buildBodyScroller(viewModel));
|
||||||
|
|
||||||
Widget _buildBodyScroller(DuolingoViewModel viewModel) =>
|
Widget _buildBodyScroller(CoursePracticeViewModel viewModel) =>
|
||||||
SingleChildScrollView(
|
SingleChildScrollView(
|
||||||
child: _buildQuestionIndenter(viewModel),
|
child: _buildQuestionIndenter(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildQuestionIndenter(DuolingoViewModel viewModel) => Padding(
|
Widget _buildQuestionIndenter(CoursePracticeViewModel viewModel) => Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildQuestionWrapper(viewModel),
|
child: _buildQuestionWrapper(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildQuestionWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildQuestionWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: _buildQuestionChildren(viewModel),
|
children: _buildQuestionChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildQuestionChildren(DuolingoViewModel viewModel) => [
|
List<Widget> _buildQuestionChildren(CoursePracticeViewModel viewModel) => [
|
||||||
verticalSpaceLarge,
|
verticalSpaceLarge,
|
||||||
_buildTitle(),
|
_buildTitle(),
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
|
|
@ -87,7 +88,7 @@ class DuolingoListeningAssessment3Question
|
||||||
verticalSpaceLarge,
|
verticalSpaceLarge,
|
||||||
_buildLabel(),
|
_buildLabel(),
|
||||||
verticalSpaceTiny,
|
verticalSpaceTiny,
|
||||||
_buildAssessmentQuestions(viewModel),
|
// _buildPracticeQuestions(viewModel),
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildContinueButtonWrapper(viewModel)
|
_buildContinueButtonWrapper(viewModel)
|
||||||
];
|
];
|
||||||
|
|
@ -99,10 +100,10 @@ class DuolingoListeningAssessment3Question
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildQuestion() =>
|
Widget _buildQuestion() =>
|
||||||
DwarfTile(small: true, child: _buildListenableAssessmentCard());
|
DwarfTile(small: true, child: _buildListenablePracticeCard());
|
||||||
|
|
||||||
Widget _buildListenableAssessmentCard() =>
|
Widget _buildListenablePracticeCard() =>
|
||||||
const Expanded(child: ListenableAssessmentCard());
|
const Expanded(child: ListenablePracticeCard());
|
||||||
|
|
||||||
Widget _buildLabel() => Text(
|
Widget _buildLabel() => Text(
|
||||||
'Select the best response',
|
'Select the best response',
|
||||||
|
|
@ -110,21 +111,21 @@ class DuolingoListeningAssessment3Question
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAssessmentQuestions(DuolingoViewModel viewModel) =>
|
// Widget _buildPracticeQuestions(CoursePracticeViewModel viewModel) =>
|
||||||
ListView.builder(
|
// ListView.builder(
|
||||||
shrinkWrap: true,
|
// shrinkWrap: true,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
// physics: const NeverScrollableScrollPhysics(),
|
||||||
itemCount: viewModel.listeningAssessments.length,
|
// itemCount: viewModel.questions.length,
|
||||||
itemBuilder: (context, index) => _buildAssessmentCard(
|
// itemBuilder: (context, index) => _buildAssessmentCard(
|
||||||
title: viewModel.listeningAssessments[index],
|
// title: viewModel.listeningAssessments[index],
|
||||||
selected: viewModel.isSelectedListeningAssessment(
|
// selected: viewModel.isSelectedListeningAssessment(
|
||||||
viewModel.listeningAssessments[index]),
|
// viewModel.listeningAssessments[index]),
|
||||||
onTap: () => viewModel.setSelectedListeningAssessment(
|
// onTap: () => viewModel.setSelectedListeningAssessment(
|
||||||
viewModel.listeningAssessments[index]),
|
// viewModel.listeningAssessments[index]),
|
||||||
),
|
// ),
|
||||||
);
|
// );
|
||||||
|
|
||||||
Widget _buildAssessmentCard(
|
Widget _buildPracticeCard(
|
||||||
{required String title,
|
{required String title,
|
||||||
required bool selected,
|
required bool selected,
|
||||||
required GestureTapCallback onTap}) =>
|
required GestureTapCallback onTap}) =>
|
||||||
|
|
@ -134,12 +135,13 @@ class DuolingoListeningAssessment3Question
|
||||||
selected: selected,
|
selected: selected,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding(
|
Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 50),
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
child: _buildContinueButton(viewModel),
|
child: _buildContinueButton(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildContinueButton(DuolingoViewModel viewModel) =>
|
Widget _buildContinueButton(CoursePracticeViewModel viewModel) =>
|
||||||
CustomElevatedButton(
|
CustomElevatedButton(
|
||||||
height: 55,
|
height: 55,
|
||||||
text: 'Submit',
|
text: 'Submit',
|
||||||
|
|
@ -3,31 +3,32 @@ import 'package:stacked/stacked.dart';
|
||||||
|
|
||||||
import '../../../common/app_colors.dart';
|
import '../../../common/app_colors.dart';
|
||||||
import '../../../common/ui_helpers.dart';
|
import '../../../common/ui_helpers.dart';
|
||||||
import '../../../widgets/duolingo_assessment_card.dart';
|
import '../../../widgets/duolingo_practice_card.dart';
|
||||||
import '../../../widgets/small_app_bar.dart';
|
import '../../../widgets/small_app_bar.dart';
|
||||||
import '../duolingo_viewmodel.dart';
|
import '../course_practice_viewmodel.dart';
|
||||||
|
|
||||||
class DuolingoAssessmentsScreens extends ViewModelWidget<DuolingoViewModel> {
|
class DuolingoPracticesScreens
|
||||||
const DuolingoAssessmentsScreens({super.key});
|
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||||
|
const DuolingoPracticesScreens({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||||
_buildScaffoldWrapper(viewModel);
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||||
backgroundColor: kcBackgroundColor,
|
backgroundColor: kcBackgroundColor,
|
||||||
body: _buildScaffold(viewModel),
|
body: _buildScaffold(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||||
SafeArea(child: _buildBody(viewModel));
|
SafeArea(child: _buildBody(viewModel));
|
||||||
|
|
||||||
Widget _buildBody(DuolingoViewModel viewModel) => Padding(
|
Widget _buildBody(CoursePracticeViewModel viewModel) => Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildColumn(viewModel),
|
child: _buildColumn(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildColumn(DuolingoViewModel viewModel) => Column(
|
Widget _buildColumn(CoursePracticeViewModel viewModel) => Column(
|
||||||
children: [
|
children: [
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildAppBar(viewModel),
|
_buildAppBar(viewModel),
|
||||||
|
|
@ -36,25 +37,27 @@ class DuolingoAssessmentsScreens extends ViewModelWidget<DuolingoViewModel> {
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAppBar(DuolingoViewModel viewModel) => const SmallAppBar(
|
Widget _buildAppBar(CoursePracticeViewModel viewModel) => const SmallAppBar(
|
||||||
showBackButton: false,
|
showBackButton: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildPracticeColumnWrapper(DuolingoViewModel viewModel) =>
|
Widget _buildPracticeColumnWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
Expanded(child: _buildPracticeColumnScrollView(viewModel));
|
Expanded(child: _buildPracticeColumnScrollView(viewModel));
|
||||||
|
|
||||||
Widget _buildPracticeColumnScrollView(DuolingoViewModel viewModel) =>
|
Widget _buildPracticeColumnScrollView(CoursePracticeViewModel viewModel) =>
|
||||||
SingleChildScrollView(
|
SingleChildScrollView(
|
||||||
child: _buildPracticeColumn(viewModel),
|
child: _buildPracticeColumn(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildPracticeColumn(DuolingoViewModel viewModel) => Column(
|
Widget _buildPracticeColumn(CoursePracticeViewModel viewModel) => Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: _buildPracticeColumnChildren(viewModel),
|
children: _buildPracticeColumnChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildPracticeColumnChildren(DuolingoViewModel viewModel) => [
|
List<Widget> _buildPracticeColumnChildren(
|
||||||
|
CoursePracticeViewModel viewModel) =>
|
||||||
|
[
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildTitle(),
|
_buildTitle(),
|
||||||
_buildSubtitle(),
|
_buildSubtitle(),
|
||||||
|
|
@ -72,14 +75,15 @@ class DuolingoAssessmentsScreens extends ViewModelWidget<DuolingoViewModel> {
|
||||||
style: style14DG400,
|
style: style14DG400,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildListView(DuolingoViewModel viewModel) => GridView.builder(
|
Widget _buildListView(CoursePracticeViewModel viewModel) => GridView.builder(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: viewModel.assessments.length,
|
itemCount: viewModel.practices.length,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
itemBuilder: (context, index) => _buildCard(
|
itemBuilder: (context, index) => _buildCard(
|
||||||
title: viewModel.assessments[index]['label'],
|
title: viewModel.practices[index].title ?? '', onTap: () {}
|
||||||
onTap: () => viewModel.setSelectedAssessment(
|
// onTap: () => viewModel.setSelectedAssessment(
|
||||||
page: 1, assessment: viewModel.assessments[index])),
|
// page: 1, assessment: viewModel.assessments[index])
|
||||||
|
),
|
||||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
crossAxisCount: 2,
|
crossAxisCount: 2,
|
||||||
mainAxisSpacing: 15,
|
mainAxisSpacing: 15,
|
||||||
|
|
@ -92,7 +96,7 @@ class DuolingoAssessmentsScreens extends ViewModelWidget<DuolingoViewModel> {
|
||||||
required String title,
|
required String title,
|
||||||
required GestureTapCallback onTap,
|
required GestureTapCallback onTap,
|
||||||
}) =>
|
}) =>
|
||||||
DuolingoAssessmentCard(
|
DuolingoPracticeCard(
|
||||||
title: title,
|
title: title,
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
);
|
);
|
||||||
166
lib/ui/views/course_practice/screens/duolingo_retake_screen.dart
Normal file
166
lib/ui/views/course_practice/screens/duolingo_retake_screen.dart
Normal file
|
|
@ -0,0 +1,166 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:stacked/stacked.dart';
|
||||||
|
|
||||||
|
import '../../../common/app_colors.dart';
|
||||||
|
import '../../../common/ui_helpers.dart';
|
||||||
|
import '../../../widgets/cancel_practice_sheet.dart';
|
||||||
|
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||||
|
import '../../../widgets/custom_elevated_button.dart';
|
||||||
|
import '../course_practice_viewmodel.dart';
|
||||||
|
|
||||||
|
class DuolingoRetakeScreen extends ViewModelWidget<CoursePracticeViewModel> {
|
||||||
|
final String title;
|
||||||
|
final String subtitle;
|
||||||
|
|
||||||
|
const DuolingoRetakeScreen(
|
||||||
|
{super.key, required this.title, required this.subtitle});
|
||||||
|
|
||||||
|
Future<void> _cancel(CoursePracticeViewModel viewModel) async {
|
||||||
|
await viewModel.stopRecording();
|
||||||
|
viewModel.pop();
|
||||||
|
viewModel.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _showSheet(
|
||||||
|
{required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) async =>
|
||||||
|
await showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
backgroundColor: kcTransparent,
|
||||||
|
builder: (cxt) => _buildSheet(context: context, viewModel: viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||||
|
_buildScaffoldWrapper(context: context, viewModel: viewModel);
|
||||||
|
|
||||||
|
Widget _buildScaffoldWrapper({required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) => Scaffold(
|
||||||
|
backgroundColor: kcBackgroundColor,
|
||||||
|
body: _buildSafeWrapper(context: context, viewModel: viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSafeWrapper({required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) =>
|
||||||
|
SafeArea(child: _buildScaffold(context: context, viewModel: viewModel));
|
||||||
|
|
||||||
|
Widget _buildScaffold({required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: _buildScaffoldChildren(context: context, viewModel: viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildScaffoldChildren({required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) => [
|
||||||
|
_buildAppBar(context: context, viewModel: viewModel),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildExpandedBody(viewModel)
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildExpandedBody( CoursePracticeViewModel viewModel) =>
|
||||||
|
Expanded(child: _buildBodyWrapper(viewModel));
|
||||||
|
|
||||||
|
Widget _buildBodyWrapper( CoursePracticeViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: _buildBody(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildBody( CoursePracticeViewModel viewModel) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: _buildBodyChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildBodyChildren(
|
||||||
|
CoursePracticeViewModel viewModel) =>
|
||||||
|
[_buildUpperColumn(viewModel), _buildLowerColumn(viewModel)];
|
||||||
|
|
||||||
|
Widget _buildUpperColumn( CoursePracticeViewModel viewModel) => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: _buildUpperColumnChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildUpperColumnChildren( CoursePracticeViewModel viewModel) => [
|
||||||
|
verticalSpaceMassive,
|
||||||
|
_buildIcon(),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildTitle(),
|
||||||
|
verticalSpaceSmall,
|
||||||
|
_buildSubtitle(),
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildAppBar( {required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) =>
|
||||||
|
DuolingoPracticeAppBar(
|
||||||
|
onClose: () async =>
|
||||||
|
await _showSheet(context: context, viewModel: viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSheet(
|
||||||
|
{required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) =>
|
||||||
|
CancelPracticeSheet(
|
||||||
|
onClose: viewModel.pop,
|
||||||
|
onContinue: viewModel.pop,
|
||||||
|
user: viewModel.user?.firstName ?? '',
|
||||||
|
onCancel: () async => await _cancel(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildIcon() => SvgPicture.asset(
|
||||||
|
'assets/icons/complete.svg',
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildTitle() => Text(
|
||||||
|
title,
|
||||||
|
style: style25DG600,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSubtitle() => Text(
|
||||||
|
subtitle,
|
||||||
|
style: style14MG400,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildLowerColumn(CoursePracticeViewModel viewModel) => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: _buildLowerColumnChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildLowerColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||||
|
_buildPracticeButton(viewModel),
|
||||||
|
verticalSpaceSmall,
|
||||||
|
_buildSkipButtonWrapper(viewModel)
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildPracticeButton(CoursePracticeViewModel viewModel) =>
|
||||||
|
CustomElevatedButton(
|
||||||
|
height: 55,
|
||||||
|
safe: false,
|
||||||
|
borderRadius: 12,
|
||||||
|
text: 'Practice Again',
|
||||||
|
foregroundColor: kcWhite,
|
||||||
|
// onTap: () => viewModel.goTo(0),
|
||||||
|
backgroundColor: kcPrimaryColor,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSkipButtonWrapper(CoursePracticeViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
|
child: _buildSkipButton(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSkipButton(CoursePracticeViewModel viewModel) =>
|
||||||
|
CustomElevatedButton(
|
||||||
|
height: 55,
|
||||||
|
text: 'Continue',
|
||||||
|
borderRadius: 12,
|
||||||
|
backgroundColor: kcWhite,
|
||||||
|
borderColor: kcPrimaryColor,
|
||||||
|
onTap: viewModel.nextScreen,
|
||||||
|
foregroundColor: kcPrimaryColor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,207 @@
|
||||||
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:stacked/stacked.dart';
|
||||||
|
import 'package:waveform_recorder/waveform_recorder.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/countdown_timer.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/speaking_indicator.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/speaking_label.dart';
|
||||||
|
|
||||||
|
import '../../../common/app_colors.dart';
|
||||||
|
import '../../../common/enmus.dart';
|
||||||
|
import '../../../common/ui_helpers.dart';
|
||||||
|
import '../../../widgets/cancel_practice_sheet.dart';
|
||||||
|
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||||
|
import '../../../widgets/custom_elevated_button.dart';
|
||||||
|
import '../course_practice_viewmodel.dart';
|
||||||
|
|
||||||
|
class DuolingoSpeakingPractice1Answer
|
||||||
|
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||||
|
const DuolingoSpeakingPractice1Answer({
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<void> _cancel(CoursePracticeViewModel viewModel) async {
|
||||||
|
await viewModel.stopRecording();
|
||||||
|
viewModel.pop();
|
||||||
|
viewModel.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _showSheet(
|
||||||
|
{required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) async =>
|
||||||
|
await showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
backgroundColor: kcTransparent,
|
||||||
|
builder: (cxt) => _buildSheet(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||||
|
_buildScaffoldWrapper(context: context, viewModel: viewModel);
|
||||||
|
|
||||||
|
Widget _buildScaffoldWrapper(
|
||||||
|
{required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) =>
|
||||||
|
Scaffold(
|
||||||
|
backgroundColor: kcBackgroundColor,
|
||||||
|
body: _buildScaffold(context: context, viewModel: viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildScaffold(
|
||||||
|
{required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) =>
|
||||||
|
SafeArea(
|
||||||
|
child:
|
||||||
|
_buildBodyColumnWrapper(context: context, viewModel: viewModel));
|
||||||
|
|
||||||
|
Widget _buildBodyColumnWrapper(
|
||||||
|
{required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) =>
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: _buildBodyColumn(context: context, viewModel: viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildBodyColumn(
|
||||||
|
{required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) =>
|
||||||
|
Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children:
|
||||||
|
_buildBodyColumnChildren(context: context, viewModel: viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildBodyColumnChildren(
|
||||||
|
{required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) =>
|
||||||
|
[
|
||||||
|
_buildAppBarWrapper(context: context, viewModel: viewModel),
|
||||||
|
_buildSpeakingIndicatorColumn(viewModel),
|
||||||
|
_buildContinueButtonWrapper(viewModel)
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildAppBarWrapper(
|
||||||
|
{required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) =>
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildAppBar(context: context, viewModel: viewModel),
|
||||||
|
verticalSpaceSmall,
|
||||||
|
_buildCountdownWrapper(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildAppBar(
|
||||||
|
{required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) =>
|
||||||
|
DuolingoPracticeAppBar(
|
||||||
|
title: 'Speaking practice',
|
||||||
|
onClose: () async =>
|
||||||
|
await _showSheet(context: context, viewModel: viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSheet(CoursePracticeViewModel viewModel) => CancelPracticeSheet(
|
||||||
|
onClose: viewModel.pop,
|
||||||
|
onContinue: viewModel.pop,
|
||||||
|
user: viewModel.user?.firstName ?? '',
|
||||||
|
onCancel: () async => await _cancel(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSpeakingIndicatorColumn(CoursePracticeViewModel viewModel) =>
|
||||||
|
Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: _buildSpeakingIndicatorChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildSpeakingIndicatorChildren(
|
||||||
|
CoursePracticeViewModel viewModel) =>
|
||||||
|
[
|
||||||
|
_buildTitle(),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildImageContainer(viewModel),
|
||||||
|
verticalSpaceSmall,
|
||||||
|
_buildSpeakerLabelState(viewModel),
|
||||||
|
verticalSpaceSmall,
|
||||||
|
_buildSpeakingIndicatorState(viewModel)
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildCountdownWrapper() =>
|
||||||
|
Align(alignment: Alignment.centerRight, child:_buildCountdown());
|
||||||
|
|
||||||
|
Widget _buildCountdown()=> const CountdownTimer();
|
||||||
|
|
||||||
|
Widget _buildTitle() => Text(
|
||||||
|
'Speak about the photo below',
|
||||||
|
style: style18DG700,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildImageContainer(CoursePracticeViewModel viewModel) => SizedBox(
|
||||||
|
width: 250,
|
||||||
|
height: 300,
|
||||||
|
child: _buildImageWrapper(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildImageWrapper(CoursePracticeViewModel viewModel) => ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(5),
|
||||||
|
child: _buildImage(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildImage(CoursePracticeViewModel viewModel) => CachedNetworkImage(
|
||||||
|
fit: BoxFit.fill,
|
||||||
|
width: double.maxFinite,
|
||||||
|
imageUrl: viewModel.questions[viewModel.currentQuestion].dynamicPayload
|
||||||
|
?.stimulus?.first.value ??
|
||||||
|
'',
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSpeakerLabelState(CoursePracticeViewModel viewModel) =>
|
||||||
|
VoiceRecordingState.recording == viewModel.recordingState
|
||||||
|
? _buildSpeakerLabel()
|
||||||
|
: const SizedBox(height: 20);
|
||||||
|
|
||||||
|
Widget _buildSpeakerLabel() => const SpeakingLabel();
|
||||||
|
|
||||||
|
Widget _buildSpeakingIndicatorState(CoursePracticeViewModel viewModel) =>
|
||||||
|
VoiceRecordingState.recording == viewModel.recordingState
|
||||||
|
? _buildSpeakingIndicatorWrapper(viewModel)
|
||||||
|
: Container();
|
||||||
|
|
||||||
|
Widget _buildSpeakingIndicatorWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
|
Container(
|
||||||
|
width: 100,
|
||||||
|
padding: const EdgeInsets.only(right: 15),
|
||||||
|
child: _buildSpeakingIndicator(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSpeakingIndicator(CoursePracticeViewModel viewModel) =>
|
||||||
|
WaveformRecorder(
|
||||||
|
height: 35,
|
||||||
|
waveColor: kcPrimaryColor,
|
||||||
|
durationTextStyle: style0Ts,
|
||||||
|
controller: viewModel.waveController,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
|
child: _buildContinueButton(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildContinueButton(CoursePracticeViewModel viewModel) =>
|
||||||
|
CustomElevatedButton(
|
||||||
|
height: 55,
|
||||||
|
borderRadius: 12,
|
||||||
|
foregroundColor: kcWhite,
|
||||||
|
backgroundColor: viewModel.buttonActive
|
||||||
|
? kcPrimaryColor
|
||||||
|
: kcPrimaryColor.withOpacity(0.1),
|
||||||
|
onTap: viewModel.buttonActive ? viewModel.nextScreen : null,
|
||||||
|
text: viewModel.buttonActive
|
||||||
|
? 'Summit'
|
||||||
|
: 'You can submit after ${viewModel.questions[viewModel.currentQuestion].dynamicPayload?.response?.first.value['seconds']}sec',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,238 @@
|
||||||
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_timer_countdown/flutter_timer_countdown.dart';
|
||||||
|
import 'package:stacked/stacked.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/helper_functions.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/page_loading_indicator.dart';
|
||||||
|
|
||||||
|
import '../../../common/app_colors.dart';
|
||||||
|
import '../../../common/enmus.dart';
|
||||||
|
import '../../../common/ui_helpers.dart';
|
||||||
|
import '../../../widgets/cancel_practice_sheet.dart';
|
||||||
|
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||||
|
import '../../../widgets/custom_elevated_button.dart';
|
||||||
|
import '../course_practice_viewmodel.dart';
|
||||||
|
|
||||||
|
class DuolingoSpeakingPractice1Question
|
||||||
|
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||||
|
const DuolingoSpeakingPractice1Question({
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<void> _cancel(CoursePracticeViewModel viewModel) async {
|
||||||
|
await viewModel.stopRecording();
|
||||||
|
viewModel.pop();
|
||||||
|
viewModel.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _showSheet(
|
||||||
|
{required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) async =>
|
||||||
|
await showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
backgroundColor: kcTransparent,
|
||||||
|
builder: (cxt) => _buildSheet(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Future<void> _next(CoursePracticeViewModel viewModel)async{
|
||||||
|
await viewModel.startRecording();
|
||||||
|
viewModel.nextScreen();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||||
|
_buildScaffoldWrapper(context: context, viewModel: viewModel);
|
||||||
|
|
||||||
|
Widget _buildScaffoldWrapper(
|
||||||
|
{required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) =>
|
||||||
|
Scaffold(
|
||||||
|
backgroundColor: kcBackgroundColor,
|
||||||
|
body: _buildScaffoldStack(context: context, viewModel: viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildScaffoldStack( {required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel})=> Stack(
|
||||||
|
children: [
|
||||||
|
_buildScaffold(context: context,viewModel: viewModel),
|
||||||
|
_buildStartRecState(viewModel)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildScaffold(
|
||||||
|
{required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) =>
|
||||||
|
SafeArea(
|
||||||
|
child:
|
||||||
|
_buildBodyColumnWrapper(context: context, viewModel: viewModel));
|
||||||
|
|
||||||
|
Widget _buildBodyColumnWrapper(
|
||||||
|
{required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) =>
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: _buildBodyColumn(context: context, viewModel: viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildBodyColumn(
|
||||||
|
{required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) =>
|
||||||
|
Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children:
|
||||||
|
_buildBodyColumnChildren(context: context, viewModel: viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildBodyColumnChildren(
|
||||||
|
{required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) =>
|
||||||
|
[
|
||||||
|
_buildAppBarWrapper(context: context, viewModel: viewModel),
|
||||||
|
_buildSpeakingIndicatorWrapper(viewModel),
|
||||||
|
_buildContinueButtonWrapper(viewModel)
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildAppBarWrapper(
|
||||||
|
{required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) =>
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildAppBar(context: context, viewModel: viewModel),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildAppBar(
|
||||||
|
{required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) =>
|
||||||
|
DuolingoPracticeAppBar(
|
||||||
|
title: 'Speaking practice',
|
||||||
|
onClose: () async =>
|
||||||
|
await _showSheet(context: context, viewModel: viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSheet(CoursePracticeViewModel viewModel) => CancelPracticeSheet(
|
||||||
|
onClose: viewModel.pop,
|
||||||
|
onContinue: viewModel.pop,
|
||||||
|
user: viewModel.user?.firstName ?? '',
|
||||||
|
onCancel: () async => await _cancel(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSpeakingIndicatorWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
|
Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: _buildSpeakingIndicatorChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildSpeakingIndicatorChildren(
|
||||||
|
CoursePracticeViewModel viewModel) =>
|
||||||
|
[
|
||||||
|
_buildTitle(),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildTimerStackWrapper(viewModel),
|
||||||
|
verticalSpaceSmall,
|
||||||
|
_buildSubtitle(),
|
||||||
|
verticalSpaceSmall,
|
||||||
|
_buildImageContainer(viewModel),
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildTitle() => Text(
|
||||||
|
'Prepare to speak about the photo',
|
||||||
|
style: style18DG700,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildTimerStackWrapper(CoursePracticeViewModel viewModel) => SizedBox(
|
||||||
|
height: 50,
|
||||||
|
child: _buildTimerStack(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildTimerStack(CoursePracticeViewModel viewModel) => Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: _buildTimerStackChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildTimerStackChildren(CoursePracticeViewModel viewModel) =>
|
||||||
|
[_buildTimer(), _buildCountdownTime(viewModel)];
|
||||||
|
|
||||||
|
Widget _buildTimer() => const CircularProgressIndicator(
|
||||||
|
value: 1.0,
|
||||||
|
strokeWidth: 5,
|
||||||
|
color: kcPrimaryColor,
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
constraints: BoxConstraints(minWidth: 50, minHeight: 50),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildCountdownTime(CoursePracticeViewModel viewModel) =>
|
||||||
|
TimerCountdown(
|
||||||
|
spacerWidth: 0,
|
||||||
|
enableDescriptions: false,
|
||||||
|
timeTextStyle: style14P600,
|
||||||
|
onEnd: viewModel.nextScreen,
|
||||||
|
colonsTextStyle: style14P400,
|
||||||
|
format: CountDownTimerFormat.minutesSeconds,
|
||||||
|
endTime: DateTime.now().add(
|
||||||
|
Duration(
|
||||||
|
minutes: getMinutes(
|
||||||
|
viewModel.questions[viewModel.currentQuestion].dynamicPayload
|
||||||
|
?.stimulus?.last.value['seconds'],
|
||||||
|
),
|
||||||
|
seconds: getSeconds(
|
||||||
|
viewModel.questions[viewModel.currentQuestion].dynamicPayload
|
||||||
|
?.stimulus?.last.value['seconds'],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSubtitle() => Text(
|
||||||
|
'Prep time',
|
||||||
|
style: style16DG400,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildImageContainer(CoursePracticeViewModel viewModel) => SizedBox(
|
||||||
|
width: 250,
|
||||||
|
height: 300,
|
||||||
|
child: _buildImageWrapper(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildImageWrapper(CoursePracticeViewModel viewModel) => ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(5),
|
||||||
|
child: _buildImage(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildImage(CoursePracticeViewModel viewModel) => CachedNetworkImage(
|
||||||
|
fit: BoxFit.fill,
|
||||||
|
width: double.maxFinite,
|
||||||
|
imageUrl: viewModel.questions[viewModel.currentQuestion].dynamicPayload
|
||||||
|
?.stimulus?.first.value ??
|
||||||
|
'',
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
|
child: _buildContinueButton(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildContinueButton(CoursePracticeViewModel viewModel) =>
|
||||||
|
CustomElevatedButton(
|
||||||
|
height: 55,
|
||||||
|
borderRadius: 12,
|
||||||
|
text: 'Start Speaking',
|
||||||
|
foregroundColor: kcWhite,
|
||||||
|
onTap: () => _next(viewModel),
|
||||||
|
backgroundColor: kcPrimaryColor,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildStartRecState(CoursePracticeViewModel viewModel) =>
|
||||||
|
viewModel.busy(StateObjects.recordCoursePracticeAnswer)
|
||||||
|
? const PageLoadingIndicator()
|
||||||
|
: Container();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,165 @@
|
||||||
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:stacked/stacked.dart';
|
||||||
|
import 'package:yimaru_app/ui/common/helper_functions.dart';
|
||||||
|
import 'package:yimaru_app/ui/widgets/countdown_timer.dart';
|
||||||
|
|
||||||
|
import '../../../common/app_colors.dart';
|
||||||
|
import '../../../common/ui_helpers.dart';
|
||||||
|
import '../../../widgets/cancel_practice_sheet.dart';
|
||||||
|
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||||
|
import '../../../widgets/speaking_practice_review_section.dart';
|
||||||
|
import '../../duolingo/duolingo_viewmodel.dart';
|
||||||
|
import '../course_practice_viewmodel.dart';
|
||||||
|
|
||||||
|
class DuolingoSpeakingPractice1Review
|
||||||
|
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||||
|
const DuolingoSpeakingPractice1Review({
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<void> _cancel(CoursePracticeViewModel viewModel) async {
|
||||||
|
await viewModel.stopRecording();
|
||||||
|
viewModel.pop();
|
||||||
|
viewModel.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _showSheet(
|
||||||
|
{required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) async =>
|
||||||
|
await showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
backgroundColor: kcTransparent,
|
||||||
|
builder: (cxt) => _buildSheet(context: context, viewModel: viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||||
|
_buildScaffoldWrapper(context: context, viewModel: viewModel);
|
||||||
|
|
||||||
|
Widget _buildScaffoldWrapper(
|
||||||
|
{required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) =>
|
||||||
|
Scaffold(
|
||||||
|
backgroundColor: kcBackgroundColor,
|
||||||
|
body: _buildScaffold(context: context, viewModel: viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildScaffold(
|
||||||
|
{required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) =>
|
||||||
|
SafeArea(child: _buildBodyColumn(context: context, viewModel: viewModel));
|
||||||
|
|
||||||
|
Widget _buildBodyColumn(
|
||||||
|
{required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) =>
|
||||||
|
Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children:
|
||||||
|
_buildBodyColumnChildren(context: context, viewModel: viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildBodyColumnChildren(
|
||||||
|
{required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) =>
|
||||||
|
[
|
||||||
|
_buildAppBarIndenter(context: context, viewModel: viewModel),
|
||||||
|
_buildImageSectionIndenter(viewModel),
|
||||||
|
_buildPracticeReviewSection(viewModel)
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildAppBarIndenter(
|
||||||
|
{required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) =>
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: _buildAppBarWrapper(context: context, viewModel: viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildAppBarWrapper(
|
||||||
|
{required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) =>
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildAppBar(context: context, viewModel: viewModel),
|
||||||
|
verticalSpaceSmall,
|
||||||
|
_buildCountdownWrapper(viewModel),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildAppBar(
|
||||||
|
{required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) =>
|
||||||
|
DuolingoPracticeAppBar(
|
||||||
|
title: 'Feedback',
|
||||||
|
onClose: () async =>
|
||||||
|
await _showSheet(context: context, viewModel: viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildSheet(
|
||||||
|
{required BuildContext context,
|
||||||
|
required CoursePracticeViewModel viewModel}) =>
|
||||||
|
CancelPracticeSheet(
|
||||||
|
onClose: viewModel.pop,
|
||||||
|
onContinue: viewModel.pop,
|
||||||
|
user: viewModel.user?.firstName ?? '',
|
||||||
|
onCancel: () async => await _cancel(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildImageSectionIndenter(CoursePracticeViewModel viewModel) =>
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: _buildImageSectionWrapper(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildImageSectionWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: _buildImageSectionChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildImageSectionChildren(CoursePracticeViewModel viewModel) =>
|
||||||
|
[
|
||||||
|
_buildTitle(),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildImageContainer(viewModel),
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildCountdownWrapper(CoursePracticeViewModel viewModel) => Align(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: _buildCountdown(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildCountdown(CoursePracticeViewModel viewModel) =>
|
||||||
|
const CountdownTimer(time: '0:0');
|
||||||
|
|
||||||
|
Widget _buildTitle() => Text(
|
||||||
|
'Speak about the photo below',
|
||||||
|
style: style18DG700,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildImageContainer(CoursePracticeViewModel viewModel) => SizedBox(
|
||||||
|
width: 150,
|
||||||
|
height: 200,
|
||||||
|
child: _buildImageWrapper(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildImageWrapper(CoursePracticeViewModel viewModel) => ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(5),
|
||||||
|
child: _buildImage(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildImage(CoursePracticeViewModel viewModel) => CachedNetworkImage(
|
||||||
|
fit: BoxFit.fill,
|
||||||
|
width: double.maxFinite,
|
||||||
|
imageUrl: viewModel.questions[viewModel.currentQuestion].dynamicPayload
|
||||||
|
?.stimulus?.first.value ??
|
||||||
|
'',
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildPracticeReviewSection(CoursePracticeViewModel viewModel) =>
|
||||||
|
SpeakingPracticeReviewSection(onTap: viewModel.nextScreen,);
|
||||||
|
}
|
||||||
|
|
@ -1,49 +1,49 @@
|
||||||
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/widgets/duolingo_assessment_question_card.dart';
|
import 'package:yimaru_app/ui/widgets/duolingo_practice_question_card.dart';
|
||||||
|
|
||||||
import '../../../common/app_colors.dart';
|
import '../../../common/app_colors.dart';
|
||||||
import '../../../common/ui_helpers.dart';
|
import '../../../common/ui_helpers.dart';
|
||||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||||
import '../../../widgets/countdown_timer.dart';
|
import '../../../widgets/countdown_timer.dart';
|
||||||
import '../../../widgets/custom_elevated_button.dart';
|
import '../../../widgets/custom_elevated_button.dart';
|
||||||
import '../../../widgets/speaking_indicator.dart';
|
import '../../../widgets/speaking_indicator.dart';
|
||||||
import '../../../widgets/speaking_label.dart';
|
import '../../../widgets/speaking_label.dart';
|
||||||
import '../duolingo_viewmodel.dart';
|
import '../course_practice_viewmodel.dart';
|
||||||
|
|
||||||
class DuolingoSpeakingAssessment2Answer
|
class DuolingoSpeakingPractice2Answer
|
||||||
extends ViewModelWidget<DuolingoViewModel> {
|
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||||
const DuolingoSpeakingAssessment2Answer({super.key});
|
const DuolingoSpeakingPractice2Answer({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||||
_buildScaffoldWrapper(viewModel);
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||||
backgroundColor: kcBackgroundColor,
|
backgroundColor: kcBackgroundColor,
|
||||||
body: _buildScaffold(viewModel),
|
body: _buildScaffold(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||||
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
||||||
|
|
||||||
Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding(
|
Widget _buildBodyColumnWrapper(CoursePracticeViewModel viewModel) => Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildBodyColumn(viewModel),
|
child: _buildBodyColumn(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: _buildBodyColumnChildren(viewModel),
|
children: _buildBodyColumnChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||||
_buildAppBarWrapper(viewModel),
|
_buildAppBarWrapper(viewModel),
|
||||||
_buildSpeakingIndicatorWrapper(viewModel),
|
_buildSpeakingIndicatorWrapper(viewModel),
|
||||||
_buildContinueButtonWrapper(viewModel)
|
_buildContinueButtonWrapper(viewModel)
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||||
children: [
|
children: [
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildAppBar(viewModel),
|
_buildAppBar(viewModel),
|
||||||
|
|
@ -52,12 +52,14 @@ class DuolingoSpeakingAssessment2Answer
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||||
title: 'Speaking Assessment',
|
DuolingoPracticeAppBar(
|
||||||
|
title: 'Speaking practice',
|
||||||
onClose: () => viewModel.goTo(0),
|
onClose: () => viewModel.goTo(0),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildSpeakingIndicatorWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildSpeakingIndicatorWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
|
Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: _buildSpeakingIndicatorChildren(),
|
children: _buildSpeakingIndicatorChildren(),
|
||||||
|
|
@ -89,7 +91,7 @@ class DuolingoSpeakingAssessment2Answer
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildQuestion() => const DuolingoAssessmentQuestionCard(
|
Widget _buildQuestion() => const DuolingoPracticeQuestionCard(
|
||||||
title: 'How has growing up in your hometown influenced you?',
|
title: 'How has growing up in your hometown influenced you?',
|
||||||
subtitle:
|
subtitle:
|
||||||
'What values of beliefs did you learn from living in your hometown? How will your hometown influence your future?',
|
'What values of beliefs did you learn from living in your hometown? How will your hometown influence your future?',
|
||||||
|
|
@ -101,12 +103,13 @@ class DuolingoSpeakingAssessment2Answer
|
||||||
size: 25,
|
size: 25,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding(
|
Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 50),
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
child: _buildContinueButton(viewModel),
|
child: _buildContinueButton(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildContinueButton(DuolingoViewModel viewModel) =>
|
Widget _buildContinueButton(CoursePracticeViewModel viewModel) =>
|
||||||
CustomElevatedButton(
|
CustomElevatedButton(
|
||||||
height: 55,
|
height: 55,
|
||||||
text: 'Submit',
|
text: 'Submit',
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
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/widgets/duolingo_assessment_question_card.dart';
|
import 'package:yimaru_app/ui/widgets/duolingo_practice_question_card.dart';
|
||||||
|
|
||||||
import '../../../common/app_colors.dart';
|
import '../../../common/app_colors.dart';
|
||||||
import '../../../common/ui_helpers.dart';
|
import '../../../common/ui_helpers.dart';
|
||||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||||
import '../../../widgets/countdown_timer.dart';
|
import '../../../widgets/countdown_timer.dart';
|
||||||
import '../../../widgets/custom_elevated_button.dart';
|
import '../../../widgets/custom_elevated_button.dart';
|
||||||
import '../duolingo_viewmodel.dart';
|
import '../../duolingo/duolingo_viewmodel.dart';
|
||||||
|
|
||||||
class DuolingoSpeakingAssessment2Question
|
class DuolingoSpeakingPractice2Question
|
||||||
extends ViewModelWidget<DuolingoViewModel> {
|
extends ViewModelWidget<DuolingoViewModel> {
|
||||||
const DuolingoSpeakingAssessment2Question({
|
const DuolingoSpeakingPractice2Question({
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -52,8 +52,8 @@ class DuolingoSpeakingAssessment2Question
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoPracticeAppBar(
|
||||||
title: 'Speaking Assessment',
|
title: 'Speaking practice',
|
||||||
onClose: () => viewModel.goTo(0),
|
onClose: () => viewModel.goTo(0),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -85,7 +85,7 @@ class DuolingoSpeakingAssessment2Question
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildQuestion() => const DuolingoAssessmentQuestionCard(
|
Widget _buildQuestion() => const DuolingoPracticeQuestionCard(
|
||||||
title: 'How has growing up in your hometown influenced you?',
|
title: 'How has growing up in your hometown influenced you?',
|
||||||
subtitle:
|
subtitle:
|
||||||
'What values of beliefs did you learn from living in your hometown? How will your hometown influence your future?',
|
'What values of beliefs did you learn from living in your hometown? How will your hometown influence your future?',
|
||||||
|
|
@ -4,44 +4,44 @@ import 'package:yimaru_app/ui/widgets/countdown_timer.dart';
|
||||||
|
|
||||||
import '../../../common/app_colors.dart';
|
import '../../../common/app_colors.dart';
|
||||||
import '../../../common/ui_helpers.dart';
|
import '../../../common/ui_helpers.dart';
|
||||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||||
import '../../../widgets/speaking_assessment_review_section.dart';
|
import '../../../widgets/speaking_practice_review_section.dart';
|
||||||
import '../../../widgets/duolingo_assessment_question_card.dart';
|
import '../../../widgets/duolingo_practice_question_card.dart';
|
||||||
import '../duolingo_viewmodel.dart';
|
import '../course_practice_viewmodel.dart';
|
||||||
|
|
||||||
class DuolingoSpeakingAssessment2Review
|
class DuolingoSpeakingPractice2Review
|
||||||
extends ViewModelWidget<DuolingoViewModel> {
|
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||||
const DuolingoSpeakingAssessment2Review({super.key});
|
const DuolingoSpeakingPractice2Review({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||||
_buildScaffoldWrapper(viewModel);
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||||
backgroundColor: kcBackgroundColor,
|
backgroundColor: kcBackgroundColor,
|
||||||
body: _buildScaffold(viewModel),
|
body: _buildScaffold(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||||
SafeArea(child: _buildBodyColumn(viewModel));
|
SafeArea(child: _buildBodyColumn(viewModel));
|
||||||
|
|
||||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: _buildBodyColumnChildren(viewModel),
|
children: _buildBodyColumnChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||||
_buildAppBarIndenter(viewModel),
|
_buildAppBarIndenter(viewModel),
|
||||||
_buildImageSectionIndenter(viewModel),
|
_buildImageSectionIndenter(viewModel),
|
||||||
_buildAssessmentReviewSection(viewModel)
|
_buildPracticeReviewSection(viewModel)
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildAppBarIndenter(DuolingoViewModel viewModel) => Padding(
|
Widget _buildAppBarIndenter(CoursePracticeViewModel viewModel) => Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildAppBarWrapper(viewModel),
|
child: _buildAppBarWrapper(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||||
children: [
|
children: [
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildAppBar(viewModel),
|
_buildAppBar(viewModel),
|
||||||
|
|
@ -50,17 +50,19 @@ class DuolingoSpeakingAssessment2Review
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||||
|
DuolingoPracticeAppBar(
|
||||||
title: 'Feedback',
|
title: 'Feedback',
|
||||||
onClose: () => viewModel.goTo(0),
|
onClose: () => viewModel.goTo(0),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildImageSectionIndenter(DuolingoViewModel viewModel) => Padding(
|
Widget _buildImageSectionIndenter(CoursePracticeViewModel viewModel) =>
|
||||||
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildImageSectionWrapper(viewModel),
|
child: _buildImageSectionWrapper(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildImageSectionWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildImageSectionWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: _buildImageSectionChildren(),
|
children: _buildImageSectionChildren(),
|
||||||
|
|
@ -88,12 +90,12 @@ class DuolingoSpeakingAssessment2Review
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildQuestion() => const DuolingoAssessmentQuestionCard(
|
Widget _buildQuestion() => const DuolingoPracticeQuestionCard(
|
||||||
title: 'How has growing up in your hometown influenced you?',
|
title: 'How has growing up in your hometown influenced you?',
|
||||||
subtitle:
|
subtitle:
|
||||||
'What values of beliefs did you learn from living in your hometown? How will your hometown influence your future?',
|
'What values of beliefs did you learn from living in your hometown? How will your hometown influence your future?',
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAssessmentReviewSection(DuolingoViewModel viewModel) =>
|
Widget _buildPracticeReviewSection(CoursePracticeViewModel viewModel) =>
|
||||||
SpeakingAssessmentReviewSection(onTap: () => viewModel.goTo(5));
|
SpeakingPracticeReviewSection(onTap: () => viewModel.goTo(5));
|
||||||
}
|
}
|
||||||
|
|
@ -1,19 +1,19 @@
|
||||||
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/widgets/duolingo_assessment_question_card.dart';
|
import 'package:yimaru_app/ui/widgets/duolingo_practice_question_card.dart';
|
||||||
|
|
||||||
import '../../../common/app_colors.dart';
|
import '../../../common/app_colors.dart';
|
||||||
import '../../../common/ui_helpers.dart';
|
import '../../../common/ui_helpers.dart';
|
||||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||||
import '../../../widgets/countdown_timer.dart';
|
import '../../../widgets/countdown_timer.dart';
|
||||||
import '../../../widgets/custom_elevated_button.dart';
|
import '../../../widgets/custom_elevated_button.dart';
|
||||||
import '../../../widgets/speaking_indicator.dart';
|
import '../../../widgets/speaking_indicator.dart';
|
||||||
import '../../../widgets/speaking_label.dart';
|
import '../../../widgets/speaking_label.dart';
|
||||||
import '../duolingo_viewmodel.dart';
|
import '../../duolingo/duolingo_viewmodel.dart';
|
||||||
|
|
||||||
class DuolingoSpeakingAssessment3Answer
|
class DuolingoSpeakingPractice3Answer
|
||||||
extends ViewModelWidget<DuolingoViewModel> {
|
extends ViewModelWidget<DuolingoViewModel> {
|
||||||
const DuolingoSpeakingAssessment3Answer({super.key});
|
const DuolingoSpeakingPractice3Answer({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
||||||
|
|
@ -52,8 +52,8 @@ class DuolingoSpeakingAssessment3Answer
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoPracticeAppBar(
|
||||||
title: 'Speaking Assessment',
|
title: 'Speaking practice',
|
||||||
onClose: () => viewModel.goTo(0),
|
onClose: () => viewModel.goTo(0),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -74,7 +74,7 @@ class DuolingoSpeakingAssessment3Answer
|
||||||
Widget _buildCountdownWrapper() =>
|
Widget _buildCountdownWrapper() =>
|
||||||
const Align(alignment: Alignment.centerRight, child: CountdownTimer());
|
const Align(alignment: Alignment.centerRight, child: CountdownTimer());
|
||||||
|
|
||||||
Widget _buildQuestion() => const DuolingoAssessmentQuestionCard(
|
Widget _buildQuestion() => const DuolingoPracticeQuestionCard(
|
||||||
subtitle:
|
subtitle:
|
||||||
'What values of beliefs did you learn from living in your hometown? How will your hometown influence your future?',
|
'What values of beliefs did you learn from living in your hometown? How will your hometown influence your future?',
|
||||||
);
|
);
|
||||||
|
|
@ -1,49 +1,49 @@
|
||||||
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/widgets/duolingo_assessment_question_card.dart';
|
import 'package:yimaru_app/ui/widgets/duolingo_practice_question_card.dart';
|
||||||
|
|
||||||
import '../../../common/app_colors.dart';
|
import '../../../common/app_colors.dart';
|
||||||
import '../../../common/ui_helpers.dart';
|
import '../../../common/ui_helpers.dart';
|
||||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||||
import '../../../widgets/countdown_timer.dart';
|
import '../../../widgets/countdown_timer.dart';
|
||||||
import '../../../widgets/custom_elevated_button.dart';
|
import '../../../widgets/custom_elevated_button.dart';
|
||||||
import '../duolingo_viewmodel.dart';
|
import '../course_practice_viewmodel.dart';
|
||||||
|
|
||||||
class DuolingoSpeakingAssessment3Question
|
class DuolingoSpeakingPractice3Question
|
||||||
extends ViewModelWidget<DuolingoViewModel> {
|
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||||
const DuolingoSpeakingAssessment3Question({
|
const DuolingoSpeakingPractice3Question({
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||||
_buildScaffoldWrapper(viewModel);
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||||
backgroundColor: kcBackgroundColor,
|
backgroundColor: kcBackgroundColor,
|
||||||
body: _buildScaffold(viewModel),
|
body: _buildScaffold(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||||
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
||||||
|
|
||||||
Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding(
|
Widget _buildBodyColumnWrapper(CoursePracticeViewModel viewModel) => Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildBodyColumn(viewModel),
|
child: _buildBodyColumn(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: _buildBodyColumnChildren(viewModel),
|
children: _buildBodyColumnChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||||
_buildAppBarWrapper(viewModel),
|
_buildAppBarWrapper(viewModel),
|
||||||
_buildSpeakingIndicatorWrapper(viewModel),
|
_buildSpeakingIndicatorWrapper(viewModel),
|
||||||
_buildContinueButtonWrapper(viewModel)
|
_buildContinueButtonWrapper(viewModel)
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||||
children: [
|
children: [
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildAppBar(viewModel),
|
_buildAppBar(viewModel),
|
||||||
|
|
@ -52,12 +52,14 @@ class DuolingoSpeakingAssessment3Question
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||||
title: 'Speaking Assessment',
|
DuolingoPracticeAppBar(
|
||||||
|
title: 'Speaking practice',
|
||||||
onClose: () => viewModel.goTo(0),
|
onClose: () => viewModel.goTo(0),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildSpeakingIndicatorWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildSpeakingIndicatorWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
|
Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: _buildSpeakingIndicatorChildren(),
|
children: _buildSpeakingIndicatorChildren(),
|
||||||
|
|
@ -85,17 +87,18 @@ class DuolingoSpeakingAssessment3Question
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildQuestion() => const DuolingoAssessmentQuestionCard(
|
Widget _buildQuestion() => const DuolingoPracticeQuestionCard(
|
||||||
subtitle:
|
subtitle:
|
||||||
'What values of beliefs did you learn from living in your hometown? How will your hometown influence your future?',
|
'What values of beliefs did you learn from living in your hometown? How will your hometown influence your future?',
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding(
|
Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 50),
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
child: _buildContinueButton(viewModel),
|
child: _buildContinueButton(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildContinueButton(DuolingoViewModel viewModel) =>
|
Widget _buildContinueButton(CoursePracticeViewModel viewModel) =>
|
||||||
CustomElevatedButton(
|
CustomElevatedButton(
|
||||||
height: 55,
|
height: 55,
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:stacked/stacked.dart';
|
||||||
|
|
||||||
|
import '../../../common/app_colors.dart';
|
||||||
|
import '../../../common/ui_helpers.dart';
|
||||||
|
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||||
|
import '../../../widgets/speaking_practice_review_section.dart';
|
||||||
|
import '../../../widgets/duolingo_practice_question_card.dart';
|
||||||
|
import '../course_practice_viewmodel.dart';
|
||||||
|
|
||||||
|
class DuolingoSpeakingPractice3Review
|
||||||
|
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||||
|
const DuolingoSpeakingPractice3Review({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||||
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
|
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||||
|
backgroundColor: kcBackgroundColor,
|
||||||
|
body: _buildScaffold(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||||
|
SafeArea(child: _buildBodyColumn(viewModel));
|
||||||
|
|
||||||
|
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: _buildBodyColumnChildren(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||||
|
_buildAppBarIndenter(viewModel),
|
||||||
|
_buildQuestionIndenter(viewModel),
|
||||||
|
_buildPracticeReviewSection(viewModel)
|
||||||
|
];
|
||||||
|
|
||||||
|
Widget _buildAppBarIndenter(CoursePracticeViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: _buildAppBarWrapper(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||||
|
children: [
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildAppBar(viewModel),
|
||||||
|
verticalSpaceSmall,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||||
|
DuolingoPracticeAppBar(
|
||||||
|
title: 'Feedback',
|
||||||
|
onClose: () => viewModel.goTo(0),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildQuestionIndenter(CoursePracticeViewModel viewModel) => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
|
child: _buildQuestion(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildQuestion() => const DuolingoPracticeQuestionCard(
|
||||||
|
subtitle:
|
||||||
|
'What values of beliefs did you learn from living in your hometown? How will your hometown influence your future?',
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildPracticeReviewSection(CoursePracticeViewModel viewModel) =>
|
||||||
|
SpeakingPracticeReviewSection(onTap: () => viewModel.goTo(5));
|
||||||
|
}
|
||||||
|
|
@ -4,46 +4,46 @@ import 'package:yimaru_app/ui/widgets/wave_wrapper.dart';
|
||||||
|
|
||||||
import '../../../common/app_colors.dart';
|
import '../../../common/app_colors.dart';
|
||||||
import '../../../common/ui_helpers.dart';
|
import '../../../common/ui_helpers.dart';
|
||||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||||
import '../../../widgets/countdown_timer.dart';
|
import '../../../widgets/countdown_timer.dart';
|
||||||
import '../../../widgets/custom_elevated_button.dart';
|
import '../../../widgets/custom_elevated_button.dart';
|
||||||
import '../../../widgets/speaking_indicator.dart';
|
import '../../../widgets/speaking_indicator.dart';
|
||||||
import '../../../widgets/speaking_label.dart';
|
import '../../../widgets/speaking_label.dart';
|
||||||
import '../duolingo_viewmodel.dart';
|
import '../course_practice_viewmodel.dart';
|
||||||
|
|
||||||
class DuolingoSpeakingAssessment4Answer
|
class DuolingoSpeakingPractice4Answer
|
||||||
extends ViewModelWidget<DuolingoViewModel> {
|
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||||
const DuolingoSpeakingAssessment4Answer({super.key});
|
const DuolingoSpeakingPractice4Answer({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||||
_buildScaffoldWrapper(viewModel);
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||||
backgroundColor: kcBackgroundColor,
|
backgroundColor: kcBackgroundColor,
|
||||||
body: _buildScaffold(viewModel),
|
body: _buildScaffold(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||||
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
||||||
|
|
||||||
Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding(
|
Widget _buildBodyColumnWrapper(CoursePracticeViewModel viewModel) => Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildBodyColumn(viewModel),
|
child: _buildBodyColumn(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: _buildBodyColumnChildren(viewModel),
|
children: _buildBodyColumnChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||||
_buildAppBarWrapper(viewModel),
|
_buildAppBarWrapper(viewModel),
|
||||||
_buildAnswerSectionWrapper(viewModel),
|
_buildAnswerSectionWrapper(viewModel),
|
||||||
_buildContinueButtonWrapper(viewModel)
|
_buildContinueButtonWrapper(viewModel)
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||||
children: [
|
children: [
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildAppBar(viewModel),
|
_buildAppBar(viewModel),
|
||||||
|
|
@ -52,15 +52,17 @@ class DuolingoSpeakingAssessment4Answer
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||||
title: 'Speaking Assessment',
|
DuolingoPracticeAppBar(
|
||||||
|
title: 'Speaking practice',
|
||||||
onClose: () => viewModel.goTo(0),
|
onClose: () => viewModel.goTo(0),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildCountdownWrapper() =>
|
Widget _buildCountdownWrapper() =>
|
||||||
const Align(alignment: Alignment.centerRight, child: CountdownTimer());
|
const Align(alignment: Alignment.centerRight, child: CountdownTimer());
|
||||||
|
|
||||||
Widget _buildAnswerSectionWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildAnswerSectionWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
|
Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: _buildAnswerSectionChildren(),
|
children: _buildAnswerSectionChildren(),
|
||||||
|
|
@ -96,12 +98,13 @@ class DuolingoSpeakingAssessment4Answer
|
||||||
color: kcWhite,
|
color: kcWhite,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding(
|
Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 50),
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
child: _buildContinueButton(viewModel),
|
child: _buildContinueButton(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildContinueButton(DuolingoViewModel viewModel) =>
|
Widget _buildContinueButton(CoursePracticeViewModel viewModel) =>
|
||||||
CustomElevatedButton(
|
CustomElevatedButton(
|
||||||
height: 55,
|
height: 55,
|
||||||
text: 'Submit',
|
text: 'Submit',
|
||||||
|
|
@ -4,17 +4,17 @@ import 'package:yimaru_app/ui/widgets/dwarf_tile.dart';
|
||||||
|
|
||||||
import '../../../common/app_colors.dart';
|
import '../../../common/app_colors.dart';
|
||||||
import '../../../common/ui_helpers.dart';
|
import '../../../common/ui_helpers.dart';
|
||||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||||
import '../../../widgets/countdown_timer.dart';
|
import '../../../widgets/countdown_timer.dart';
|
||||||
import '../../../widgets/custom_elevated_button.dart';
|
import '../../../widgets/custom_elevated_button.dart';
|
||||||
import '../../../widgets/speaking_indicator.dart';
|
import '../../../widgets/speaking_indicator.dart';
|
||||||
import '../duolingo_viewmodel.dart';
|
import '../course_practice_viewmodel.dart';
|
||||||
|
|
||||||
class DuolingoSpeakingAssessment4Question
|
class DuolingoSpeakingPractice4Question
|
||||||
extends ViewModelWidget<DuolingoViewModel> {
|
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||||
const DuolingoSpeakingAssessment4Question({super.key});
|
const DuolingoSpeakingPractice4Question({super.key});
|
||||||
|
|
||||||
void _goTo(DuolingoViewModel viewModel) {
|
void _goTo(CoursePracticeViewModel viewModel) {
|
||||||
viewModel.setSpeakingState();
|
viewModel.setSpeakingState();
|
||||||
if (!viewModel.isSpeaking) {
|
if (!viewModel.isSpeaking) {
|
||||||
viewModel.goTo(3);
|
viewModel.goTo(3);
|
||||||
|
|
@ -22,34 +22,34 @@ class DuolingoSpeakingAssessment4Question
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||||
_buildScaffoldWrapper(viewModel);
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||||
backgroundColor: kcBackgroundColor,
|
backgroundColor: kcBackgroundColor,
|
||||||
body: _buildScaffold(viewModel),
|
body: _buildScaffold(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||||
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
||||||
|
|
||||||
Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding(
|
Widget _buildBodyColumnWrapper(CoursePracticeViewModel viewModel) => Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildBodyColumn(viewModel),
|
child: _buildBodyColumn(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: _buildBodyColumnChildren(viewModel),
|
children: _buildBodyColumnChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||||
_buildAppBarWrapper(viewModel),
|
_buildAppBarWrapper(viewModel),
|
||||||
_buildSpeakingIndicatorState(viewModel),
|
_buildSpeakingIndicatorState(viewModel),
|
||||||
_buildContinueButtonWrapper(viewModel)
|
_buildContinueButtonWrapper(viewModel)
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||||
children: [
|
children: [
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildAppBar(viewModel),
|
_buildAppBar(viewModel),
|
||||||
|
|
@ -57,17 +57,20 @@ class DuolingoSpeakingAssessment4Question
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||||
title: 'Speaking Assessment',
|
DuolingoPracticeAppBar(
|
||||||
|
title: 'Speaking practice',
|
||||||
onClose: () => viewModel.goTo(0),
|
onClose: () => viewModel.goTo(0),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildSpeakingIndicatorState(DuolingoViewModel viewModel) =>
|
Widget _buildSpeakingIndicatorState(CoursePracticeViewModel viewModel) =>
|
||||||
viewModel.isSpeaking
|
// viewModel.isSpeaking
|
||||||
? _buildSpeakingState()
|
// ? _buildSpeakingState()
|
||||||
: _buildQuestionSectionWrapper(viewModel);
|
// :
|
||||||
|
_buildQuestionSectionWrapper(viewModel);
|
||||||
|
|
||||||
Widget _buildQuestionSectionWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildQuestionSectionWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
|
Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: _buildQuestionSectionChildren(),
|
children: _buildQuestionSectionChildren(),
|
||||||
|
|
@ -105,12 +108,13 @@ class DuolingoSpeakingAssessment4Question
|
||||||
size: 75,
|
size: 75,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding(
|
Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 50),
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
child: _buildContinueButton(viewModel),
|
child: _buildContinueButton(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildContinueButton(DuolingoViewModel viewModel) =>
|
Widget _buildContinueButton(CoursePracticeViewModel viewModel) =>
|
||||||
CustomElevatedButton(
|
CustomElevatedButton(
|
||||||
height: 55,
|
height: 55,
|
||||||
text: 'Start',
|
text: 'Start',
|
||||||
|
|
@ -3,44 +3,44 @@ import 'package:stacked/stacked.dart';
|
||||||
|
|
||||||
import '../../../common/app_colors.dart';
|
import '../../../common/app_colors.dart';
|
||||||
import '../../../common/ui_helpers.dart';
|
import '../../../common/ui_helpers.dart';
|
||||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||||
import '../../../widgets/speaking_assessment_review_section.dart';
|
import '../../../widgets/speaking_practice_review_section.dart';
|
||||||
import '../../../widgets/dwarf_tile.dart';
|
import '../../../widgets/dwarf_tile.dart';
|
||||||
import '../duolingo_viewmodel.dart';
|
import '../course_practice_viewmodel.dart';
|
||||||
|
|
||||||
class DuolingoSpeakingAssessment4Review
|
class DuolingoSpeakingPractice4Review
|
||||||
extends ViewModelWidget<DuolingoViewModel> {
|
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||||
const DuolingoSpeakingAssessment4Review({super.key});
|
const DuolingoSpeakingPractice4Review({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||||
_buildScaffoldWrapper(viewModel);
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||||
backgroundColor: kcBackgroundColor,
|
backgroundColor: kcBackgroundColor,
|
||||||
body: _buildScaffold(viewModel),
|
body: _buildScaffold(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||||
SafeArea(child: _buildBodyColumn(viewModel));
|
SafeArea(child: _buildBodyColumn(viewModel));
|
||||||
|
|
||||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: _buildBodyColumnChildren(viewModel),
|
children: _buildBodyColumnChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||||
_buildAppBarIndenter(viewModel),
|
_buildAppBarIndenter(viewModel),
|
||||||
_buildQuestionIndenter(viewModel),
|
_buildQuestionIndenter(viewModel),
|
||||||
_buildAssessmentReviewSection(viewModel)
|
_buildAssessmentReviewSection(viewModel)
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildAppBarIndenter(DuolingoViewModel viewModel) => Padding(
|
Widget _buildAppBarIndenter(CoursePracticeViewModel viewModel) => Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildAppBarWrapper(viewModel),
|
child: _buildAppBarWrapper(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||||
children: [
|
children: [
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildAppBar(viewModel),
|
_buildAppBar(viewModel),
|
||||||
|
|
@ -48,12 +48,13 @@ class DuolingoSpeakingAssessment4Review
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||||
|
DuolingoPracticeAppBar(
|
||||||
title: 'Feedback',
|
title: 'Feedback',
|
||||||
onClose: () => viewModel.goTo(0),
|
onClose: () => viewModel.goTo(0),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildQuestionIndenter(DuolingoViewModel viewModel) => Padding(
|
Widget _buildQuestionIndenter(CoursePracticeViewModel viewModel) => Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildDwarfWrapper(),
|
child: _buildDwarfWrapper(),
|
||||||
);
|
);
|
||||||
|
|
@ -69,6 +70,6 @@ class DuolingoSpeakingAssessment4Review
|
||||||
style: style16DG600,
|
style: style16DG600,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAssessmentReviewSection(DuolingoViewModel viewModel) =>
|
Widget _buildAssessmentReviewSection(CoursePracticeViewModel viewModel) =>
|
||||||
SpeakingAssessmentReviewSection(onTap: () => viewModel.goTo(5));
|
SpeakingPracticeReviewSection(onTap: () => viewModel.goTo(5));
|
||||||
}
|
}
|
||||||
|
|
@ -4,73 +4,74 @@ import 'package:yimaru_app/ui/views/duolingo/duolingo_view.form.dart';
|
||||||
|
|
||||||
import '../../../common/app_colors.dart';
|
import '../../../common/app_colors.dart';
|
||||||
import '../../../common/ui_helpers.dart';
|
import '../../../common/ui_helpers.dart';
|
||||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||||
import '../../../widgets/custom_elevated_button.dart';
|
import '../../../widgets/custom_elevated_button.dart';
|
||||||
import '../duolingo_viewmodel.dart';
|
import '../course_practice_viewmodel.dart';
|
||||||
|
|
||||||
class DuolingoWritingAssessment1Question
|
class DuolingoWritingPractice1Question
|
||||||
extends ViewModelWidget<DuolingoViewModel> {
|
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||||
final TextEditingController assessmentController;
|
final TextEditingController practiceController;
|
||||||
const DuolingoWritingAssessment1Question(
|
const DuolingoWritingPractice1Question(
|
||||||
{super.key, required this.assessmentController});
|
{super.key, required this.practiceController});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||||
_buildScaffoldWrapper(viewModel);
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||||
backgroundColor: kcBackgroundColor,
|
backgroundColor: kcBackgroundColor,
|
||||||
body: _buildScaffold(viewModel),
|
body: _buildScaffold(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||||
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
||||||
|
|
||||||
Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding(
|
Widget _buildBodyColumnWrapper(CoursePracticeViewModel viewModel) => Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildBodyColumn(viewModel),
|
child: _buildBodyColumn(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: _buildBodyColumnChildren(viewModel),
|
children: _buildBodyColumnChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||||
_buildAppBarWrapper(viewModel),
|
_buildAppBarWrapper(viewModel),
|
||||||
_buildQuestionWrapper(viewModel),
|
_buildQuestionWrapper(viewModel),
|
||||||
_buildContinueButtonWrapper(viewModel)
|
_buildContinueButtonWrapper(viewModel)
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||||
children: [
|
children: [
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildAppBar(viewModel),
|
_buildAppBar(viewModel),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||||
|
DuolingoPracticeAppBar(
|
||||||
title: 'Writing Assessment',
|
title: 'Writing Assessment',
|
||||||
onClose: () => viewModel.goTo(0),
|
onClose: () => viewModel.goTo(0),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildQuestionWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildQuestionWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: _buildQuestionChildren(viewModel),
|
children: _buildQuestionChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildQuestionChildren(DuolingoViewModel viewModel) => [
|
List<Widget> _buildQuestionChildren(CoursePracticeViewModel viewModel) => [
|
||||||
_buildTitle(),
|
_buildTitle(),
|
||||||
verticalSpaceSmall,
|
verticalSpaceSmall,
|
||||||
_buildImageContainer(),
|
_buildImageContainer(),
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildAssessmentFormField(viewModel),
|
_buildAssessmentFormField(viewModel),
|
||||||
if (viewModel.hasAssessmentValidationMessage &&
|
if (viewModel.hasAssessmentValidationMessage &&
|
||||||
viewModel.focusAssessment)
|
viewModel.focusPractice)
|
||||||
verticalSpaceTiny,
|
verticalSpaceTiny,
|
||||||
if (viewModel.hasAssessmentValidationMessage &&
|
if (viewModel.hasAssessmentValidationMessage &&
|
||||||
viewModel.focusAssessment)
|
viewModel.focusPractice)
|
||||||
_buildAssessmentWrapper(viewModel),
|
_buildAssessmentWrapper(viewModel),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -97,34 +98,35 @@ class DuolingoWritingAssessment1Question
|
||||||
width: double.maxFinite,
|
width: double.maxFinite,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAssessmentFormField(DuolingoViewModel viewModel) =>
|
Widget _buildAssessmentFormField(CoursePracticeViewModel viewModel) =>
|
||||||
TextFormField(
|
TextFormField(
|
||||||
maxLines: 5,
|
maxLines: 5,
|
||||||
maxLength: 250,
|
maxLength: 250,
|
||||||
controller: assessmentController,
|
controller: practiceController,
|
||||||
onTap: viewModel.setAssessmentFocus,
|
onTap: viewModel.setPracticeFocus,
|
||||||
decoration: inputDecoration(
|
decoration: inputDecoration(
|
||||||
focus: true,
|
focus: true,
|
||||||
hint: 'Start writing here...',
|
hint: 'Start writing here...',
|
||||||
filled: assessmentController.text.isNotEmpty),
|
filled: practiceController.text.isNotEmpty),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAssessmentWrapper(DuolingoViewModel viewModel) =>
|
Widget _buildAssessmentWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
viewModel.hasAssessmentValidationMessage
|
viewModel.hasAssessmentValidationMessage
|
||||||
? _buildAssessmentValidator(viewModel)
|
? _buildAssessmentValidator(viewModel)
|
||||||
: Container();
|
: Container();
|
||||||
|
|
||||||
Widget _buildAssessmentValidator(DuolingoViewModel viewModel) => Text(
|
Widget _buildAssessmentValidator(CoursePracticeViewModel viewModel) => Text(
|
||||||
viewModel.assessmentValidationMessage!,
|
viewModel.assessmentValidationMessage!,
|
||||||
style: style12R700,
|
style: style12R700,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding(
|
Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 50),
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
child: _buildContinueButton(viewModel),
|
child: _buildContinueButton(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildContinueButton(DuolingoViewModel viewModel) =>
|
Widget _buildContinueButton(CoursePracticeViewModel viewModel) =>
|
||||||
CustomElevatedButton(
|
CustomElevatedButton(
|
||||||
height: 55,
|
height: 55,
|
||||||
text: 'Submit',
|
text: 'Submit',
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
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/widgets/duolingo_assessment_review_section.dart';
|
import 'package:yimaru_app/ui/widgets/duolingo_practice_review_section.dart';
|
||||||
|
|
||||||
import '../../../common/app_colors.dart';
|
import '../../../common/app_colors.dart';
|
||||||
import '../../../common/ui_helpers.dart';
|
import '../../../common/ui_helpers.dart';
|
||||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||||
import '../duolingo_viewmodel.dart';
|
import '../../duolingo/duolingo_viewmodel.dart';
|
||||||
|
|
||||||
class DuolingoWritingAssessment1Review
|
class DuolingoWritingPractice1Review
|
||||||
extends ViewModelWidget<DuolingoViewModel> {
|
extends ViewModelWidget<DuolingoViewModel> {
|
||||||
final TextEditingController assessmentController;
|
final TextEditingController practiceController;
|
||||||
|
|
||||||
const DuolingoWritingAssessment1Review(
|
const DuolingoWritingPractice1Review(
|
||||||
{super.key, required this.assessmentController});
|
{super.key, required this.practiceController});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
||||||
|
|
@ -49,7 +49,7 @@ class DuolingoWritingAssessment1Review
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoPracticeAppBar(
|
||||||
title: 'Feedback',
|
title: 'Feedback',
|
||||||
onClose: () => viewModel.goTo(0),
|
onClose: () => viewModel.goTo(0),
|
||||||
);
|
);
|
||||||
|
|
@ -112,13 +112,13 @@ class DuolingoWritingAssessment1Review
|
||||||
maxLines: 5,
|
maxLines: 5,
|
||||||
enabled: false,
|
enabled: false,
|
||||||
maxLength: 250,
|
maxLength: 250,
|
||||||
controller: assessmentController,
|
controller: practiceController,
|
||||||
decoration: inputDecoration(
|
decoration: inputDecoration(
|
||||||
focus: true,
|
focus: true,
|
||||||
hint: 'Start writing here...',
|
hint: 'Start writing here...',
|
||||||
filled: assessmentController.text.isNotEmpty),
|
filled: practiceController.text.isNotEmpty),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAssessmentReviewSection(DuolingoViewModel viewModel) =>
|
Widget _buildAssessmentReviewSection(DuolingoViewModel viewModel) =>
|
||||||
DuolingoAssessmentReviewSection(onTap: () => viewModel.goTo(5));
|
DuolingoPracticeReviewSection(onTap: () => viewModel.goTo(5));
|
||||||
}
|
}
|
||||||
|
|
@ -4,75 +4,76 @@ import 'package:yimaru_app/ui/views/duolingo/duolingo_view.form.dart';
|
||||||
|
|
||||||
import '../../../common/app_colors.dart';
|
import '../../../common/app_colors.dart';
|
||||||
import '../../../common/ui_helpers.dart';
|
import '../../../common/ui_helpers.dart';
|
||||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||||
import '../../../widgets/custom_elevated_button.dart';
|
import '../../../widgets/custom_elevated_button.dart';
|
||||||
import '../../../widgets/duolingo_assessment_question_card.dart';
|
import '../../../widgets/duolingo_practice_question_card.dart';
|
||||||
import '../duolingo_viewmodel.dart';
|
import '../course_practice_viewmodel.dart';
|
||||||
|
|
||||||
class DuolingoWritingAssessment2Answer
|
class DuolingoWritingPractice2Answer
|
||||||
extends ViewModelWidget<DuolingoViewModel> {
|
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||||
final TextEditingController assessmentController;
|
final TextEditingController practiceController;
|
||||||
|
|
||||||
const DuolingoWritingAssessment2Answer(
|
const DuolingoWritingPractice2Answer(
|
||||||
{super.key, required this.assessmentController});
|
{super.key, required this.practiceController});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||||
_buildScaffoldWrapper(viewModel);
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||||
backgroundColor: kcBackgroundColor,
|
backgroundColor: kcBackgroundColor,
|
||||||
body: _buildScaffold(viewModel),
|
body: _buildScaffold(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||||
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
||||||
|
|
||||||
Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding(
|
Widget _buildBodyColumnWrapper(CoursePracticeViewModel viewModel) => Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildBodyColumn(viewModel),
|
child: _buildBodyColumn(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: _buildBodyColumnChildren(viewModel),
|
children: _buildBodyColumnChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||||
_buildAppBarWrapper(viewModel),
|
_buildAppBarWrapper(viewModel),
|
||||||
_buildQuestionWrapper(viewModel),
|
_buildQuestionWrapper(viewModel),
|
||||||
_buildContinueButtonWrapper(viewModel)
|
_buildContinueButtonWrapper(viewModel)
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||||
children: [
|
children: [
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildAppBar(viewModel),
|
_buildAppBar(viewModel),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||||
|
DuolingoPracticeAppBar(
|
||||||
title: 'Writing Assessment',
|
title: 'Writing Assessment',
|
||||||
onClose: () => viewModel.goTo(0),
|
onClose: () => viewModel.goTo(0),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildQuestionWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildQuestionWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: _buildQuestionChildren(viewModel),
|
children: _buildQuestionChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildQuestionChildren(DuolingoViewModel viewModel) => [
|
List<Widget> _buildQuestionChildren(CoursePracticeViewModel viewModel) => [
|
||||||
_buildTitle(),
|
_buildTitle(),
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildQuestion(),
|
_buildQuestion(),
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildAssessmentFormField(viewModel),
|
_buildAssessmentFormField(viewModel),
|
||||||
if (viewModel.hasAssessmentValidationMessage &&
|
if (viewModel.hasAssessmentValidationMessage &&
|
||||||
viewModel.focusAssessment)
|
viewModel.focusPractice)
|
||||||
verticalSpaceTiny,
|
verticalSpaceTiny,
|
||||||
if (viewModel.hasAssessmentValidationMessage &&
|
if (viewModel.hasAssessmentValidationMessage &&
|
||||||
viewModel.focusAssessment)
|
viewModel.focusPractice)
|
||||||
_buildAssessmentWrapper(viewModel),
|
_buildAssessmentWrapper(viewModel),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -82,38 +83,39 @@ class DuolingoWritingAssessment2Answer
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildQuestion() => const DuolingoAssessmentQuestionCard(
|
Widget _buildQuestion() => const DuolingoPracticeQuestionCard(
|
||||||
subtitle:
|
subtitle:
|
||||||
'Describe a memorable experience from your school or work life and explain why it was meaningful to you.');
|
'Describe a memorable experience from your school or work life and explain why it was meaningful to you.');
|
||||||
|
|
||||||
Widget _buildAssessmentFormField(DuolingoViewModel viewModel) =>
|
Widget _buildAssessmentFormField(CoursePracticeViewModel viewModel) =>
|
||||||
TextFormField(
|
TextFormField(
|
||||||
maxLines: 5,
|
maxLines: 5,
|
||||||
maxLength: 250,
|
maxLength: 250,
|
||||||
controller: assessmentController,
|
controller: practiceController,
|
||||||
onTap: viewModel.setAssessmentFocus,
|
onTap: viewModel.setPracticeFocus,
|
||||||
decoration: inputDecoration(
|
decoration: inputDecoration(
|
||||||
focus: true,
|
focus: true,
|
||||||
hint: 'Start writing here...',
|
hint: 'Start writing here...',
|
||||||
filled: assessmentController.text.isNotEmpty),
|
filled: practiceController.text.isNotEmpty),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAssessmentWrapper(DuolingoViewModel viewModel) =>
|
Widget _buildAssessmentWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
viewModel.hasAssessmentValidationMessage
|
viewModel.hasAssessmentValidationMessage
|
||||||
? _buildAssessmentValidator(viewModel)
|
? _buildAssessmentValidator(viewModel)
|
||||||
: Container();
|
: Container();
|
||||||
|
|
||||||
Widget _buildAssessmentValidator(DuolingoViewModel viewModel) => Text(
|
Widget _buildAssessmentValidator(CoursePracticeViewModel viewModel) => Text(
|
||||||
viewModel.assessmentValidationMessage!,
|
viewModel.assessmentValidationMessage!,
|
||||||
style: style12R700,
|
style: style12R700,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding(
|
Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 50),
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
child: _buildContinueButton(viewModel),
|
child: _buildContinueButton(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildContinueButton(DuolingoViewModel viewModel) =>
|
Widget _buildContinueButton(CoursePracticeViewModel viewModel) =>
|
||||||
CustomElevatedButton(
|
CustomElevatedButton(
|
||||||
height: 55,
|
height: 55,
|
||||||
text: 'Submit',
|
text: 'Submit',
|
||||||
|
|
@ -3,65 +3,66 @@ import 'package:stacked/stacked.dart';
|
||||||
|
|
||||||
import '../../../common/app_colors.dart';
|
import '../../../common/app_colors.dart';
|
||||||
import '../../../common/ui_helpers.dart';
|
import '../../../common/ui_helpers.dart';
|
||||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||||
import '../../../widgets/custom_elevated_button.dart';
|
import '../../../widgets/custom_elevated_button.dart';
|
||||||
import '../../../widgets/duolingo_assessment_question_card.dart';
|
import '../../../widgets/duolingo_practice_question_card.dart';
|
||||||
import '../duolingo_viewmodel.dart';
|
import '../course_practice_viewmodel.dart';
|
||||||
|
|
||||||
class DuolingoWritingAssessment2Question
|
class DuolingoWritingPractice2Question
|
||||||
extends ViewModelWidget<DuolingoViewModel> {
|
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||||
final TextEditingController assessmentController;
|
final TextEditingController practiceController;
|
||||||
|
|
||||||
const DuolingoWritingAssessment2Question(
|
const DuolingoWritingPractice2Question(
|
||||||
{super.key, required this.assessmentController});
|
{super.key, required this.practiceController});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||||
_buildScaffoldWrapper(viewModel);
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||||
backgroundColor: kcBackgroundColor,
|
backgroundColor: kcBackgroundColor,
|
||||||
body: _buildScaffold(viewModel),
|
body: _buildScaffold(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||||
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
||||||
|
|
||||||
Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding(
|
Widget _buildBodyColumnWrapper(CoursePracticeViewModel viewModel) => Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildBodyColumn(viewModel),
|
child: _buildBodyColumn(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: _buildBodyColumnChildren(viewModel),
|
children: _buildBodyColumnChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||||
_buildAppBarWrapper(viewModel),
|
_buildAppBarWrapper(viewModel),
|
||||||
_buildQuestionWrapper(viewModel),
|
_buildQuestionWrapper(viewModel),
|
||||||
_buildContinueButtonWrapper(viewModel)
|
_buildContinueButtonWrapper(viewModel)
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||||
children: [
|
children: [
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildAppBar(viewModel),
|
_buildAppBar(viewModel),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||||
|
DuolingoPracticeAppBar(
|
||||||
title: 'Writing Assessment',
|
title: 'Writing Assessment',
|
||||||
onClose: () => viewModel.goTo(0),
|
onClose: () => viewModel.goTo(0),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildQuestionWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildQuestionWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: _buildQuestionChildren(viewModel),
|
children: _buildQuestionChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildQuestionChildren(DuolingoViewModel viewModel) => [
|
List<Widget> _buildQuestionChildren(CoursePracticeViewModel viewModel) => [
|
||||||
_buildTitle(),
|
_buildTitle(),
|
||||||
_buildSubtitle(),
|
_buildSubtitle(),
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
|
|
@ -81,16 +82,17 @@ class DuolingoWritingAssessment2Question
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildQuestion() => const DuolingoAssessmentQuestionCard(
|
Widget _buildQuestion() => const DuolingoPracticeQuestionCard(
|
||||||
subtitle:
|
subtitle:
|
||||||
'Describe a memorable experience from your school or work life and explain why it was meaningful to you.');
|
'Describe a memorable experience from your school or work life and explain why it was meaningful to you.');
|
||||||
|
|
||||||
Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding(
|
Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 50),
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
child: _buildContinueButton(viewModel),
|
child: _buildContinueButton(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildContinueButton(DuolingoViewModel viewModel) =>
|
Widget _buildContinueButton(CoursePracticeViewModel viewModel) =>
|
||||||
CustomElevatedButton(
|
CustomElevatedButton(
|
||||||
height: 55,
|
height: 55,
|
||||||
text: 'Submit',
|
text: 'Submit',
|
||||||
|
|
@ -1,48 +1,48 @@
|
||||||
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/widgets/duolingo_assessment_review_section.dart';
|
import 'package:yimaru_app/ui/widgets/duolingo_practice_review_section.dart';
|
||||||
|
|
||||||
import '../../../common/app_colors.dart';
|
import '../../../common/app_colors.dart';
|
||||||
import '../../../common/ui_helpers.dart';
|
import '../../../common/ui_helpers.dart';
|
||||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||||
import '../../../widgets/duolingo_assessment_question_card.dart';
|
import '../../../widgets/duolingo_practice_question_card.dart';
|
||||||
import '../duolingo_viewmodel.dart';
|
import '../course_practice_viewmodel.dart';
|
||||||
|
|
||||||
class DuolingoWritingAssessment2Review
|
class DuolingoWritingPractice2Review
|
||||||
extends ViewModelWidget<DuolingoViewModel> {
|
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||||
final TextEditingController assessmentController;
|
final TextEditingController practiceController;
|
||||||
|
|
||||||
const DuolingoWritingAssessment2Review(
|
const DuolingoWritingPractice2Review(
|
||||||
{super.key, required this.assessmentController});
|
{super.key, required this.practiceController});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||||
_buildScaffoldWrapper(viewModel);
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||||
backgroundColor: kcBackgroundColor,
|
backgroundColor: kcBackgroundColor,
|
||||||
body: _buildScaffold(viewModel),
|
body: _buildScaffold(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||||
SafeArea(child: _buildBodyColumn(viewModel));
|
SafeArea(child: _buildBodyColumn(viewModel));
|
||||||
|
|
||||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: _buildBodyColumnChildren(viewModel),
|
children: _buildBodyColumnChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||||
_buildAppBarIndenter(viewModel),
|
_buildAppBarIndenter(viewModel),
|
||||||
_buildExpandedBody(viewModel),
|
_buildExpandedBody(viewModel),
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildAppBarIndenter(DuolingoViewModel viewModel) => Padding(
|
Widget _buildAppBarIndenter(CoursePracticeViewModel viewModel) => Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildAppBarWrapper(viewModel),
|
child: _buildAppBarWrapper(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||||
children: [
|
children: [
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildAppBar(viewModel),
|
_buildAppBar(viewModel),
|
||||||
|
|
@ -50,27 +50,29 @@ class DuolingoWritingAssessment2Review
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||||
|
DuolingoPracticeAppBar(
|
||||||
title: 'Feedback',
|
title: 'Feedback',
|
||||||
onClose: () => viewModel.goTo(0),
|
onClose: () => viewModel.goTo(0),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildExpandedBody(DuolingoViewModel viewModel) =>
|
Widget _buildExpandedBody(CoursePracticeViewModel viewModel) =>
|
||||||
Expanded(child: _buildBodyScroller(viewModel));
|
Expanded(child: _buildBodyScroller(viewModel));
|
||||||
|
|
||||||
Widget _buildBodyScroller(DuolingoViewModel viewModel) =>
|
Widget _buildBodyScroller(CoursePracticeViewModel viewModel) =>
|
||||||
SingleChildScrollView(
|
SingleChildScrollView(
|
||||||
child: _buildQuestionSectionWrapper(viewModel),
|
child: _buildQuestionSectionWrapper(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildQuestionSectionWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildQuestionSectionWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
|
Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: _buildQuestionQuestionSectionChildren(viewModel),
|
children: _buildQuestionQuestionSectionChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildQuestionQuestionSectionChildren(
|
List<Widget> _buildQuestionQuestionSectionChildren(
|
||||||
DuolingoViewModel viewModel) =>
|
CoursePracticeViewModel viewModel) =>
|
||||||
[
|
[
|
||||||
verticalSpaceLarge,
|
verticalSpaceLarge,
|
||||||
_buildTitle(),
|
_buildTitle(),
|
||||||
|
|
@ -93,28 +95,28 @@ class DuolingoWritingAssessment2Review
|
||||||
child: _buildQuestion(),
|
child: _buildQuestion(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildQuestion() => const DuolingoAssessmentQuestionCard(
|
Widget _buildQuestion() => const DuolingoPracticeQuestionCard(
|
||||||
subtitle:
|
subtitle:
|
||||||
'Describe a memorable experience from your school or work life and explain why it was meaningful to you.');
|
'Describe a memorable experience from your school or work life and explain why it was meaningful to you.');
|
||||||
|
|
||||||
Widget _buildAssessmentFormFieldWrapper(DuolingoViewModel viewModel) =>
|
Widget _buildAssessmentFormFieldWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildAssessmentFormField(viewModel),
|
child: _buildAssessmentFormField(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAssessmentFormField(DuolingoViewModel viewModel) =>
|
Widget _buildAssessmentFormField(CoursePracticeViewModel viewModel) =>
|
||||||
TextFormField(
|
TextFormField(
|
||||||
maxLines: 5,
|
maxLines: 5,
|
||||||
enabled: false,
|
enabled: false,
|
||||||
maxLength: 250,
|
maxLength: 250,
|
||||||
controller: assessmentController,
|
controller: practiceController,
|
||||||
decoration: inputDecoration(
|
decoration: inputDecoration(
|
||||||
focus: true,
|
focus: true,
|
||||||
hint: 'Start writing here...',
|
hint: 'Start writing here...',
|
||||||
filled: assessmentController.text.isNotEmpty),
|
filled: practiceController.text.isNotEmpty),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAssessmentReviewSection(DuolingoViewModel viewModel) =>
|
Widget _buildAssessmentReviewSection(CoursePracticeViewModel viewModel) =>
|
||||||
DuolingoAssessmentReviewSection(onTap: () => viewModel.goTo(5));
|
DuolingoPracticeReviewSection(onTap: () => viewModel.goTo(5));
|
||||||
}
|
}
|
||||||
|
|
@ -4,75 +4,76 @@ import 'package:yimaru_app/ui/views/duolingo/duolingo_view.form.dart';
|
||||||
|
|
||||||
import '../../../common/app_colors.dart';
|
import '../../../common/app_colors.dart';
|
||||||
import '../../../common/ui_helpers.dart';
|
import '../../../common/ui_helpers.dart';
|
||||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||||
import '../../../widgets/custom_elevated_button.dart';
|
import '../../../widgets/custom_elevated_button.dart';
|
||||||
import '../../../widgets/duolingo_assessment_question_card.dart';
|
import '../../../widgets/duolingo_practice_question_card.dart';
|
||||||
import '../duolingo_viewmodel.dart';
|
import '../course_practice_viewmodel.dart';
|
||||||
|
|
||||||
class DuolingoWritingAssessment3Question
|
class DuolingoWritingPractice3Question
|
||||||
extends ViewModelWidget<DuolingoViewModel> {
|
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||||
final TextEditingController assessmentController;
|
final TextEditingController practiceController;
|
||||||
|
|
||||||
const DuolingoWritingAssessment3Question(
|
const DuolingoWritingPractice3Question(
|
||||||
{super.key, required this.assessmentController});
|
{super.key, required this.practiceController});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||||
_buildScaffoldWrapper(viewModel);
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||||
backgroundColor: kcBackgroundColor,
|
backgroundColor: kcBackgroundColor,
|
||||||
body: _buildScaffold(viewModel),
|
body: _buildScaffold(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||||
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
||||||
|
|
||||||
Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding(
|
Widget _buildBodyColumnWrapper(CoursePracticeViewModel viewModel) => Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildBodyColumn(viewModel),
|
child: _buildBodyColumn(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: _buildBodyColumnChildren(viewModel),
|
children: _buildBodyColumnChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||||
_buildAppBarWrapper(viewModel),
|
_buildAppBarWrapper(viewModel),
|
||||||
_buildQuestionWrapper(viewModel),
|
_buildQuestionWrapper(viewModel),
|
||||||
_buildContinueButtonWrapper(viewModel)
|
_buildContinueButtonWrapper(viewModel)
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||||
children: [
|
children: [
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildAppBar(viewModel),
|
_buildAppBar(viewModel),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||||
|
DuolingoPracticeAppBar(
|
||||||
title: 'Writing Assessment',
|
title: 'Writing Assessment',
|
||||||
onClose: () => viewModel.goTo(0),
|
onClose: () => viewModel.goTo(0),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildQuestionWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildQuestionWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: _buildQuestionChildren(viewModel),
|
children: _buildQuestionChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildQuestionChildren(DuolingoViewModel viewModel) => [
|
List<Widget> _buildQuestionChildren(CoursePracticeViewModel viewModel) => [
|
||||||
_buildTitle(),
|
_buildTitle(),
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildQuestion(),
|
_buildQuestion(),
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildAssessmentFormField(viewModel),
|
_buildAssessmentFormField(viewModel),
|
||||||
if (viewModel.hasAssessmentValidationMessage &&
|
if (viewModel.hasAssessmentValidationMessage &&
|
||||||
viewModel.focusAssessment)
|
viewModel.focusPractice)
|
||||||
verticalSpaceTiny,
|
verticalSpaceTiny,
|
||||||
if (viewModel.hasAssessmentValidationMessage &&
|
if (viewModel.hasAssessmentValidationMessage &&
|
||||||
viewModel.focusAssessment)
|
viewModel.focusPractice)
|
||||||
_buildAssessmentWrapper(viewModel),
|
_buildAssessmentWrapper(viewModel),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -82,38 +83,39 @@ class DuolingoWritingAssessment3Question
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildQuestion() => const DuolingoAssessmentQuestionCard(
|
Widget _buildQuestion() => const DuolingoPracticeQuestionCard(
|
||||||
title: '1. Can you tell me about your favorite type of music?',
|
title: '1. Can you tell me about your favorite type of music?',
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAssessmentFormField(DuolingoViewModel viewModel) =>
|
Widget _buildAssessmentFormField(CoursePracticeViewModel viewModel) =>
|
||||||
TextFormField(
|
TextFormField(
|
||||||
maxLines: 5,
|
maxLines: 5,
|
||||||
maxLength: 250,
|
maxLength: 250,
|
||||||
controller: assessmentController,
|
controller: practiceController,
|
||||||
onTap: viewModel.setAssessmentFocus,
|
onTap: viewModel.setPracticeFocus,
|
||||||
decoration: inputDecoration(
|
decoration: inputDecoration(
|
||||||
focus: true,
|
focus: true,
|
||||||
hint: 'Start writing here...',
|
hint: 'Start writing here...',
|
||||||
filled: assessmentController.text.isNotEmpty),
|
filled: practiceController.text.isNotEmpty),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAssessmentWrapper(DuolingoViewModel viewModel) =>
|
Widget _buildAssessmentWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
viewModel.hasAssessmentValidationMessage
|
viewModel.hasAssessmentValidationMessage
|
||||||
? _buildAssessmentValidator(viewModel)
|
? _buildAssessmentValidator(viewModel)
|
||||||
: Container();
|
: Container();
|
||||||
|
|
||||||
Widget _buildAssessmentValidator(DuolingoViewModel viewModel) => Text(
|
Widget _buildAssessmentValidator(CoursePracticeViewModel viewModel) => Text(
|
||||||
viewModel.assessmentValidationMessage!,
|
viewModel.assessmentValidationMessage!,
|
||||||
style: style12R700,
|
style: style12R700,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding(
|
Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 50),
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
child: _buildContinueButton(viewModel),
|
child: _buildContinueButton(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildContinueButton(DuolingoViewModel viewModel) =>
|
Widget _buildContinueButton(CoursePracticeViewModel viewModel) =>
|
||||||
CustomElevatedButton(
|
CustomElevatedButton(
|
||||||
height: 55,
|
height: 55,
|
||||||
text: 'Submit',
|
text: 'Submit',
|
||||||
|
|
@ -1,48 +1,48 @@
|
||||||
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/widgets/duolingo_assessment_review_section.dart';
|
import 'package:yimaru_app/ui/widgets/duolingo_practice_review_section.dart';
|
||||||
|
|
||||||
import '../../../common/app_colors.dart';
|
import '../../../common/app_colors.dart';
|
||||||
import '../../../common/ui_helpers.dart';
|
import '../../../common/ui_helpers.dart';
|
||||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||||
import '../../../widgets/duolingo_assessment_question_card.dart';
|
import '../../../widgets/duolingo_practice_question_card.dart';
|
||||||
import '../duolingo_viewmodel.dart';
|
import '../course_practice_viewmodel.dart';
|
||||||
|
|
||||||
class DuolingoWritingAssessment3Review
|
class DuolingoWritingPractice3Review
|
||||||
extends ViewModelWidget<DuolingoViewModel> {
|
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||||
final TextEditingController assessmentController;
|
final TextEditingController practiceController;
|
||||||
|
|
||||||
const DuolingoWritingAssessment3Review(
|
const DuolingoWritingPractice3Review(
|
||||||
{super.key, required this.assessmentController});
|
{super.key, required this.practiceController});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||||
_buildScaffoldWrapper(viewModel);
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||||
backgroundColor: kcBackgroundColor,
|
backgroundColor: kcBackgroundColor,
|
||||||
body: _buildScaffold(viewModel),
|
body: _buildScaffold(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||||
SafeArea(child: _buildBodyColumn(viewModel));
|
SafeArea(child: _buildBodyColumn(viewModel));
|
||||||
|
|
||||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: _buildBodyColumnChildren(viewModel),
|
children: _buildBodyColumnChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||||
_buildAppBarIndenter(viewModel),
|
_buildAppBarIndenter(viewModel),
|
||||||
_buildExpandedBody(viewModel),
|
_buildExpandedBody(viewModel),
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildAppBarIndenter(DuolingoViewModel viewModel) => Padding(
|
Widget _buildAppBarIndenter(CoursePracticeViewModel viewModel) => Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildAppBarWrapper(viewModel),
|
child: _buildAppBarWrapper(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||||
children: [
|
children: [
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildAppBar(viewModel),
|
_buildAppBar(viewModel),
|
||||||
|
|
@ -50,27 +50,29 @@ class DuolingoWritingAssessment3Review
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||||
|
DuolingoPracticeAppBar(
|
||||||
title: 'Feedback',
|
title: 'Feedback',
|
||||||
onClose: () => viewModel.goTo(0),
|
onClose: () => viewModel.goTo(0),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildExpandedBody(DuolingoViewModel viewModel) =>
|
Widget _buildExpandedBody(CoursePracticeViewModel viewModel) =>
|
||||||
Expanded(child: _buildBodyScroller(viewModel));
|
Expanded(child: _buildBodyScroller(viewModel));
|
||||||
|
|
||||||
Widget _buildBodyScroller(DuolingoViewModel viewModel) =>
|
Widget _buildBodyScroller(CoursePracticeViewModel viewModel) =>
|
||||||
SingleChildScrollView(
|
SingleChildScrollView(
|
||||||
child: _buildQuestionSectionWrapper(viewModel),
|
child: _buildQuestionSectionWrapper(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildQuestionSectionWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildQuestionSectionWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
|
Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: _buildQuestionQuestionSectionChildren(viewModel),
|
children: _buildQuestionQuestionSectionChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildQuestionQuestionSectionChildren(
|
List<Widget> _buildQuestionQuestionSectionChildren(
|
||||||
DuolingoViewModel viewModel) =>
|
CoursePracticeViewModel viewModel) =>
|
||||||
[
|
[
|
||||||
verticalSpaceLarge,
|
verticalSpaceLarge,
|
||||||
_buildTitle(),
|
_buildTitle(),
|
||||||
|
|
@ -93,27 +95,27 @@ class DuolingoWritingAssessment3Review
|
||||||
child: _buildQuestion(),
|
child: _buildQuestion(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildQuestion() => const DuolingoAssessmentQuestionCard(
|
Widget _buildQuestion() => const DuolingoPracticeQuestionCard(
|
||||||
title: 'Can you tell me about your favorite type of music?');
|
title: 'Can you tell me about your favorite type of music?');
|
||||||
|
|
||||||
Widget _buildAssessmentFormFieldWrapper(DuolingoViewModel viewModel) =>
|
Widget _buildAssessmentFormFieldWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildAssessmentFormField(viewModel),
|
child: _buildAssessmentFormField(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAssessmentFormField(DuolingoViewModel viewModel) =>
|
Widget _buildAssessmentFormField(CoursePracticeViewModel viewModel) =>
|
||||||
TextFormField(
|
TextFormField(
|
||||||
maxLines: 5,
|
maxLines: 5,
|
||||||
enabled: false,
|
enabled: false,
|
||||||
maxLength: 250,
|
maxLength: 250,
|
||||||
controller: assessmentController,
|
controller: practiceController,
|
||||||
decoration: inputDecoration(
|
decoration: inputDecoration(
|
||||||
focus: true,
|
focus: true,
|
||||||
hint: 'Start writing here...',
|
hint: 'Start writing here...',
|
||||||
filled: assessmentController.text.isNotEmpty),
|
filled: practiceController.text.isNotEmpty),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAssessmentReviewSection(DuolingoViewModel viewModel) =>
|
Widget _buildAssessmentReviewSection(CoursePracticeViewModel viewModel) =>
|
||||||
DuolingoAssessmentReviewSection(onTap: () => viewModel.goTo(5));
|
DuolingoPracticeReviewSection(onTap: () => viewModel.goTo(5));
|
||||||
}
|
}
|
||||||
|
|
@ -4,75 +4,76 @@ import 'package:yimaru_app/ui/views/duolingo/duolingo_view.form.dart';
|
||||||
|
|
||||||
import '../../../common/app_colors.dart';
|
import '../../../common/app_colors.dart';
|
||||||
import '../../../common/ui_helpers.dart';
|
import '../../../common/ui_helpers.dart';
|
||||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||||
import '../../../widgets/custom_elevated_button.dart';
|
import '../../../widgets/custom_elevated_button.dart';
|
||||||
import '../../../widgets/duolingo_assessment_question_card.dart';
|
import '../../../widgets/duolingo_practice_question_card.dart';
|
||||||
import '../duolingo_viewmodel.dart';
|
import '../course_practice_viewmodel.dart';
|
||||||
|
|
||||||
class DuolingoWritingAssessment4Question
|
class DuolingoWritingPractice4Question
|
||||||
extends ViewModelWidget<DuolingoViewModel> {
|
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||||
final TextEditingController assessmentController;
|
final TextEditingController practiceController;
|
||||||
|
|
||||||
const DuolingoWritingAssessment4Question(
|
const DuolingoWritingPractice4Question(
|
||||||
{super.key, required this.assessmentController});
|
{super.key, required this.practiceController});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||||
_buildScaffoldWrapper(viewModel);
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||||
backgroundColor: kcBackgroundColor,
|
backgroundColor: kcBackgroundColor,
|
||||||
body: _buildScaffold(viewModel),
|
body: _buildScaffold(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||||
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
||||||
|
|
||||||
Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding(
|
Widget _buildBodyColumnWrapper(CoursePracticeViewModel viewModel) => Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildBodyColumn(viewModel),
|
child: _buildBodyColumn(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: _buildBodyColumnChildren(viewModel),
|
children: _buildBodyColumnChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||||
_buildAppBarWrapper(viewModel),
|
_buildAppBarWrapper(viewModel),
|
||||||
_buildQuestionWrapper(viewModel),
|
_buildQuestionWrapper(viewModel),
|
||||||
_buildContinueButtonWrapper(viewModel)
|
_buildContinueButtonWrapper(viewModel)
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||||
children: [
|
children: [
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildAppBar(viewModel),
|
_buildAppBar(viewModel),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||||
|
DuolingoPracticeAppBar(
|
||||||
title: 'Writing Assessment',
|
title: 'Writing Assessment',
|
||||||
onClose: () => viewModel.goTo(0),
|
onClose: () => viewModel.goTo(0),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildQuestionWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildQuestionWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: _buildQuestionChildren(viewModel),
|
children: _buildQuestionChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildQuestionChildren(DuolingoViewModel viewModel) => [
|
List<Widget> _buildQuestionChildren(CoursePracticeViewModel viewModel) => [
|
||||||
_buildTitle(),
|
_buildTitle(),
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildQuestion(),
|
_buildQuestion(),
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildAssessmentFormField(viewModel),
|
_buildAssessmentFormField(viewModel),
|
||||||
if (viewModel.hasAssessmentValidationMessage &&
|
if (viewModel.hasAssessmentValidationMessage &&
|
||||||
viewModel.focusAssessment)
|
viewModel.focusPractice)
|
||||||
verticalSpaceTiny,
|
verticalSpaceTiny,
|
||||||
if (viewModel.hasAssessmentValidationMessage &&
|
if (viewModel.hasAssessmentValidationMessage &&
|
||||||
viewModel.focusAssessment)
|
viewModel.focusPractice)
|
||||||
_buildAssessmentWrapper(viewModel),
|
_buildAssessmentWrapper(viewModel),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -82,38 +83,39 @@ class DuolingoWritingAssessment4Question
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildQuestion() => const DuolingoAssessmentQuestionCard(
|
Widget _buildQuestion() => const DuolingoPracticeQuestionCard(
|
||||||
title: '1. Can you tell me about your favorite type of music?',
|
title: '1. Can you tell me about your favorite type of music?',
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAssessmentFormField(DuolingoViewModel viewModel) =>
|
Widget _buildAssessmentFormField(CoursePracticeViewModel viewModel) =>
|
||||||
TextFormField(
|
TextFormField(
|
||||||
maxLines: 5,
|
maxLines: 5,
|
||||||
maxLength: 250,
|
maxLength: 250,
|
||||||
controller: assessmentController,
|
controller: practiceController,
|
||||||
onTap: viewModel.setAssessmentFocus,
|
onTap: viewModel.setPracticeFocus,
|
||||||
decoration: inputDecoration(
|
decoration: inputDecoration(
|
||||||
focus: true,
|
focus: true,
|
||||||
hint: 'Start writing here...',
|
hint: 'Start writing here...',
|
||||||
filled: assessmentController.text.isNotEmpty),
|
filled: practiceController.text.isNotEmpty),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAssessmentWrapper(DuolingoViewModel viewModel) =>
|
Widget _buildAssessmentWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
viewModel.hasAssessmentValidationMessage
|
viewModel.hasAssessmentValidationMessage
|
||||||
? _buildAssessmentValidator(viewModel)
|
? _buildAssessmentValidator(viewModel)
|
||||||
: Container();
|
: Container();
|
||||||
|
|
||||||
Widget _buildAssessmentValidator(DuolingoViewModel viewModel) => Text(
|
Widget _buildAssessmentValidator(CoursePracticeViewModel viewModel) => Text(
|
||||||
viewModel.assessmentValidationMessage!,
|
viewModel.assessmentValidationMessage!,
|
||||||
style: style12R700,
|
style: style12R700,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding(
|
Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 50),
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
child: _buildContinueButton(viewModel),
|
child: _buildContinueButton(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildContinueButton(DuolingoViewModel viewModel) =>
|
Widget _buildContinueButton(CoursePracticeViewModel viewModel) =>
|
||||||
CustomElevatedButton(
|
CustomElevatedButton(
|
||||||
height: 55,
|
height: 55,
|
||||||
text: 'Submit',
|
text: 'Submit',
|
||||||
|
|
@ -1,48 +1,48 @@
|
||||||
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/widgets/duolingo_assessment_review_section.dart';
|
import 'package:yimaru_app/ui/widgets/duolingo_practice_review_section.dart';
|
||||||
|
|
||||||
import '../../../common/app_colors.dart';
|
import '../../../common/app_colors.dart';
|
||||||
import '../../../common/ui_helpers.dart';
|
import '../../../common/ui_helpers.dart';
|
||||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||||
import '../../../widgets/duolingo_assessment_question_card.dart';
|
import '../../../widgets/duolingo_practice_question_card.dart';
|
||||||
import '../duolingo_viewmodel.dart';
|
import '../course_practice_viewmodel.dart';
|
||||||
|
|
||||||
class DuolingoWritingAssessment4Review
|
class DuolingoWritingPractice4Review
|
||||||
extends ViewModelWidget<DuolingoViewModel> {
|
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||||
final TextEditingController assessmentController;
|
final TextEditingController practiceController;
|
||||||
|
|
||||||
const DuolingoWritingAssessment4Review(
|
const DuolingoWritingPractice4Review(
|
||||||
{super.key, required this.assessmentController});
|
{super.key, required this.practiceController});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||||
_buildScaffoldWrapper(viewModel);
|
_buildScaffoldWrapper(viewModel);
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||||
backgroundColor: kcBackgroundColor,
|
backgroundColor: kcBackgroundColor,
|
||||||
body: _buildScaffold(viewModel),
|
body: _buildScaffold(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||||
SafeArea(child: _buildBodyColumn(viewModel));
|
SafeArea(child: _buildBodyColumn(viewModel));
|
||||||
|
|
||||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: _buildBodyColumnChildren(viewModel),
|
children: _buildBodyColumnChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||||
_buildAppBarIndenter(viewModel),
|
_buildAppBarIndenter(viewModel),
|
||||||
_buildExpandedBody(viewModel),
|
_buildExpandedBody(viewModel),
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildAppBarIndenter(DuolingoViewModel viewModel) => Padding(
|
Widget _buildAppBarIndenter(CoursePracticeViewModel viewModel) => Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildAppBarWrapper(viewModel),
|
child: _buildAppBarWrapper(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||||
children: [
|
children: [
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildAppBar(viewModel),
|
_buildAppBar(viewModel),
|
||||||
|
|
@ -50,27 +50,29 @@ class DuolingoWritingAssessment4Review
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||||
|
DuolingoPracticeAppBar(
|
||||||
title: 'Feedback',
|
title: 'Feedback',
|
||||||
onClose: () => viewModel.goTo(0),
|
onClose: () => viewModel.goTo(0),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildExpandedBody(DuolingoViewModel viewModel) =>
|
Widget _buildExpandedBody(CoursePracticeViewModel viewModel) =>
|
||||||
Expanded(child: _buildBodyScroller(viewModel));
|
Expanded(child: _buildBodyScroller(viewModel));
|
||||||
|
|
||||||
Widget _buildBodyScroller(DuolingoViewModel viewModel) =>
|
Widget _buildBodyScroller(CoursePracticeViewModel viewModel) =>
|
||||||
SingleChildScrollView(
|
SingleChildScrollView(
|
||||||
child: _buildQuestionSectionWrapper(viewModel),
|
child: _buildQuestionSectionWrapper(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildQuestionSectionWrapper(DuolingoViewModel viewModel) => Column(
|
Widget _buildQuestionSectionWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
|
Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: _buildQuestionQuestionSectionChildren(viewModel),
|
children: _buildQuestionQuestionSectionChildren(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildQuestionQuestionSectionChildren(
|
List<Widget> _buildQuestionQuestionSectionChildren(
|
||||||
DuolingoViewModel viewModel) =>
|
CoursePracticeViewModel viewModel) =>
|
||||||
[
|
[
|
||||||
verticalSpaceLarge,
|
verticalSpaceLarge,
|
||||||
_buildTitle(),
|
_buildTitle(),
|
||||||
|
|
@ -93,27 +95,27 @@ class DuolingoWritingAssessment4Review
|
||||||
child: _buildQuestion(),
|
child: _buildQuestion(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildQuestion() => const DuolingoAssessmentQuestionCard(
|
Widget _buildQuestion() => const DuolingoPracticeQuestionCard(
|
||||||
title: 'Can you tell me about your favorite type of music?');
|
title: 'Can you tell me about your favorite type of music?');
|
||||||
|
|
||||||
Widget _buildAssessmentFormFieldWrapper(DuolingoViewModel viewModel) =>
|
Widget _buildAssessmentFormFieldWrapper(CoursePracticeViewModel viewModel) =>
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildAssessmentFormField(viewModel),
|
child: _buildAssessmentFormField(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAssessmentFormField(DuolingoViewModel viewModel) =>
|
Widget _buildAssessmentFormField(CoursePracticeViewModel viewModel) =>
|
||||||
TextFormField(
|
TextFormField(
|
||||||
maxLines: 5,
|
maxLines: 5,
|
||||||
enabled: false,
|
enabled: false,
|
||||||
maxLength: 250,
|
maxLength: 250,
|
||||||
controller: assessmentController,
|
controller: practiceController,
|
||||||
decoration: inputDecoration(
|
decoration: inputDecoration(
|
||||||
focus: true,
|
focus: true,
|
||||||
hint: 'Start writing here...',
|
hint: 'Start writing here...',
|
||||||
filled: assessmentController.text.isNotEmpty),
|
filled: practiceController.text.isNotEmpty),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAssessmentReviewSection(DuolingoViewModel viewModel) =>
|
Widget _buildAssessmentReviewSection(CoursePracticeViewModel viewModel) =>
|
||||||
DuolingoAssessmentReviewSection(onTap: () => viewModel.goTo(5));
|
DuolingoPracticeReviewSection(onTap: () => viewModel.goTo(5));
|
||||||
}
|
}
|
||||||
|
|
@ -2,37 +2,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
import 'package:stacked/stacked_annotations.dart';
|
import 'package:stacked/stacked_annotations.dart';
|
||||||
import 'package:yimaru_app/ui/views/duolingo/duolingo_view.form.dart';
|
import 'package:yimaru_app/ui/views/duolingo/duolingo_view.form.dart';
|
||||||
import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_assessments_screens.dart';
|
|
||||||
import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_finish_screen.dart';
|
|
||||||
import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_intro_screen.dart';
|
|
||||||
import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_listening_assessment_1_question.dart';
|
|
||||||
import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_listening_assessment_1_review.dart';
|
|
||||||
import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_listening_assessment_2_question.dart';
|
|
||||||
import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_listening_assessment_2_review.dart';
|
|
||||||
import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_listening_assessment_3_question.dart';
|
|
||||||
import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_retake_screen.dart';
|
|
||||||
import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_speaking_assessment_1_answer.dart';
|
|
||||||
import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_speaking_assessment_1_question.dart';
|
|
||||||
import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_speaking_assessment_1_review.dart';
|
|
||||||
import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_speaking_assessment_2_answer.dart';
|
|
||||||
import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_speaking_assessment_2_question.dart';
|
|
||||||
import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_speaking_assessment_2_review.dart';
|
|
||||||
import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_speaking_assessment_3_answer.dart';
|
|
||||||
import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_speaking_assessment_3_question.dart';
|
|
||||||
import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_speaking_assessment_3_review.dart'
|
|
||||||
hide DuolingoSpeakingAssessment2Question;
|
|
||||||
import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_speaking_assessment_4_answer.dart';
|
|
||||||
import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_speaking_assessment_4_question.dart';
|
|
||||||
import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_speaking_assessment_4_review.dart';
|
|
||||||
import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_writing_assessment_1_question.dart';
|
|
||||||
import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_writing_assessment_1_review.dart';
|
|
||||||
import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_writing_assessment_2_answer.dart';
|
|
||||||
import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_writing_assessment_2_question.dart';
|
|
||||||
import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_writing_assessment_2_review.dart';
|
|
||||||
import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_writing_assessment_3_question.dart';
|
|
||||||
import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_writing_assessment_3_review.dart';
|
|
||||||
import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_writing_assessment_4_question.dart';
|
|
||||||
import 'package:yimaru_app/ui/views/duolingo/screens/duolingo_writing_assessment_4_review.dart';
|
|
||||||
|
|
||||||
import '../../common/app_colors.dart';
|
import '../../common/app_colors.dart';
|
||||||
import '../../common/validators/form_validator.dart';
|
import '../../common/validators/form_validator.dart';
|
||||||
|
|
@ -44,130 +14,7 @@ import 'duolingo_viewmodel.dart';
|
||||||
class DuolingoView extends StackedView<DuolingoViewModel> with $DuolingoView {
|
class DuolingoView extends StackedView<DuolingoViewModel> with $DuolingoView {
|
||||||
const DuolingoView({Key? key}) : super(key: key);
|
const DuolingoView({Key? key}) : super(key: key);
|
||||||
|
|
||||||
Widget _buildQuestionScreen(DuolingoViewModel viewModel) {
|
|
||||||
if (viewModel.selectedAssessment['intro_title'] ==
|
|
||||||
'Speak About the Photo') {
|
|
||||||
return const DuolingoSpeakingAssessment1Question();
|
|
||||||
} else if (viewModel.selectedAssessment['intro_title'] ==
|
|
||||||
'Read, Then Speak') {
|
|
||||||
return const DuolingoSpeakingAssessment2Question();
|
|
||||||
} else if (viewModel.selectedAssessment['intro_title'] ==
|
|
||||||
'Speaking Sample') {
|
|
||||||
return const DuolingoSpeakingAssessment3Question();
|
|
||||||
} else if (viewModel.selectedAssessment['intro_title'] ==
|
|
||||||
'Interactive Speaking') {
|
|
||||||
return const DuolingoSpeakingAssessment4Question();
|
|
||||||
} else if (viewModel.selectedAssessment['intro_title'] ==
|
|
||||||
'Write About the Photo') {
|
|
||||||
return DuolingoWritingAssessment1Question(
|
|
||||||
assessmentController: assessmentController);
|
|
||||||
} else if (viewModel.selectedAssessment['intro_title'] ==
|
|
||||||
'Writing Sample') {
|
|
||||||
return DuolingoWritingAssessment2Question(
|
|
||||||
assessmentController: assessmentController);
|
|
||||||
} else if (viewModel.selectedAssessment['intro_title'] ==
|
|
||||||
'Interactive Writing Part 1') {
|
|
||||||
return DuolingoWritingAssessment3Question(
|
|
||||||
assessmentController: assessmentController);
|
|
||||||
} else if (viewModel.selectedAssessment['intro_title'] ==
|
|
||||||
'Interactive Writing Part 2') {
|
|
||||||
return DuolingoWritingAssessment4Question(
|
|
||||||
assessmentController: assessmentController);
|
|
||||||
} else if (viewModel.selectedAssessment['intro_title'] ==
|
|
||||||
'Listen and Type') {
|
|
||||||
return DuolingoListeningAssessment1Question(
|
|
||||||
assessmentController: assessmentController);
|
|
||||||
} else if (viewModel.selectedAssessment['intro_title'] ==
|
|
||||||
'Interactive Listening - Part 1') {
|
|
||||||
return DuolingoListeningAssessment2Question(
|
|
||||||
assessmentController: assessmentController);
|
|
||||||
} else if (viewModel.selectedAssessment['intro_title'] ==
|
|
||||||
'Interactive Listening - Part 2') {
|
|
||||||
return DuolingoListeningAssessment3Question(
|
|
||||||
assessmentController: assessmentController);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Container();
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildAnswerScreen(DuolingoViewModel viewModel) {
|
|
||||||
if (viewModel.selectedAssessment['intro_title'] ==
|
|
||||||
'Speak About the Photo') {
|
|
||||||
return const DuolingoSpeakingAssessment1Answer();
|
|
||||||
} else if (viewModel.selectedAssessment['intro_title'] ==
|
|
||||||
'Read, Then Speak') {
|
|
||||||
return const DuolingoSpeakingAssessment2Answer();
|
|
||||||
} else if (viewModel.selectedAssessment['intro_title'] ==
|
|
||||||
'Speaking Sample') {
|
|
||||||
return const DuolingoSpeakingAssessment3Answer();
|
|
||||||
} else if (viewModel.selectedAssessment['intro_title'] ==
|
|
||||||
'Interactive Speaking') {
|
|
||||||
return const DuolingoSpeakingAssessment4Answer();
|
|
||||||
} else if (viewModel.selectedAssessment['intro_title'] ==
|
|
||||||
'Write About the Photo') {
|
|
||||||
return Container();
|
|
||||||
} else if (viewModel.selectedAssessment['intro_title'] ==
|
|
||||||
'Writing Sample') {
|
|
||||||
return DuolingoWritingAssessment2Answer(
|
|
||||||
assessmentController: assessmentController);
|
|
||||||
} else if (viewModel.selectedAssessment['intro_title'] ==
|
|
||||||
'Interactive Writing Part 1') {
|
|
||||||
return Container();
|
|
||||||
} else if (viewModel.selectedAssessment['intro_title'] ==
|
|
||||||
'Interactive Writing Part 2') {
|
|
||||||
return Container();
|
|
||||||
} else if (viewModel.selectedAssessment['intro_title'] ==
|
|
||||||
'Listen and Type') {
|
|
||||||
return Container();
|
|
||||||
} else if (viewModel.selectedAssessment['intro_title'] ==
|
|
||||||
'Interactive Listening - Part 1') {
|
|
||||||
return Container();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Container();
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildReviewScreen(DuolingoViewModel viewModel) {
|
|
||||||
if (viewModel.selectedAssessment['intro_title'] ==
|
|
||||||
'Speak About the Photo') {
|
|
||||||
return const DuolingoSpeakingAssessment1Review();
|
|
||||||
} else if (viewModel.selectedAssessment['intro_title'] ==
|
|
||||||
'Read, Then Speak') {
|
|
||||||
return const DuolingoSpeakingAssessment2Review();
|
|
||||||
} else if (viewModel.selectedAssessment['intro_title'] ==
|
|
||||||
'Speaking Sample') {
|
|
||||||
return const DuolingoSpeakingAssessment3Review();
|
|
||||||
} else if (viewModel.selectedAssessment['intro_title'] ==
|
|
||||||
'Interactive Speaking') {
|
|
||||||
return const DuolingoSpeakingAssessment4Review();
|
|
||||||
} else if (viewModel.selectedAssessment['intro_title'] ==
|
|
||||||
'Write About the Photo') {
|
|
||||||
return DuolingoWritingAssessment1Review(
|
|
||||||
assessmentController: assessmentController);
|
|
||||||
} else if (viewModel.selectedAssessment['intro_title'] ==
|
|
||||||
'Writing Sample') {
|
|
||||||
return DuolingoWritingAssessment2Review(
|
|
||||||
assessmentController: assessmentController);
|
|
||||||
} else if (viewModel.selectedAssessment['intro_title'] ==
|
|
||||||
'Interactive Writing Part 1') {
|
|
||||||
return DuolingoWritingAssessment3Review(
|
|
||||||
assessmentController: assessmentController);
|
|
||||||
} else if (viewModel.selectedAssessment['intro_title'] ==
|
|
||||||
'Interactive Writing Part 2') {
|
|
||||||
return DuolingoWritingAssessment4Review(
|
|
||||||
assessmentController: assessmentController);
|
|
||||||
} else if (viewModel.selectedAssessment['intro_title'] ==
|
|
||||||
'Listen and Type') {
|
|
||||||
return DuolingoListeningAssessment1Review(
|
|
||||||
assessmentController: assessmentController);
|
|
||||||
} else if (viewModel.selectedAssessment['intro_title'] ==
|
|
||||||
'Interactive Listening - Part 1') {
|
|
||||||
return DuolingoListeningAssessment2Review(
|
|
||||||
assessmentController: assessmentController);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Container();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
DuolingoViewModel viewModelBuilder(BuildContext context) =>
|
DuolingoViewModel viewModelBuilder(BuildContext context) =>
|
||||||
|
|
@ -198,40 +45,40 @@ class DuolingoView extends StackedView<DuolingoViewModel> with $DuolingoView {
|
||||||
index: viewModel.currentPage, children: _buildScreens(viewModel));
|
index: viewModel.currentPage, children: _buildScreens(viewModel));
|
||||||
|
|
||||||
List<Widget> _buildScreens(DuolingoViewModel viewModel) => [
|
List<Widget> _buildScreens(DuolingoViewModel viewModel) => [
|
||||||
_buildDuolingoAssessmentsScreen(),
|
// _buildDuolingoAssessmentsScreen(),
|
||||||
_buildDuolingoIntroScreen(viewModel),
|
// _buildDuolingoIntroScreen(viewModel),
|
||||||
_buildDuolingoQuestionScreen(viewModel),
|
// _buildDuolingoQuestionScreen(viewModel),
|
||||||
_buildDuolingoAnswerScreen(viewModel),
|
// _buildDuolingoAnswerScreen(viewModel),
|
||||||
_buildDuolingoReviewScreen(viewModel),
|
// _buildDuolingoReviewScreen(viewModel),
|
||||||
_buildDuolingoRetakeScreen(viewModel),
|
// _buildDuolingoRetakeScreen(viewModel),
|
||||||
_buildDuolingoFinishScreen(viewModel),
|
// _buildDuolingoFinishScreen(viewModel),
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildDuolingoAssessmentsScreen() =>
|
// Widget _buildDuolingoAssessmentsScreen() =>
|
||||||
const DuolingoAssessmentsScreens();
|
// const DuolingoAssessmentsScreens();
|
||||||
|
//
|
||||||
Widget _buildDuolingoIntroScreen(DuolingoViewModel viewModel) =>
|
// Widget _buildDuolingoIntroScreen(DuolingoViewModel viewModel) =>
|
||||||
DuolingoIntroScreen(
|
// DuolingoIntroScreen(
|
||||||
type: viewModel.selectedAssessment['type'],
|
// type: viewModel.selectedAssessment['type'],
|
||||||
title: viewModel.selectedAssessment['intro_title'],
|
// title: viewModel.selectedAssessment['intro_title'],
|
||||||
subtitle: viewModel.selectedAssessment['intro_subtitle']);
|
// subtitle: viewModel.selectedAssessment['intro_subtitle']);
|
||||||
|
//
|
||||||
Widget _buildDuolingoQuestionScreen(DuolingoViewModel viewModel) =>
|
// Widget _buildDuolingoQuestionScreen(DuolingoViewModel viewModel) =>
|
||||||
_buildQuestionScreen(viewModel);
|
// _buildQuestionScreen(viewModel);
|
||||||
|
//
|
||||||
Widget _buildDuolingoAnswerScreen(DuolingoViewModel viewModel) =>
|
// Widget _buildDuolingoAnswerScreen(DuolingoViewModel viewModel) =>
|
||||||
_buildAnswerScreen(viewModel);
|
// _buildAnswerScreen(viewModel);
|
||||||
|
//
|
||||||
Widget _buildDuolingoReviewScreen(DuolingoViewModel viewModel) =>
|
// Widget _buildDuolingoReviewScreen(DuolingoViewModel viewModel) =>
|
||||||
_buildReviewScreen(viewModel);
|
// _buildReviewScreen(viewModel);
|
||||||
|
//
|
||||||
Widget _buildDuolingoRetakeScreen(DuolingoViewModel viewModel) =>
|
// Widget _buildDuolingoRetakeScreen(DuolingoViewModel viewModel) =>
|
||||||
DuolingoRetakeScreen(
|
// DuolingoRetakeScreen(
|
||||||
title: viewModel.selectedAssessment['outro_title'],
|
// title: viewModel.selectedAssessment['outro_title'],
|
||||||
subtitle: viewModel.selectedAssessment['outro_subtitle']);
|
// subtitle: viewModel.selectedAssessment['outro_subtitle']);
|
||||||
|
//
|
||||||
Widget _buildDuolingoFinishScreen(DuolingoViewModel viewModel) =>
|
// Widget _buildDuolingoFinishScreen(DuolingoViewModel viewModel) =>
|
||||||
DuolingoFinishScreen(
|
// DuolingoFinishScreen(
|
||||||
title: viewModel.selectedAssessment['outro_title'],
|
// title: viewModel.selectedAssessment['outro_title'],
|
||||||
subtitle: viewModel.selectedAssessment['outro_subtitle']);
|
// subtitle: viewModel.selectedAssessment['outro_subtitle']);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,107 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_svg/svg.dart';
|
|
||||||
import 'package:stacked/stacked.dart';
|
|
||||||
|
|
||||||
import '../../../common/app_colors.dart';
|
|
||||||
import '../../../common/ui_helpers.dart';
|
|
||||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
|
||||||
import '../../../widgets/custom_elevated_button.dart';
|
|
||||||
import '../duolingo_viewmodel.dart';
|
|
||||||
|
|
||||||
class DuolingoFinishScreen extends ViewModelWidget<DuolingoViewModel> {
|
|
||||||
final String title;
|
|
||||||
final String subtitle;
|
|
||||||
const DuolingoFinishScreen(
|
|
||||||
{super.key, required this.title, required this.subtitle});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
|
||||||
_buildScaffoldWrapper(viewModel);
|
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
|
||||||
backgroundColor: kcBackgroundColor,
|
|
||||||
body: _buildSafeWrapper(viewModel),
|
|
||||||
);
|
|
||||||
Widget _buildSafeWrapper(DuolingoViewModel viewModel) =>
|
|
||||||
SafeArea(child: _buildScaffold(viewModel));
|
|
||||||
|
|
||||||
Widget _buildScaffold(DuolingoViewModel viewModel) => Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: _buildScaffoldChildren(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
List<Widget> _buildScaffoldChildren(DuolingoViewModel viewModel) => [
|
|
||||||
_buildAppBar(viewModel),
|
|
||||||
verticalSpaceMedium,
|
|
||||||
_buildExpandedBody(viewModel)
|
|
||||||
];
|
|
||||||
|
|
||||||
Widget _buildExpandedBody(DuolingoViewModel viewModel) =>
|
|
||||||
Expanded(child: _buildBodyWrapper(viewModel));
|
|
||||||
|
|
||||||
Widget _buildBodyWrapper(DuolingoViewModel viewModel) => Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
|
||||||
child: _buildBody(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildBody(DuolingoViewModel viewModel) => Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: _buildBodyChildren(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
List<Widget> _buildBodyChildren(DuolingoViewModel viewModel) =>
|
|
||||||
[_buildUpperColumn(viewModel), _buildContinueButtonWrapper(viewModel)];
|
|
||||||
|
|
||||||
Widget _buildUpperColumn(DuolingoViewModel viewModel) => Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
||||||
children: _buildUpperColumnChildren(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
List<Widget> _buildUpperColumnChildren(DuolingoViewModel viewModel) => [
|
|
||||||
verticalSpaceLarge,
|
|
||||||
_buildIcon(),
|
|
||||||
verticalSpaceMedium,
|
|
||||||
_buildTitle(),
|
|
||||||
verticalSpaceSmall,
|
|
||||||
_buildSubtitle(),
|
|
||||||
];
|
|
||||||
|
|
||||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
|
||||||
onClose: () => viewModel.goTo(0),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildIcon() => SvgPicture.asset(
|
|
||||||
'assets/icons/complete.svg',
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildTitle() => Text(
|
|
||||||
title,
|
|
||||||
style: style25DG600,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildSubtitle() => Text(
|
|
||||||
subtitle,
|
|
||||||
style: style14MG400,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 50),
|
|
||||||
child: _buildContinueButton(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildContinueButton(DuolingoViewModel viewModel) =>
|
|
||||||
CustomElevatedButton(
|
|
||||||
height: 55,
|
|
||||||
text: 'Continue',
|
|
||||||
borderRadius: 12,
|
|
||||||
backgroundColor: kcWhite,
|
|
||||||
borderColor: kcPrimaryColor,
|
|
||||||
onTap: () => viewModel.goTo(0),
|
|
||||||
foregroundColor: kcPrimaryColor,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,125 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:iconsax/iconsax.dart';
|
|
||||||
import 'package:stacked/stacked.dart';
|
|
||||||
import 'package:yimaru_app/ui/common/enmus.dart';
|
|
||||||
import 'package:yimaru_app/ui/views/duolingo/duolingo_viewmodel.dart';
|
|
||||||
import 'package:yimaru_app/ui/widgets/duolingo_assessment_app_bar.dart';
|
|
||||||
|
|
||||||
import '../../../common/app_colors.dart';
|
|
||||||
import '../../../common/ui_helpers.dart';
|
|
||||||
import '../../../widgets/custom_elevated_button.dart';
|
|
||||||
import '../../../widgets/wave_wrapper.dart';
|
|
||||||
|
|
||||||
class DuolingoIntroScreen extends ViewModelWidget<DuolingoViewModel> {
|
|
||||||
final String title;
|
|
||||||
final String subtitle;
|
|
||||||
final DuolingoAssessments type;
|
|
||||||
|
|
||||||
const DuolingoIntroScreen(
|
|
||||||
{super.key,
|
|
||||||
required this.type,
|
|
||||||
required this.title,
|
|
||||||
required this.subtitle});
|
|
||||||
|
|
||||||
IconData _getIcon() {
|
|
||||||
if (type == DuolingoAssessments.speaking) {
|
|
||||||
return Icons.waves;
|
|
||||||
} else if (type == DuolingoAssessments.writing) {
|
|
||||||
return Iconsax.pen_add;
|
|
||||||
} else if (type == DuolingoAssessments.listening) {
|
|
||||||
return Icons.hearing;
|
|
||||||
} else {
|
|
||||||
return Icons.book;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
|
||||||
_buildScaffoldWrapper(viewModel);
|
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
|
||||||
backgroundColor: kcBackgroundColor,
|
|
||||||
body: _buildScaffold(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
|
||||||
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
|
||||||
|
|
||||||
Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
|
||||||
child: _buildBodyColumn(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: _buildBodyColumnChildren(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
|
||||||
_buildAppBarWrapper(viewModel),
|
|
||||||
_buildSpeakingIndicatorWrapper(viewModel),
|
|
||||||
_buildContinueButtonWrapper(viewModel)
|
|
||||||
];
|
|
||||||
|
|
||||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
|
||||||
children: [
|
|
||||||
verticalSpaceMedium,
|
|
||||||
_buildAppBar(viewModel),
|
|
||||||
verticalSpaceMedium,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
|
||||||
title: 'Speaking Assessment',
|
|
||||||
onClose: () => viewModel.goTo(0),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildSpeakingIndicatorWrapper(DuolingoViewModel viewModel) => Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
children: _buildSpeakingIndicatorChildren(),
|
|
||||||
);
|
|
||||||
|
|
||||||
List<Widget> _buildSpeakingIndicatorChildren() => [
|
|
||||||
_buildSpeakingIconWrapper(),
|
|
||||||
verticalSpaceMedium,
|
|
||||||
_buildTitle(),
|
|
||||||
_buildSubtitle(),
|
|
||||||
];
|
|
||||||
|
|
||||||
Widget _buildTitle() => Text(
|
|
||||||
title,
|
|
||||||
style: style18DG700,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildSubtitle() => Text(
|
|
||||||
subtitle,
|
|
||||||
style: style14DG400,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildSpeakingIconWrapper() =>
|
|
||||||
WaveWrapper(height: 125, child: _buildSpeakingIcon());
|
|
||||||
|
|
||||||
Widget _buildSpeakingIcon() => Icon(
|
|
||||||
_getIcon(),
|
|
||||||
size: 30,
|
|
||||||
color: kcPrimaryColor,
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 50),
|
|
||||||
child: _buildContinueButton(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildContinueButton(DuolingoViewModel viewModel) =>
|
|
||||||
CustomElevatedButton(
|
|
||||||
height: 55,
|
|
||||||
borderRadius: 12,
|
|
||||||
foregroundColor: kcWhite,
|
|
||||||
text: 'Start Preparation',
|
|
||||||
onTap: () => viewModel.goTo(2),
|
|
||||||
backgroundColor: kcPrimaryColor,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,150 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:stacked/stacked.dart';
|
|
||||||
import 'package:yimaru_app/ui/views/duolingo/duolingo_view.form.dart';
|
|
||||||
import 'package:yimaru_app/ui/widgets/listenable_assessment_card.dart';
|
|
||||||
|
|
||||||
import '../../../common/app_colors.dart';
|
|
||||||
import '../../../common/ui_helpers.dart';
|
|
||||||
import '../../../widgets/custom_elevated_button.dart';
|
|
||||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
|
||||||
import '../duolingo_viewmodel.dart';
|
|
||||||
|
|
||||||
class DuolingoListeningAssessment2Question
|
|
||||||
extends ViewModelWidget<DuolingoViewModel> {
|
|
||||||
final TextEditingController assessmentController;
|
|
||||||
|
|
||||||
const DuolingoListeningAssessment2Question(
|
|
||||||
{super.key, required this.assessmentController});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
|
||||||
_buildScaffoldWrapper(viewModel);
|
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
|
||||||
backgroundColor: kcBackgroundColor,
|
|
||||||
body: _buildScaffold(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
|
||||||
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
|
||||||
|
|
||||||
Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
|
||||||
child: _buildBodyColumn(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: _buildBodyColumnChildren(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
|
||||||
_buildAppBarWrapper(viewModel),
|
|
||||||
_buildQuestionWrapper(viewModel),
|
|
||||||
_buildContinueButtonWrapper(viewModel)
|
|
||||||
];
|
|
||||||
|
|
||||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
|
||||||
children: [
|
|
||||||
verticalSpaceMedium,
|
|
||||||
_buildAppBar(viewModel),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
|
||||||
title: 'Listening Assessment',
|
|
||||||
onClose: () => viewModel.goTo(0),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildQuestionWrapper(DuolingoViewModel viewModel) => Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: _buildQuestionChildren(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
List<Widget> _buildQuestionChildren(DuolingoViewModel viewModel) => [
|
|
||||||
_buildTitle(),
|
|
||||||
verticalSpaceMedium,
|
|
||||||
_buildQuestion(),
|
|
||||||
verticalSpaceLarge,
|
|
||||||
_buildLabel('1. Focus of the conference:'),
|
|
||||||
verticalSpaceTiny,
|
|
||||||
_buildAssessmentFormField(viewModel),
|
|
||||||
if (viewModel.hasAssessmentValidationMessage &&
|
|
||||||
viewModel.focusAssessment)
|
|
||||||
verticalSpaceTiny,
|
|
||||||
if (viewModel.hasAssessmentValidationMessage &&
|
|
||||||
viewModel.focusAssessment)
|
|
||||||
_buildAssessmentWrapper(viewModel),
|
|
||||||
verticalSpaceSmall,
|
|
||||||
_buildLabel('2. Number of presentations:'),
|
|
||||||
verticalSpaceTiny,
|
|
||||||
_buildAssessmentFormField(viewModel),
|
|
||||||
if (viewModel.hasAssessmentValidationMessage &&
|
|
||||||
viewModel.focusAssessment)
|
|
||||||
verticalSpaceTiny,
|
|
||||||
if (viewModel.hasAssessmentValidationMessage &&
|
|
||||||
viewModel.focusAssessment)
|
|
||||||
_buildAssessmentWrapper(viewModel),
|
|
||||||
verticalSpaceSmall,
|
|
||||||
_buildLabel('3. Keynote speaker’s field:'),
|
|
||||||
verticalSpaceTiny,
|
|
||||||
_buildAssessmentFormField(viewModel),
|
|
||||||
if (viewModel.hasAssessmentValidationMessage &&
|
|
||||||
viewModel.focusAssessment)
|
|
||||||
verticalSpaceTiny,
|
|
||||||
if (viewModel.hasAssessmentValidationMessage &&
|
|
||||||
viewModel.focusAssessment)
|
|
||||||
_buildAssessmentWrapper(viewModel),
|
|
||||||
];
|
|
||||||
|
|
||||||
Widget _buildTitle() => Text(
|
|
||||||
'Listen to the audio message and type your response.',
|
|
||||||
style: style18DG700,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildQuestion() => const ListenableAssessmentCard();
|
|
||||||
|
|
||||||
Widget _buildLabel(String question) => Text(
|
|
||||||
question,
|
|
||||||
style: style14MG400,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildAssessmentFormField(DuolingoViewModel viewModel) =>
|
|
||||||
TextFormField(
|
|
||||||
controller: assessmentController,
|
|
||||||
onTap: viewModel.setAssessmentFocus,
|
|
||||||
decoration: inputDecoration(
|
|
||||||
focus: true,
|
|
||||||
hint: 'Start writing here...',
|
|
||||||
filled: assessmentController.text.isNotEmpty),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildAssessmentWrapper(DuolingoViewModel viewModel) =>
|
|
||||||
viewModel.hasAssessmentValidationMessage
|
|
||||||
? _buildAssessmentValidator(viewModel)
|
|
||||||
: Container();
|
|
||||||
|
|
||||||
Widget _buildAssessmentValidator(DuolingoViewModel viewModel) => Text(
|
|
||||||
viewModel.assessmentValidationMessage!,
|
|
||||||
style: style12R700,
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 50),
|
|
||||||
child: _buildContinueButton(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildContinueButton(DuolingoViewModel viewModel) =>
|
|
||||||
CustomElevatedButton(
|
|
||||||
height: 55,
|
|
||||||
text: 'Submit',
|
|
||||||
borderRadius: 12,
|
|
||||||
foregroundColor: kcWhite,
|
|
||||||
onTap: () => viewModel.goTo(4),
|
|
||||||
backgroundColor: kcPrimaryColor,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,128 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_svg/svg.dart';
|
|
||||||
import 'package:stacked/stacked.dart';
|
|
||||||
|
|
||||||
import '../../../common/app_colors.dart';
|
|
||||||
import '../../../common/ui_helpers.dart';
|
|
||||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
|
||||||
import '../../../widgets/custom_elevated_button.dart';
|
|
||||||
import '../duolingo_viewmodel.dart';
|
|
||||||
|
|
||||||
class DuolingoRetakeScreen extends ViewModelWidget<DuolingoViewModel> {
|
|
||||||
final String title;
|
|
||||||
final String subtitle;
|
|
||||||
const DuolingoRetakeScreen(
|
|
||||||
{super.key, required this.title, required this.subtitle});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
|
||||||
_buildScaffoldWrapper(viewModel);
|
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
|
||||||
backgroundColor: kcBackgroundColor,
|
|
||||||
body: _buildSafeWrapper(viewModel),
|
|
||||||
);
|
|
||||||
Widget _buildSafeWrapper(DuolingoViewModel viewModel) =>
|
|
||||||
SafeArea(child: _buildScaffold(viewModel));
|
|
||||||
|
|
||||||
Widget _buildScaffold(DuolingoViewModel viewModel) => Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: _buildScaffoldChildren(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
List<Widget> _buildScaffoldChildren(DuolingoViewModel viewModel) => [
|
|
||||||
_buildAppBar(viewModel),
|
|
||||||
verticalSpaceMedium,
|
|
||||||
_buildExpandedBody(viewModel)
|
|
||||||
];
|
|
||||||
|
|
||||||
Widget _buildExpandedBody(DuolingoViewModel viewModel) =>
|
|
||||||
Expanded(child: _buildBodyWrapper(viewModel));
|
|
||||||
|
|
||||||
Widget _buildBodyWrapper(DuolingoViewModel viewModel) => Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
|
||||||
child: _buildBody(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildBody(DuolingoViewModel viewModel) => Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: _buildBodyChildren(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
List<Widget> _buildBodyChildren(DuolingoViewModel viewModel) =>
|
|
||||||
[_buildUpperColumn(viewModel), _buildLowerColumn(viewModel)];
|
|
||||||
|
|
||||||
Widget _buildUpperColumn(DuolingoViewModel viewModel) => Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
||||||
children: _buildUpperColumnChildren(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
List<Widget> _buildUpperColumnChildren(DuolingoViewModel viewModel) => [
|
|
||||||
verticalSpaceLarge,
|
|
||||||
_buildIcon(),
|
|
||||||
verticalSpaceMedium,
|
|
||||||
_buildTitle(),
|
|
||||||
verticalSpaceSmall,
|
|
||||||
_buildSubtitle(),
|
|
||||||
];
|
|
||||||
|
|
||||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
|
||||||
onClose: () => viewModel.goTo(0),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildIcon() => SvgPicture.asset(
|
|
||||||
'assets/icons/complete.svg',
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildTitle() => Text(
|
|
||||||
title,
|
|
||||||
style: style25DG600,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildSubtitle() => Text(
|
|
||||||
subtitle,
|
|
||||||
style: style14MG400,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildLowerColumn(DuolingoViewModel viewModel) => Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: _buildLowerColumnChildren(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
List<Widget> _buildLowerColumnChildren(DuolingoViewModel viewModel) => [
|
|
||||||
_buildContinueButton(viewModel),
|
|
||||||
verticalSpaceSmall,
|
|
||||||
_buildSkipButtonWrapper(viewModel)
|
|
||||||
];
|
|
||||||
|
|
||||||
Widget _buildContinueButton(DuolingoViewModel viewModel) =>
|
|
||||||
CustomElevatedButton(
|
|
||||||
height: 55,
|
|
||||||
safe: false,
|
|
||||||
borderRadius: 12,
|
|
||||||
text: 'Practice Again',
|
|
||||||
foregroundColor: kcWhite,
|
|
||||||
onTap: () => viewModel.goTo(0),
|
|
||||||
backgroundColor: kcPrimaryColor,
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildSkipButtonWrapper(DuolingoViewModel viewModel) => Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 50),
|
|
||||||
child: _buildSkipButton(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildSkipButton(DuolingoViewModel viewModel) => CustomElevatedButton(
|
|
||||||
height: 55,
|
|
||||||
text: 'Continue',
|
|
||||||
borderRadius: 12,
|
|
||||||
backgroundColor: kcWhite,
|
|
||||||
borderColor: kcPrimaryColor,
|
|
||||||
onTap: () => viewModel.goTo(6),
|
|
||||||
foregroundColor: kcPrimaryColor,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,123 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:stacked/stacked.dart';
|
|
||||||
import 'package:yimaru_app/ui/widgets/countdown_timer.dart';
|
|
||||||
import 'package:yimaru_app/ui/widgets/speaking_indicator.dart';
|
|
||||||
import 'package:yimaru_app/ui/widgets/speaking_label.dart';
|
|
||||||
|
|
||||||
import '../../../common/app_colors.dart';
|
|
||||||
import '../../../common/ui_helpers.dart';
|
|
||||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
|
||||||
import '../../../widgets/custom_elevated_button.dart';
|
|
||||||
import '../duolingo_viewmodel.dart';
|
|
||||||
|
|
||||||
class DuolingoSpeakingAssessment1Answer
|
|
||||||
extends ViewModelWidget<DuolingoViewModel> {
|
|
||||||
const DuolingoSpeakingAssessment1Answer({
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
|
||||||
_buildScaffoldWrapper(viewModel);
|
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
|
||||||
backgroundColor: kcBackgroundColor,
|
|
||||||
body: _buildScaffold(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
|
||||||
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
|
||||||
|
|
||||||
Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
|
||||||
child: _buildBodyColumn(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: _buildBodyColumnChildren(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
|
||||||
_buildAppBarWrapper(viewModel),
|
|
||||||
_buildSpeakingIndicatorWrapper(viewModel),
|
|
||||||
_buildContinueButtonWrapper(viewModel)
|
|
||||||
];
|
|
||||||
|
|
||||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
|
||||||
children: [
|
|
||||||
verticalSpaceMedium,
|
|
||||||
_buildAppBar(viewModel),
|
|
||||||
verticalSpaceSmall,
|
|
||||||
_buildCountdownWrapper(),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
|
||||||
title: 'Speaking Assessment',
|
|
||||||
onClose: () => viewModel.goTo(0),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildSpeakingIndicatorWrapper(DuolingoViewModel viewModel) => Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
children: _buildSpeakingIndicatorChildren(),
|
|
||||||
);
|
|
||||||
|
|
||||||
List<Widget> _buildSpeakingIndicatorChildren() => [
|
|
||||||
_buildTitle(),
|
|
||||||
verticalSpaceMedium,
|
|
||||||
_buildImageContainer(),
|
|
||||||
verticalSpaceSmall,
|
|
||||||
_buildSpeakerLabel(),
|
|
||||||
verticalSpaceSmall,
|
|
||||||
_buildSpeakingIndicator()
|
|
||||||
];
|
|
||||||
|
|
||||||
Widget _buildCountdownWrapper() =>
|
|
||||||
const Align(alignment: Alignment.centerRight, child: CountdownTimer());
|
|
||||||
|
|
||||||
Widget _buildTitle() => Text(
|
|
||||||
'Speak about the photo below',
|
|
||||||
style: style18DG700,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildImageContainer() => SizedBox(
|
|
||||||
width: 250,
|
|
||||||
height: 300,
|
|
||||||
child: _buildImageWrapper(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildImageWrapper() => ClipRRect(
|
|
||||||
borderRadius: BorderRadius.circular(5),
|
|
||||||
child: _buildImage(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildImage() => Image.asset(
|
|
||||||
'assets/images/image_1.png',
|
|
||||||
fit: BoxFit.fill,
|
|
||||||
width: double.maxFinite,
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildSpeakerLabel() => const SpeakingLabel();
|
|
||||||
|
|
||||||
Widget _buildSpeakingIndicator() => const SpeakingIndicator(
|
|
||||||
size: 25,
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 50),
|
|
||||||
child: _buildContinueButton(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildContinueButton(DuolingoViewModel viewModel) =>
|
|
||||||
CustomElevatedButton(
|
|
||||||
height: 55,
|
|
||||||
text: 'Submit',
|
|
||||||
borderRadius: 12,
|
|
||||||
foregroundColor: kcWhite,
|
|
||||||
onTap: () => viewModel.goTo(4),
|
|
||||||
backgroundColor: kcPrimaryColor,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,142 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:stacked/stacked.dart';
|
|
||||||
|
|
||||||
import '../../../common/app_colors.dart';
|
|
||||||
import '../../../common/ui_helpers.dart';
|
|
||||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
|
||||||
import '../../../widgets/custom_elevated_button.dart';
|
|
||||||
import '../duolingo_viewmodel.dart';
|
|
||||||
|
|
||||||
class DuolingoSpeakingAssessment1Question
|
|
||||||
extends ViewModelWidget<DuolingoViewModel> {
|
|
||||||
const DuolingoSpeakingAssessment1Question({
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
|
||||||
_buildScaffoldWrapper(viewModel);
|
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
|
||||||
backgroundColor: kcBackgroundColor,
|
|
||||||
body: _buildScaffold(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
|
||||||
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
|
||||||
|
|
||||||
Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
|
||||||
child: _buildBodyColumn(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: _buildBodyColumnChildren(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
|
||||||
_buildAppBarWrapper(viewModel),
|
|
||||||
_buildSpeakingIndicatorWrapper(viewModel),
|
|
||||||
_buildContinueButtonWrapper(viewModel)
|
|
||||||
];
|
|
||||||
|
|
||||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
|
||||||
children: [
|
|
||||||
verticalSpaceMedium,
|
|
||||||
_buildAppBar(viewModel),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
|
||||||
title: 'Speaking Assessment',
|
|
||||||
onClose: () => viewModel.goTo(0),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildSpeakingIndicatorWrapper(DuolingoViewModel viewModel) => Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
children: _buildSpeakingIndicatorChildren(),
|
|
||||||
);
|
|
||||||
|
|
||||||
List<Widget> _buildSpeakingIndicatorChildren() => [
|
|
||||||
_buildTitle(),
|
|
||||||
verticalSpaceMedium,
|
|
||||||
_buildTimerStackWrapper(),
|
|
||||||
verticalSpaceSmall,
|
|
||||||
_buildSubtitle(),
|
|
||||||
verticalSpaceSmall,
|
|
||||||
_buildImageContainer(),
|
|
||||||
];
|
|
||||||
|
|
||||||
Widget _buildTitle() => Text(
|
|
||||||
'Prepare to speak about the photo',
|
|
||||||
style: style18DG700,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildTimerStackWrapper() => SizedBox(
|
|
||||||
height: 50,
|
|
||||||
child: _buildTimerStack(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildTimerStack() => Stack(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
children: _buildTimerStackChildren(),
|
|
||||||
);
|
|
||||||
|
|
||||||
List<Widget> _buildTimerStackChildren() =>
|
|
||||||
[_buildTimer(), _buildCountdownTime()];
|
|
||||||
|
|
||||||
Widget _buildTimer() => const CircularProgressIndicator(
|
|
||||||
value: 1.0,
|
|
||||||
strokeWidth: 5,
|
|
||||||
color: kcPrimaryColor,
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
constraints: BoxConstraints(minWidth: 50, minHeight: 50),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildCountdownTime() => Text(
|
|
||||||
'0:20',
|
|
||||||
style: style16P600,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildSubtitle() => Text(
|
|
||||||
'Prep time',
|
|
||||||
style: style16DG400,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildImageContainer() => SizedBox(
|
|
||||||
width: 250,
|
|
||||||
height: 300,
|
|
||||||
child: _buildImageWrapper(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildImageWrapper() => ClipRRect(
|
|
||||||
borderRadius: BorderRadius.circular(5),
|
|
||||||
child: _buildImage(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildImage() => Image.asset(
|
|
||||||
'assets/images/image_1.png',
|
|
||||||
fit: BoxFit.fill,
|
|
||||||
width: double.maxFinite,
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 50),
|
|
||||||
child: _buildContinueButton(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildContinueButton(DuolingoViewModel viewModel) =>
|
|
||||||
CustomElevatedButton(
|
|
||||||
height: 55,
|
|
||||||
borderRadius: 12,
|
|
||||||
text: 'Start Speaking',
|
|
||||||
foregroundColor: kcWhite,
|
|
||||||
onTap: () => viewModel.goTo(3),
|
|
||||||
backgroundColor: kcPrimaryColor,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,104 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:stacked/stacked.dart';
|
|
||||||
import 'package:yimaru_app/ui/widgets/countdown_timer.dart';
|
|
||||||
|
|
||||||
import '../../../common/app_colors.dart';
|
|
||||||
import '../../../common/ui_helpers.dart';
|
|
||||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
|
||||||
import '../../../widgets/speaking_assessment_review_section.dart';
|
|
||||||
import '../duolingo_viewmodel.dart';
|
|
||||||
|
|
||||||
class DuolingoSpeakingAssessment1Review
|
|
||||||
extends ViewModelWidget<DuolingoViewModel> {
|
|
||||||
const DuolingoSpeakingAssessment1Review({
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
|
||||||
_buildScaffoldWrapper(viewModel);
|
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
|
||||||
backgroundColor: kcBackgroundColor,
|
|
||||||
body: _buildScaffold(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
|
||||||
SafeArea(child: _buildBodyColumn(viewModel));
|
|
||||||
|
|
||||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: _buildBodyColumnChildren(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
|
||||||
_buildAppBarIndenter(viewModel),
|
|
||||||
_buildImageSectionIndenter(viewModel),
|
|
||||||
_buildAssessmentReviewSection(viewModel)
|
|
||||||
];
|
|
||||||
|
|
||||||
Widget _buildAppBarIndenter(DuolingoViewModel viewModel) => Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
|
||||||
child: _buildAppBarWrapper(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
|
||||||
children: [
|
|
||||||
verticalSpaceMedium,
|
|
||||||
_buildAppBar(viewModel),
|
|
||||||
verticalSpaceSmall,
|
|
||||||
_buildCountdownWrapper(),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
|
||||||
title: 'Feedback',
|
|
||||||
onClose: () => viewModel.goTo(0),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildImageSectionIndenter(DuolingoViewModel viewModel) => Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
|
||||||
child: _buildImageSectionWrapper(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildImageSectionWrapper(DuolingoViewModel viewModel) => Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
children: _buildImageSectionChildren(),
|
|
||||||
);
|
|
||||||
|
|
||||||
List<Widget> _buildImageSectionChildren() => [
|
|
||||||
_buildTitle(),
|
|
||||||
verticalSpaceMedium,
|
|
||||||
_buildImageContainer(),
|
|
||||||
];
|
|
||||||
|
|
||||||
Widget _buildCountdownWrapper() =>
|
|
||||||
const Align(alignment: Alignment.centerRight, child: CountdownTimer());
|
|
||||||
|
|
||||||
Widget _buildTitle() => Text(
|
|
||||||
'Speak about the photo below',
|
|
||||||
style: style18DG700,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildImageContainer() => SizedBox(
|
|
||||||
width: 150,
|
|
||||||
height: 200,
|
|
||||||
child: _buildImageWrapper(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildImageWrapper() => ClipRRect(
|
|
||||||
borderRadius: BorderRadius.circular(5),
|
|
||||||
child: _buildImage(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildImage() => Image.asset(
|
|
||||||
'assets/images/image_1.png',
|
|
||||||
fit: BoxFit.fill,
|
|
||||||
width: double.maxFinite,
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildAssessmentReviewSection(DuolingoViewModel viewModel) =>
|
|
||||||
SpeakingAssessmentReviewSection(onTap: () => viewModel.goTo(5));
|
|
||||||
}
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:stacked/stacked.dart';
|
|
||||||
|
|
||||||
import '../../../common/app_colors.dart';
|
|
||||||
import '../../../common/ui_helpers.dart';
|
|
||||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
|
||||||
import '../../../widgets/speaking_assessment_review_section.dart';
|
|
||||||
import '../../../widgets/duolingo_assessment_question_card.dart';
|
|
||||||
import '../duolingo_viewmodel.dart';
|
|
||||||
|
|
||||||
class DuolingoSpeakingAssessment3Review
|
|
||||||
extends ViewModelWidget<DuolingoViewModel> {
|
|
||||||
const DuolingoSpeakingAssessment3Review({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
|
||||||
_buildScaffoldWrapper(viewModel);
|
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
|
||||||
backgroundColor: kcBackgroundColor,
|
|
||||||
body: _buildScaffold(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
|
||||||
SafeArea(child: _buildBodyColumn(viewModel));
|
|
||||||
|
|
||||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: _buildBodyColumnChildren(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
|
||||||
_buildAppBarIndenter(viewModel),
|
|
||||||
_buildQuestionIndenter(viewModel),
|
|
||||||
_buildAssessmentReviewSection(viewModel)
|
|
||||||
];
|
|
||||||
|
|
||||||
Widget _buildAppBarIndenter(DuolingoViewModel viewModel) => Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
|
||||||
child: _buildAppBarWrapper(viewModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
|
||||||
children: [
|
|
||||||
verticalSpaceMedium,
|
|
||||||
_buildAppBar(viewModel),
|
|
||||||
verticalSpaceSmall,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
|
||||||
title: 'Feedback',
|
|
||||||
onClose: () => viewModel.goTo(0),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildQuestionIndenter(DuolingoViewModel viewModel) => Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
|
||||||
child: _buildQuestion(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildQuestion() => const DuolingoAssessmentQuestionCard(
|
|
||||||
subtitle:
|
|
||||||
'What values of beliefs did you learn from living in your hometown? How will your hometown influence your future?',
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildAssessmentReviewSection(DuolingoViewModel viewModel) =>
|
|
||||||
SpeakingAssessmentReviewSection(onTap: () => viewModel.goTo(5));
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +1,11 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_carousel_widget/flutter_carousel_widget.dart';
|
import 'package:flutter_carousel_widget/flutter_carousel_widget.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
import 'package:yimaru_app/ui/views/failure/screens/first_failure_screen.dart';
|
import 'package:yimaru_app/ui/views/failure/screens/first_failure_screen.dart';
|
||||||
import 'package:yimaru_app/ui/views/failure/screens/second_failure_screen.dart';
|
import 'package:yimaru_app/ui/views/failure/screens/second_failure_screen.dart';
|
||||||
import 'package:yimaru_app/ui/views/failure/screens/third_failure_screen.dart';
|
import 'package:yimaru_app/ui/views/failure/screens/third_failure_screen.dart';
|
||||||
|
|
||||||
import '../../common/app_colors.dart';
|
import '../../common/app_colors.dart';
|
||||||
import '../../common/ui_helpers.dart';
|
|
||||||
import '../../widgets/custom_circular_progress_indicator.dart';
|
|
||||||
import 'failure_viewmodel.dart';
|
import 'failure_viewmodel.dart';
|
||||||
|
|
||||||
class FailureView extends StackedView<FailureViewModel> {
|
class FailureView extends StackedView<FailureViewModel> {
|
||||||
|
|
|
||||||
|
|
@ -1,43 +1,40 @@
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
import 'package:yimaru_app/ui/views/failure/failure_viewmodel.dart';
|
import 'package:yimaru_app/ui/views/failure/failure_viewmodel.dart';
|
||||||
import 'package:yimaru_app/ui/views/startup/startup_viewmodel.dart';
|
|
||||||
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
|
||||||
|
|
||||||
import '../../../common/translations/locale_keys.g.dart';
|
|
||||||
import '../../../widgets/custom_circular_progress_indicator.dart';
|
import '../../../widgets/custom_circular_progress_indicator.dart';
|
||||||
|
|
||||||
class FirstFailureScreen extends ViewModelWidget<FailureViewModel> {
|
class FirstFailureScreen extends ViewModelWidget<FailureViewModel> {
|
||||||
final String label;
|
final String label;
|
||||||
final GestureTapCallback onTap;
|
final GestureTapCallback onTap;
|
||||||
|
|
||||||
const FirstFailureScreen({super.key,required this.onTap,required this.label});
|
const FirstFailureScreen(
|
||||||
|
{super.key, required this.onTap, required this.label});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, FailureViewModel viewModel) =>
|
Widget build(BuildContext context, FailureViewModel viewModel) =>
|
||||||
_buildScaffoldWrapper();
|
_buildScaffoldWrapper();
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper( ) => Scaffold(
|
Widget _buildScaffoldWrapper() => Scaffold(
|
||||||
backgroundColor: kcPrimaryColor,
|
backgroundColor: kcPrimaryColor,
|
||||||
body: _buildScaffoldPadding(),
|
body: _buildScaffoldPadding(),
|
||||||
);
|
);
|
||||||
Widget _buildScaffoldPadding( ) => Padding(
|
Widget _buildScaffoldPadding() => Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildScaffold(),
|
child: _buildScaffold(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildScaffold( ) => Column(
|
Widget _buildScaffold() => Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: _buildScaffoldChildren(),
|
children: _buildScaffoldChildren(),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildScaffoldChildren( ) =>
|
List<Widget> _buildScaffoldChildren() =>
|
||||||
[_buildUpperColumn(), _buildLowerColumnWrapper()];
|
[_buildUpperColumn(), _buildLowerColumnWrapper()];
|
||||||
|
|
||||||
Widget _buildUpperColumn() => Column(
|
Widget _buildUpperColumn() => Column(
|
||||||
|
|
@ -59,17 +56,17 @@ class FirstFailureScreen extends ViewModelWidget<FailureViewModel> {
|
||||||
height: 25,
|
height: 25,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildLowerColumnWrapper( ) => Expanded(
|
Widget _buildLowerColumnWrapper() => Expanded(
|
||||||
child: _buildLowerColumn(),
|
child: _buildLowerColumn(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildLowerColumn( ) => Column(
|
Widget _buildLowerColumn() => Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: _buildLowerColumnChildren(),
|
children: _buildLowerColumnChildren(),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildLowerColumnChildren( ) => [
|
List<Widget> _buildLowerColumnChildren() => [
|
||||||
_buildTitle(),
|
_buildTitle(),
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildImageWrapper(),
|
_buildImageWrapper(),
|
||||||
|
|
@ -110,56 +107,51 @@ class FirstFailureScreen extends ViewModelWidget<FailureViewModel> {
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildSafeWrapper( ) =>
|
Widget _buildSafeWrapper() => SafeArea(child: _buildContinueButtonWrapper());
|
||||||
SafeArea(child: _buildContinueButtonWrapper());
|
|
||||||
|
|
||||||
Widget _buildContinueButtonWrapper( ) => Align(
|
Widget _buildContinueButtonWrapper() => Align(
|
||||||
alignment: Alignment.bottomCenter,
|
alignment: Alignment.bottomCenter,
|
||||||
child: _buildLoadingTextContainer(),
|
child: _buildLoadingTextContainer(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Widget _buildLoadingTextContainer() => Padding(
|
Widget _buildLoadingTextContainer() => Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 50),
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
child: _buildLoadingTextWrapper(),
|
child: _buildLoadingTextWrapper(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildLoadingTextWrapper() => Row(
|
Widget _buildLoadingTextWrapper() => Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: _buildLoadingTextChildren(),
|
children: _buildLoadingTextChildren(),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildLoadingTextChildren() => [
|
List<Widget> _buildLoadingTextChildren() => [
|
||||||
_buildLoadingText(),
|
_buildLoadingText(),
|
||||||
horizontalSpaceSmall,
|
horizontalSpaceSmall,
|
||||||
_buildIndicatorWrapper(),
|
_buildIndicatorWrapper(),
|
||||||
horizontalSpaceSmall,
|
horizontalSpaceSmall,
|
||||||
_buildRetryButtonWrapper()
|
_buildRetryButtonWrapper()
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildLoadingText() =>
|
Widget _buildLoadingText() => Text('$label...', style: style16W600);
|
||||||
Text('$label...', style: style16W600);
|
|
||||||
|
|
||||||
Widget _buildIndicatorWrapper() => SizedBox(
|
Widget _buildIndicatorWrapper() => SizedBox(
|
||||||
width: 16,
|
width: 16,
|
||||||
height: 16,
|
height: 16,
|
||||||
child: _buildIndicator(),
|
child: _buildIndicator(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildIndicator() =>
|
Widget _buildIndicator() =>
|
||||||
const CustomCircularProgressIndicator(color: kcWhite);
|
const CustomCircularProgressIndicator(color: kcWhite);
|
||||||
|
|
||||||
|
|
||||||
Widget _buildRetryButtonWrapper() => GestureDetector(
|
Widget _buildRetryButtonWrapper() => GestureDetector(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: _buildRetryButton(),
|
child: _buildRetryButton(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildRetryButton() => Text(
|
Widget _buildRetryButton() => Text(
|
||||||
'Retry',
|
'Retry',
|
||||||
style: style16W600.copyWith(
|
style: style16W600.copyWith(
|
||||||
fontStyle: FontStyle.italic, decoration: TextDecoration.underline),
|
fontStyle: FontStyle.italic, decoration: TextDecoration.underline),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,10 @@
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
import 'package:yimaru_app/ui/views/failure/failure_viewmodel.dart';
|
import 'package:yimaru_app/ui/views/failure/failure_viewmodel.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
|
||||||
|
|
||||||
import '../../../common/translations/locale_keys.g.dart';
|
|
||||||
import '../../../widgets/custom_circular_progress_indicator.dart';
|
import '../../../widgets/custom_circular_progress_indicator.dart';
|
||||||
|
|
||||||
class SecondFailureScreen extends ViewModelWidget<FailureViewModel> {
|
class SecondFailureScreen extends ViewModelWidget<FailureViewModel> {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,10 @@
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
import 'package:yimaru_app/ui/views/failure/failure_viewmodel.dart';
|
import 'package:yimaru_app/ui/views/failure/failure_viewmodel.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
|
||||||
|
|
||||||
import '../../../common/translations/locale_keys.g.dart';
|
|
||||||
import '../../../widgets/custom_circular_progress_indicator.dart';
|
import '../../../widgets/custom_circular_progress_indicator.dart';
|
||||||
|
|
||||||
class ThirdFailureScreen extends ViewModelWidget<FailureViewModel> {
|
class ThirdFailureScreen extends ViewModelWidget<FailureViewModel> {
|
||||||
|
|
@ -21,145 +18,140 @@ class ThirdFailureScreen extends ViewModelWidget<FailureViewModel> {
|
||||||
Widget build(BuildContext context, FailureViewModel viewModel) =>
|
Widget build(BuildContext context, FailureViewModel viewModel) =>
|
||||||
_buildScaffoldWrapper();
|
_buildScaffoldWrapper();
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper( ) => Scaffold(
|
Widget _buildScaffoldWrapper() => Scaffold(
|
||||||
backgroundColor:kcWhite,
|
backgroundColor: kcWhite,
|
||||||
body: _buildScaffoldPadding(),
|
body: _buildScaffoldPadding(),
|
||||||
);
|
);
|
||||||
Widget _buildScaffoldPadding( ) => Padding(
|
Widget _buildScaffoldPadding() => Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildScaffold(),
|
child: _buildScaffold(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildScaffold( ) => Column(
|
Widget _buildScaffold() => Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: _buildScaffoldChildren(),
|
children: _buildScaffoldChildren(),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildScaffoldChildren( ) =>
|
List<Widget> _buildScaffoldChildren() =>
|
||||||
[_buildUpperColumn(), _buildLowerColumnWrapper()];
|
[_buildUpperColumn(), _buildLowerColumnWrapper()];
|
||||||
|
|
||||||
Widget _buildUpperColumn() => Column(
|
Widget _buildUpperColumn() => Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: _buildUpperColumnChildren(),
|
children: _buildUpperColumnChildren(),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildUpperColumnChildren() =>
|
List<Widget> _buildUpperColumnChildren() =>
|
||||||
[verticalSpaceLarge, _buildIconWrapper(), verticalSpaceLarge];
|
[verticalSpaceLarge, _buildIconWrapper(), verticalSpaceLarge];
|
||||||
|
|
||||||
Widget _buildIconWrapper() => Align(
|
Widget _buildIconWrapper() => Align(
|
||||||
alignment: Alignment.topLeft,
|
alignment: Alignment.topLeft,
|
||||||
child: _buildIcon(),
|
child: _buildIcon(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildIcon() => SvgPicture.asset(
|
Widget _buildIcon() => SvgPicture.asset(
|
||||||
'assets/icons/logo_purple.svg',
|
'assets/icons/logo_purple.svg',
|
||||||
height: 25,
|
height: 25,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildLowerColumnWrapper( ) => Expanded(
|
Widget _buildLowerColumnWrapper() => Expanded(
|
||||||
child: _buildLowerColumn(),
|
child: _buildLowerColumn(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildLowerColumn( ) => Column(
|
Widget _buildLowerColumn() => Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: _buildLowerColumnChildren(),
|
children: _buildLowerColumnChildren(),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildLowerColumnChildren( ) => [
|
|
||||||
_buildTitle(),
|
|
||||||
verticalSpaceMedium,
|
|
||||||
_buildImageWrapper(),
|
|
||||||
verticalSpaceMedium,
|
|
||||||
_buildSafeWrapper()
|
|
||||||
];
|
|
||||||
|
|
||||||
|
List<Widget> _buildLowerColumnChildren() => [
|
||||||
|
_buildTitle(),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildImageWrapper(),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildSafeWrapper()
|
||||||
|
];
|
||||||
|
|
||||||
Widget _buildTitle() => Text.rich(
|
Widget _buildTitle() => Text.rich(
|
||||||
TextSpan(
|
|
||||||
text: 'እንግሊዝኛ\n',
|
|
||||||
style: style25P600,
|
|
||||||
children: [
|
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: 'በማንኛውም',
|
text: 'እንግሊዝኛ\n',
|
||||||
style: style25P400,
|
|
||||||
),
|
|
||||||
TextSpan(
|
|
||||||
text: ' ቦታ ',
|
|
||||||
style: style25P600,
|
style: style25P600,
|
||||||
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text: 'በማንኛውም',
|
||||||
|
style: style25P400,
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: ' ቦታ ',
|
||||||
|
style: style25P600,
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: 'ይማሩ!',
|
||||||
|
style: style25P400,
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
TextSpan(
|
);
|
||||||
text: 'ይማሩ!',
|
|
||||||
style: style25P400,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildImageWrapper() => Expanded(child: _buildImageClipper());
|
Widget _buildImageWrapper() => Expanded(child: _buildImageClipper());
|
||||||
|
|
||||||
Widget _buildImageClipper() => ClipRRect(
|
Widget _buildImageClipper() => ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(25),
|
borderRadius: BorderRadius.circular(25),
|
||||||
child: _buildImage(),
|
child: _buildImage(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildImage() => Image.asset(
|
Widget _buildImage() => Image.asset(
|
||||||
'assets/images/landing_3.png',
|
'assets/images/landing_3.png',
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildSafeWrapper( ) =>
|
|
||||||
SafeArea(child: _buildContinueButtonWrapper());
|
|
||||||
|
|
||||||
Widget _buildContinueButtonWrapper( ) => Align(
|
|
||||||
alignment: Alignment.bottomCenter,
|
|
||||||
child: _buildLoadingTextContainer(),
|
|
||||||
);
|
|
||||||
|
|
||||||
|
Widget _buildSafeWrapper() => SafeArea(child: _buildContinueButtonWrapper());
|
||||||
|
|
||||||
|
Widget _buildContinueButtonWrapper() => Align(
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
child: _buildLoadingTextContainer(),
|
||||||
|
);
|
||||||
|
|
||||||
Widget _buildLoadingTextContainer() => Padding(
|
Widget _buildLoadingTextContainer() => Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 50),
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
child: _buildLoadingTextWrapper(),
|
child: _buildLoadingTextWrapper(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildLoadingTextWrapper() => Row(
|
Widget _buildLoadingTextWrapper() => Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: _buildLoadingTextChildren(),
|
children: _buildLoadingTextChildren(),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildLoadingTextChildren() => [
|
List<Widget> _buildLoadingTextChildren() => [
|
||||||
_buildLoadingText(),
|
_buildLoadingText(),
|
||||||
horizontalSpaceSmall,
|
horizontalSpaceSmall,
|
||||||
_buildIndicatorWrapper(),
|
_buildIndicatorWrapper(),
|
||||||
horizontalSpaceSmall,_buildRetryButtonWrapper()
|
horizontalSpaceSmall,
|
||||||
];
|
_buildRetryButtonWrapper()
|
||||||
|
];
|
||||||
|
|
||||||
Widget _buildLoadingText() =>
|
Widget _buildLoadingText() => Text('$label...', style: style16P600);
|
||||||
Text('$label...', style: style16P600);
|
|
||||||
|
|
||||||
Widget _buildIndicatorWrapper() => SizedBox(
|
Widget _buildIndicatorWrapper() => SizedBox(
|
||||||
width: 16,
|
width: 16,
|
||||||
height: 16,
|
height: 16,
|
||||||
child: _buildIndicator(),
|
child: _buildIndicator(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildIndicator() =>
|
Widget _buildIndicator() =>
|
||||||
const CustomCircularProgressIndicator(color: kcPrimaryColor);
|
const CustomCircularProgressIndicator(color: kcPrimaryColor);
|
||||||
|
|
||||||
Widget _buildRetryButtonWrapper() => GestureDetector(
|
Widget _buildRetryButtonWrapper() => GestureDetector(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: _buildRetryButton(),
|
child: _buildRetryButton(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildRetryButton() => Text(
|
Widget _buildRetryButton() => Text(
|
||||||
'Retry',
|
'Retry',
|
||||||
style: style16P600.copyWith(
|
style: style16P600.copyWith(
|
||||||
fontStyle: FontStyle.italic, decoration: TextDecoration.underline),
|
fontStyle: FontStyle.italic, decoration: TextDecoration.underline),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,10 @@ import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
import 'package:yimaru_app/ui/common/enmus.dart';
|
import 'package:yimaru_app/ui/common/enmus.dart';
|
||||||
import 'package:yimaru_app/ui/common/translations/locale_keys.g.dart';
|
import 'package:yimaru_app/ui/common/translations/locale_keys.g.dart';
|
||||||
import 'package:yimaru_app/ui/views/course/course_view.dart';
|
import 'package:yimaru_app/ui/views/course/course_view.dart';
|
||||||
import 'package:yimaru_app/ui/views/course_catalog/course_catalog_view.dart';
|
|
||||||
import 'package:yimaru_app/ui/views/learn_program/learn_program_view.dart';
|
import 'package:yimaru_app/ui/views/learn_program/learn_program_view.dart';
|
||||||
import 'package:yimaru_app/ui/views/profile/profile_view.dart';
|
import 'package:yimaru_app/ui/views/profile/profile_view.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/page_loading_indicator.dart';
|
import 'package:yimaru_app/ui/widgets/page_loading_indicator.dart';
|
||||||
|
|
||||||
import '../../widgets/coming_soon.dart';
|
|
||||||
import 'home_viewmodel.dart';
|
import 'home_viewmodel.dart';
|
||||||
|
|
||||||
class HomeView extends StackedView<HomeViewModel> {
|
class HomeView extends StackedView<HomeViewModel> {
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ class HomeViewModel extends ReactiveViewModel {
|
||||||
// Dependency injection
|
// Dependency injection
|
||||||
final _statusChecker = locator<StatusCheckerService>();
|
final _statusChecker = locator<StatusCheckerService>();
|
||||||
|
|
||||||
|
|
||||||
final _bottomSheetService = locator<BottomSheetService>();
|
final _bottomSheetService = locator<BottomSheetService>();
|
||||||
|
|
||||||
final _inAppUpdateService = locator<InAppUpdateService>();
|
final _inAppUpdateService = locator<InAppUpdateService>();
|
||||||
|
|
@ -26,7 +25,7 @@ class HomeViewModel extends ReactiveViewModel {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<ListenableServiceMixin> get listenableServices =>
|
List<ListenableServiceMixin> get listenableServices =>
|
||||||
[_authenticationService,_inAppNotificationService];
|
[_authenticationService, _inAppNotificationService];
|
||||||
|
|
||||||
// Current user
|
// Current user
|
||||||
User? get _user => _authenticationService.user;
|
User? get _user => _authenticationService.user;
|
||||||
|
|
@ -66,7 +65,6 @@ class HomeViewModel extends ReactiveViewModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Unread notifications
|
// Unread notifications
|
||||||
Future<void> getUnreadNotifications() async {
|
Future<void> getUnreadNotifications() async {
|
||||||
if (await _statusChecker.checkConnection()) {
|
if (await _statusChecker.checkConnection()) {
|
||||||
|
|
|
||||||
|
|
@ -53,5 +53,4 @@ class LandingView extends StackedView<LandingViewModel> {
|
||||||
Widget _buildSecondLanding() => const SecondLandingScreen();
|
Widget _buildSecondLanding() => const SecondLandingScreen();
|
||||||
|
|
||||||
Widget _buildThirdLanding() => const ThirdLandingScreen();
|
Widget _buildThirdLanding() => const ThirdLandingScreen();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ class LearnModuleView extends StackedView<LearnModuleViewModel> {
|
||||||
{required BuildContext context,
|
{required BuildContext context,
|
||||||
required LearnModule module,
|
required LearnModule module,
|
||||||
required LearnModuleViewModel viewModel}) async {
|
required LearnModuleViewModel viewModel}) async {
|
||||||
if (module.access?.isCompleted ?? false ) {
|
if (module.access?.isCompleted ?? false) {
|
||||||
await viewModel.navigateToLearnPractice(
|
await viewModel.navigateToLearnPractice(
|
||||||
id: module.id ?? 0, module: module.name ?? '');
|
id: module.id ?? 0, module: module.name ?? '');
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ 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/common/enmus.dart';
|
||||||
import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_finish_screen.dart';
|
import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_finish_screen.dart';
|
||||||
import 'package:yimaru_app/ui/views/learn_practice/screens/learn_loading_screen.dart';
|
import 'package:yimaru_app/ui/widgets/practice_loading_screen.dart';
|
||||||
import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_appreciation_screen.dart';
|
import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_appreciation_screen.dart';
|
||||||
import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_completion_screen.dart';
|
import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_completion_screen.dart';
|
||||||
import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_description_screen.dart';
|
import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_description_screen.dart';
|
||||||
|
|
@ -12,7 +12,7 @@ import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_intro_
|
||||||
import 'package:yimaru_app/ui/widgets/page_loading_indicator.dart';
|
import 'package:yimaru_app/ui/widgets/page_loading_indicator.dart';
|
||||||
|
|
||||||
import '../../common/app_colors.dart';
|
import '../../common/app_colors.dart';
|
||||||
import '../../widgets/cancel_learn_practice_sheet.dart';
|
import '../../widgets/cancel_practice_sheet.dart';
|
||||||
import 'learn_practice_viewmodel.dart';
|
import 'learn_practice_viewmodel.dart';
|
||||||
|
|
||||||
class LearnPracticeView extends StackedView<LearnPracticeViewModel> {
|
class LearnPracticeView extends StackedView<LearnPracticeViewModel> {
|
||||||
|
|
@ -80,8 +80,7 @@ class LearnPracticeView extends StackedView<LearnPracticeViewModel> {
|
||||||
},
|
},
|
||||||
child: _buildScaffoldWrapper(viewModel));
|
child: _buildScaffoldWrapper(viewModel));
|
||||||
|
|
||||||
Widget _buildSheet(LearnPracticeViewModel viewModel) =>
|
Widget _buildSheet(LearnPracticeViewModel viewModel) => CancelPracticeSheet(
|
||||||
CancelLearnPracticeSheet(
|
|
||||||
onClose: viewModel.pop,
|
onClose: viewModel.pop,
|
||||||
onContinue: viewModel.pop,
|
onContinue: viewModel.pop,
|
||||||
user: viewModel.user?.firstName ?? '',
|
user: viewModel.user?.firstName ?? '',
|
||||||
|
|
@ -101,7 +100,7 @@ class LearnPracticeView extends StackedView<LearnPracticeViewModel> {
|
||||||
: _buildBody(viewModel);
|
: _buildBody(viewModel);
|
||||||
|
|
||||||
Widget _buildPageLoadingIndicator(LearnPracticeViewModel viewModel) =>
|
Widget _buildPageLoadingIndicator(LearnPracticeViewModel viewModel) =>
|
||||||
LearnLoadingScreen(
|
PracticeLoadingScreen(
|
||||||
isLoading: viewModel.busy(StateObjects.learnPractice),
|
isLoading: viewModel.busy(StateObjects.learnPractice),
|
||||||
onTap: () async =>
|
onTap: () async =>
|
||||||
await viewModel.getLearnPractices(id: id, practice: practice),
|
await viewModel.getLearnPractices(id: id, practice: practice),
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import 'package:flutter_spinkit/flutter_spinkit.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
import 'package:waveform_recorder/waveform_recorder.dart';
|
import 'package:waveform_recorder/waveform_recorder.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/cancel_learn_practice_sheet.dart';
|
import 'package:yimaru_app/ui/widgets/cancel_practice_sheet.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/custom_circular_progress_indicator.dart';
|
import 'package:yimaru_app/ui/widgets/custom_circular_progress_indicator.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/custom_linear_progress_indicator.dart';
|
import 'package:yimaru_app/ui/widgets/custom_linear_progress_indicator.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/wave_wrapper.dart';
|
import 'package:yimaru_app/ui/widgets/wave_wrapper.dart';
|
||||||
|
|
@ -365,8 +365,7 @@ class InteractLearnPracticeScreen
|
||||||
await _showSheet(context: context, viewModel: viewModel),
|
await _showSheet(context: context, viewModel: viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildSheet(LearnPracticeViewModel viewModel) =>
|
Widget _buildSheet(LearnPracticeViewModel viewModel) => CancelPracticeSheet(
|
||||||
CancelLearnPracticeSheet(
|
|
||||||
onClose: viewModel.pop,
|
onClose: viewModel.pop,
|
||||||
onContinue: viewModel.pop,
|
onContinue: viewModel.pop,
|
||||||
user: viewModel.user?.firstName ?? '',
|
user: viewModel.user?.firstName ?? '',
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import 'package:yimaru_app/ui/views/learn_practice/learn_practice_viewmodel.dart
|
||||||
|
|
||||||
import '../../../common/app_colors.dart';
|
import '../../../common/app_colors.dart';
|
||||||
import '../../../common/ui_helpers.dart';
|
import '../../../common/ui_helpers.dart';
|
||||||
import '../../../widgets/cancel_learn_practice_sheet.dart';
|
import '../../../widgets/cancel_practice_sheet.dart';
|
||||||
import '../../../widgets/custom_elevated_button.dart';
|
import '../../../widgets/custom_elevated_button.dart';
|
||||||
import '../../../widgets/small_app_bar.dart';
|
import '../../../widgets/small_app_bar.dart';
|
||||||
|
|
||||||
|
|
@ -104,8 +104,7 @@ class LearnPracticeAppreciationScreen
|
||||||
await _showSheet(context: context, viewModel: viewModel),
|
await _showSheet(context: context, viewModel: viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildSheet(LearnPracticeViewModel viewModel) =>
|
Widget _buildSheet(LearnPracticeViewModel viewModel) => CancelPracticeSheet(
|
||||||
CancelLearnPracticeSheet(
|
|
||||||
onClose: viewModel.pop,
|
onClose: viewModel.pop,
|
||||||
onContinue: viewModel.pop,
|
onContinue: viewModel.pop,
|
||||||
user: viewModel.user?.firstName ?? '',
|
user: viewModel.user?.firstName ?? '',
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import 'package:yimaru_app/ui/views/learn_practice/learn_practice_viewmodel.dart
|
||||||
import '../../../common/app_colors.dart';
|
import '../../../common/app_colors.dart';
|
||||||
import '../../../common/translations/locale_keys.g.dart';
|
import '../../../common/translations/locale_keys.g.dart';
|
||||||
import '../../../common/ui_helpers.dart';
|
import '../../../common/ui_helpers.dart';
|
||||||
import '../../../widgets/cancel_learn_practice_sheet.dart';
|
import '../../../widgets/cancel_practice_sheet.dart';
|
||||||
import '../../../widgets/custom_elevated_button.dart';
|
import '../../../widgets/custom_elevated_button.dart';
|
||||||
import '../../../widgets/small_app_bar.dart';
|
import '../../../widgets/small_app_bar.dart';
|
||||||
|
|
||||||
|
|
@ -87,8 +87,7 @@ class LearnPracticeDescriptionScreen
|
||||||
await _showSheet(context: context, viewModel: viewModel),
|
await _showSheet(context: context, viewModel: viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildSheet(LearnPracticeViewModel viewModel) =>
|
Widget _buildSheet(LearnPracticeViewModel viewModel) => CancelPracticeSheet(
|
||||||
CancelLearnPracticeSheet(
|
|
||||||
onClose: viewModel.pop,
|
onClose: viewModel.pop,
|
||||||
onContinue: viewModel.pop,
|
onContinue: viewModel.pop,
|
||||||
user: viewModel.user?.firstName ?? '',
|
user: viewModel.user?.firstName ?? '',
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ 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/enmus.dart';
|
||||||
import '../../../common/ui_helpers.dart';
|
import '../../../common/ui_helpers.dart';
|
||||||
import '../../../widgets/cancel_learn_practice_sheet.dart';
|
import '../../../widgets/cancel_practice_sheet.dart';
|
||||||
import '../../../widgets/custom_elevated_button.dart';
|
import '../../../widgets/custom_elevated_button.dart';
|
||||||
import '../../../widgets/small_app_bar.dart';
|
import '../../../widgets/small_app_bar.dart';
|
||||||
import '../../../widgets/speaking_partner_image.dart';
|
import '../../../widgets/speaking_partner_image.dart';
|
||||||
|
|
@ -106,8 +106,7 @@ class LearnPracticeIntroScreen extends ViewModelWidget<LearnPracticeViewModel> {
|
||||||
await _showSheet(context: context, viewModel: viewModel),
|
await _showSheet(context: context, viewModel: viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildSheet(LearnPracticeViewModel viewModel) =>
|
Widget _buildSheet(LearnPracticeViewModel viewModel) => CancelPracticeSheet(
|
||||||
CancelLearnPracticeSheet(
|
|
||||||
onClose: viewModel.pop,
|
onClose: viewModel.pop,
|
||||||
onContinue: viewModel.pop,
|
onContinue: viewModel.pop,
|
||||||
user: viewModel.user?.firstName ?? '',
|
user: viewModel.user?.firstName ?? '',
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import 'package:yimaru_app/ui/widgets/learn_practice_results_wrapper.dart';
|
||||||
|
|
||||||
import '../../../common/app_colors.dart';
|
import '../../../common/app_colors.dart';
|
||||||
import '../../../common/ui_helpers.dart';
|
import '../../../common/ui_helpers.dart';
|
||||||
import '../../../widgets/cancel_learn_practice_sheet.dart';
|
import '../../../widgets/cancel_practice_sheet.dart';
|
||||||
import '../../../widgets/custom_elevated_button.dart';
|
import '../../../widgets/custom_elevated_button.dart';
|
||||||
import '../../../widgets/page_loading_indicator.dart';
|
import '../../../widgets/page_loading_indicator.dart';
|
||||||
import '../../../widgets/small_app_bar.dart';
|
import '../../../widgets/small_app_bar.dart';
|
||||||
|
|
@ -120,8 +120,7 @@ class LearnPracticeResultScreen
|
||||||
await _showSheet(context: context, viewModel: viewModel),
|
await _showSheet(context: context, viewModel: viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildSheet(LearnPracticeViewModel viewModel) =>
|
Widget _buildSheet(LearnPracticeViewModel viewModel) => CancelPracticeSheet(
|
||||||
CancelLearnPracticeSheet(
|
|
||||||
onClose: viewModel.pop,
|
onClose: viewModel.pop,
|
||||||
onContinue: viewModel.pop,
|
onContinue: viewModel.pop,
|
||||||
user: viewModel.user?.firstName ?? '',
|
user: viewModel.user?.firstName ?? '',
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import 'package:yimaru_app/ui/widgets/custom_column_button.dart';
|
||||||
import '../../../../models/learn_question.dart';
|
import '../../../../models/learn_question.dart';
|
||||||
import '../../../common/app_colors.dart';
|
import '../../../common/app_colors.dart';
|
||||||
import '../../../common/ui_helpers.dart';
|
import '../../../common/ui_helpers.dart';
|
||||||
import '../../../widgets/cancel_learn_practice_sheet.dart';
|
import '../../../widgets/cancel_practice_sheet.dart';
|
||||||
import '../../../widgets/small_app_bar.dart';
|
import '../../../widgets/small_app_bar.dart';
|
||||||
|
|
||||||
class StartLearnPracticeScreen extends ViewModelWidget<LearnPracticeViewModel> {
|
class StartLearnPracticeScreen extends ViewModelWidget<LearnPracticeViewModel> {
|
||||||
|
|
@ -111,8 +111,7 @@ class StartLearnPracticeScreen extends ViewModelWidget<LearnPracticeViewModel> {
|
||||||
title:
|
title:
|
||||||
'${LocaleKeys.practice_speaking.tr()} ($index/${viewModel.questions.length})');
|
'${LocaleKeys.practice_speaking.tr()} ($index/${viewModel.questions.length})');
|
||||||
|
|
||||||
Widget _buildSheet(LearnPracticeViewModel viewModel) =>
|
Widget _buildSheet(LearnPracticeViewModel viewModel) => CancelPracticeSheet(
|
||||||
CancelLearnPracticeSheet(
|
|
||||||
onClose: viewModel.pop,
|
onClose: viewModel.pop,
|
||||||
onContinue: viewModel.pop,
|
onContinue: viewModel.pop,
|
||||||
user: viewModel.user?.firstName ?? '',
|
user: viewModel.user?.firstName ?? '',
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ class LearnProgramViewModel extends ReactiveViewModel {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<ListenableServiceMixin> get listenableServices =>
|
List<ListenableServiceMixin> get listenableServices =>
|
||||||
[_learnService, _authenticationService,_inAppNotificationService];
|
[_learnService, _authenticationService, _inAppNotificationService];
|
||||||
|
|
||||||
// Current user
|
// Current user
|
||||||
User? get _user => _authenticationService.user;
|
User? get _user => _authenticationService.user;
|
||||||
|
|
|
||||||
|
|
@ -60,8 +60,8 @@ class NotificationView extends StackedView<NotificationViewModel> {
|
||||||
List<Widget> _buildColumnChildren(NotificationViewModel viewModel) => [
|
List<Widget> _buildColumnChildren(NotificationViewModel viewModel) => [
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildAppBarWrapper(viewModel),
|
_buildAppBarWrapper(viewModel),
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildNotificationsColumnWrapper(viewModel)
|
_buildNotificationsColumnWrapper(viewModel)
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildAppBarWrapper(NotificationViewModel viewModel) => Padding(
|
Widget _buildAppBarWrapper(NotificationViewModel viewModel) => Padding(
|
||||||
|
|
@ -75,7 +75,6 @@ class NotificationView extends StackedView<NotificationViewModel> {
|
||||||
title: LocaleKeys.notifications.tr(),
|
title: LocaleKeys.notifications.tr(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
Widget _buildNotificationsColumnWrapper(NotificationViewModel viewModel) =>
|
Widget _buildNotificationsColumnWrapper(NotificationViewModel viewModel) =>
|
||||||
Expanded(child: _buildNotificationsColumnScrollView(viewModel));
|
Expanded(child: _buildNotificationsColumnScrollView(viewModel));
|
||||||
|
|
||||||
|
|
@ -87,26 +86,24 @@ class NotificationView extends StackedView<NotificationViewModel> {
|
||||||
Widget _buildListViewBuilder(NotificationViewModel viewModel) =>
|
Widget _buildListViewBuilder(NotificationViewModel viewModel) =>
|
||||||
viewModel.busy(StateObjects.notifications)
|
viewModel.busy(StateObjects.notifications)
|
||||||
? _buildProgressIndicator()
|
? _buildProgressIndicator()
|
||||||
: _buildListView(viewModel);
|
: _buildListView(viewModel);
|
||||||
|
|
||||||
Widget _buildProgressIndicator() => const Center(
|
Widget _buildProgressIndicator() => const Center(
|
||||||
child: CustomCircularProgressIndicator(color: kcPrimaryColor),
|
child: CustomCircularProgressIndicator(color: kcPrimaryColor),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildListView(NotificationViewModel viewModel) => ListView.separated(
|
Widget _buildListView(NotificationViewModel viewModel) => ListView.separated(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: viewModel.notifications.length,
|
itemCount: viewModel.notifications.length,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
separatorBuilder: (context, index) => verticalSpaceSmall,
|
separatorBuilder: (context, index) => verticalSpaceSmall,
|
||||||
itemBuilder: (context, index) => _buildCard(
|
itemBuilder: (context, index) => _buildCard(
|
||||||
notification: viewModel.notifications[index],
|
notification: viewModel.notifications[index],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildCard({
|
Widget _buildCard({
|
||||||
required InAppNotification notification,
|
required InAppNotification notification,
|
||||||
}) =>
|
}) =>
|
||||||
NotificationCard(notification: notification);
|
NotificationCard(notification: notification);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import '../../../app/app.locator.dart';
|
||||||
import '../../../models/user.dart';
|
import '../../../models/user.dart';
|
||||||
import '../../../services/api_service.dart';
|
import '../../../services/api_service.dart';
|
||||||
import '../../../services/authentication_service.dart';
|
import '../../../services/authentication_service.dart';
|
||||||
import '../../../services/google_auth_service.dart';
|
|
||||||
import '../../../services/in_app_notification_service.dart';
|
import '../../../services/in_app_notification_service.dart';
|
||||||
import '../../../services/status_checker_service.dart';
|
import '../../../services/status_checker_service.dart';
|
||||||
import '../../common/app_colors.dart';
|
import '../../common/app_colors.dart';
|
||||||
|
|
@ -32,7 +31,7 @@ class ProfileViewModel extends ReactiveViewModel {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<ListenableServiceMixin> get listenableServices =>
|
List<ListenableServiceMixin> get listenableServices =>
|
||||||
[_authenticationService,_inAppNotificationService];
|
[_authenticationService, _inAppNotificationService];
|
||||||
|
|
||||||
// Current user
|
// Current user
|
||||||
User? get _user => _authenticationService.user;
|
User? get _user => _authenticationService.user;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ import 'package:stacked/stacked.dart';
|
||||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
import 'package:yimaru_app/ui/views/startup/startup_viewmodel.dart';
|
import 'package:yimaru_app/ui/views/startup/startup_viewmodel.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
|
||||||
|
|
||||||
import '../../../common/translations/locale_keys.g.dart';
|
import '../../../common/translations/locale_keys.g.dart';
|
||||||
import '../../../widgets/custom_circular_progress_indicator.dart';
|
import '../../../widgets/custom_circular_progress_indicator.dart';
|
||||||
|
|
@ -13,29 +12,29 @@ import '../../../widgets/custom_circular_progress_indicator.dart';
|
||||||
class FirstStartupScreen extends ViewModelWidget<StartupViewModel> {
|
class FirstStartupScreen extends ViewModelWidget<StartupViewModel> {
|
||||||
final String? label;
|
final String? label;
|
||||||
|
|
||||||
const FirstStartupScreen({super.key,this.label});
|
const FirstStartupScreen({super.key, this.label});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, StartupViewModel viewModel) =>
|
Widget build(BuildContext context, StartupViewModel viewModel) =>
|
||||||
_buildScaffoldWrapper();
|
_buildScaffoldWrapper();
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper( ) => Scaffold(
|
Widget _buildScaffoldWrapper() => Scaffold(
|
||||||
backgroundColor: kcPrimaryColor,
|
backgroundColor: kcPrimaryColor,
|
||||||
body: _buildScaffoldPadding(),
|
body: _buildScaffoldPadding(),
|
||||||
);
|
);
|
||||||
Widget _buildScaffoldPadding( ) => Padding(
|
Widget _buildScaffoldPadding() => Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildScaffold(),
|
child: _buildScaffold(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildScaffold( ) => Column(
|
Widget _buildScaffold() => Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: _buildScaffoldChildren(),
|
children: _buildScaffoldChildren(),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildScaffoldChildren( ) =>
|
List<Widget> _buildScaffoldChildren() =>
|
||||||
[_buildUpperColumn(), _buildLowerColumnWrapper()];
|
[_buildUpperColumn(), _buildLowerColumnWrapper()];
|
||||||
|
|
||||||
Widget _buildUpperColumn() => Column(
|
Widget _buildUpperColumn() => Column(
|
||||||
|
|
@ -57,17 +56,17 @@ class FirstStartupScreen extends ViewModelWidget<StartupViewModel> {
|
||||||
height: 25,
|
height: 25,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildLowerColumnWrapper( ) => Expanded(
|
Widget _buildLowerColumnWrapper() => Expanded(
|
||||||
child: _buildLowerColumn(),
|
child: _buildLowerColumn(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildLowerColumn( ) => Column(
|
Widget _buildLowerColumn() => Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: _buildLowerColumnChildren(),
|
children: _buildLowerColumnChildren(),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildLowerColumnChildren( ) => [
|
List<Widget> _buildLowerColumnChildren() => [
|
||||||
_buildTitle(),
|
_buildTitle(),
|
||||||
verticalSpaceMedium,
|
verticalSpaceMedium,
|
||||||
_buildImageWrapper(),
|
_buildImageWrapper(),
|
||||||
|
|
@ -108,44 +107,39 @@ class FirstStartupScreen extends ViewModelWidget<StartupViewModel> {
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildSafeWrapper( ) =>
|
Widget _buildSafeWrapper() => SafeArea(child: _buildContinueButtonWrapper());
|
||||||
SafeArea(child: _buildContinueButtonWrapper());
|
|
||||||
|
|
||||||
Widget _buildContinueButtonWrapper( ) => Align(
|
Widget _buildContinueButtonWrapper() => Align(
|
||||||
alignment: Alignment.bottomCenter,
|
alignment: Alignment.bottomCenter,
|
||||||
child: _buildLoadingTextContainer(),
|
child: _buildLoadingTextContainer(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Widget _buildLoadingTextContainer() => Padding(
|
Widget _buildLoadingTextContainer() => Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 50),
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
child: _buildLoadingTextWrapper(),
|
child: _buildLoadingTextWrapper(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildLoadingTextWrapper() => Row(
|
Widget _buildLoadingTextWrapper() => Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: _buildLoadingTextChildren(),
|
children: _buildLoadingTextChildren(),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildLoadingTextChildren() => [
|
List<Widget> _buildLoadingTextChildren() => [
|
||||||
_buildLoadingText(),
|
_buildLoadingText(),
|
||||||
horizontalSpaceSmall,
|
horizontalSpaceSmall,
|
||||||
_buildIndicatorWrapper(),
|
_buildIndicatorWrapper(),
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildLoadingText() =>
|
Widget _buildLoadingText() =>
|
||||||
Text('${label ?? LocaleKeys.loading.tr()} ...', style: style16W600);
|
Text('${label ?? LocaleKeys.loading.tr()} ...', style: style16W600);
|
||||||
|
|
||||||
Widget _buildIndicatorWrapper() => SizedBox(
|
Widget _buildIndicatorWrapper() => SizedBox(
|
||||||
width: 16,
|
width: 16,
|
||||||
height: 16,
|
height: 16,
|
||||||
child: _buildIndicator(),
|
child: _buildIndicator(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildIndicator() =>
|
Widget _buildIndicator() =>
|
||||||
const CustomCircularProgressIndicator(color: kcWhite);
|
const CustomCircularProgressIndicator(color: kcWhite);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
|
||||||
|
|
||||||
import '../../../common/translations/locale_keys.g.dart';
|
import '../../../common/translations/locale_keys.g.dart';
|
||||||
import '../../../widgets/custom_circular_progress_indicator.dart';
|
import '../../../widgets/custom_circular_progress_indicator.dart';
|
||||||
|
|
@ -13,141 +12,134 @@ import '../startup_viewmodel.dart';
|
||||||
class SecondStartupScreen extends ViewModelWidget<StartupViewModel> {
|
class SecondStartupScreen extends ViewModelWidget<StartupViewModel> {
|
||||||
final String? label;
|
final String? label;
|
||||||
|
|
||||||
const SecondStartupScreen({super.key,this.label});
|
const SecondStartupScreen({super.key, this.label});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, StartupViewModel viewModel) =>
|
Widget build(BuildContext context, StartupViewModel viewModel) =>
|
||||||
_buildScaffoldWrapper();
|
_buildScaffoldWrapper();
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper( ) => Scaffold(
|
Widget _buildScaffoldWrapper() => Scaffold(
|
||||||
backgroundColor: Colors.amber,
|
backgroundColor: Colors.amber,
|
||||||
body: _buildScaffoldPadding(),
|
body: _buildScaffoldPadding(),
|
||||||
);
|
);
|
||||||
Widget _buildScaffoldPadding( ) => Padding(
|
Widget _buildScaffoldPadding() => Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildScaffold(),
|
child: _buildScaffold(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildScaffold( ) => Column(
|
Widget _buildScaffold() => Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: _buildScaffoldChildren(),
|
children: _buildScaffoldChildren(),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildScaffoldChildren( ) =>
|
List<Widget> _buildScaffoldChildren() =>
|
||||||
[_buildUpperColumn(), _buildLowerColumnWrapper()];
|
[_buildUpperColumn(), _buildLowerColumnWrapper()];
|
||||||
|
|
||||||
Widget _buildUpperColumn() => Column(
|
Widget _buildUpperColumn() => Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: _buildUpperColumnChildren(),
|
children: _buildUpperColumnChildren(),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildUpperColumnChildren() =>
|
List<Widget> _buildUpperColumnChildren() =>
|
||||||
[verticalSpaceLarge, _buildIconWrapper(), verticalSpaceLarge];
|
[verticalSpaceLarge, _buildIconWrapper(), verticalSpaceLarge];
|
||||||
|
|
||||||
Widget _buildIconWrapper() => Align(
|
Widget _buildIconWrapper() => Align(
|
||||||
alignment: Alignment.topLeft,
|
alignment: Alignment.topLeft,
|
||||||
child: _buildIcon(),
|
child: _buildIcon(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildIcon() => SvgPicture.asset(
|
Widget _buildIcon() => SvgPicture.asset(
|
||||||
'assets/icons/logo_purple.svg',
|
'assets/icons/logo_purple.svg',
|
||||||
height: 25,
|
height: 25,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildLowerColumnWrapper( ) => Expanded(
|
Widget _buildLowerColumnWrapper() => Expanded(
|
||||||
child: _buildLowerColumn(),
|
child: _buildLowerColumn(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildLowerColumn( ) => Column(
|
Widget _buildLowerColumn() => Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: _buildLowerColumnChildren(),
|
children: _buildLowerColumnChildren(),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildLowerColumnChildren( ) => [
|
|
||||||
_buildTitle(),
|
|
||||||
verticalSpaceMedium,
|
|
||||||
_buildImageWrapper(),
|
|
||||||
verticalSpaceMedium,
|
|
||||||
_buildSafeWrapper()
|
|
||||||
];
|
|
||||||
|
|
||||||
|
List<Widget> _buildLowerColumnChildren() => [
|
||||||
|
_buildTitle(),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildImageWrapper(),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildSafeWrapper()
|
||||||
|
];
|
||||||
|
|
||||||
Widget _buildTitle() => Text.rich(
|
Widget _buildTitle() => Text.rich(
|
||||||
TextSpan(
|
|
||||||
text: 'እንግሊዝኛ\n',
|
|
||||||
style: style25P600,
|
|
||||||
children: [
|
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: 'በማንኛውም',
|
text: 'እንግሊዝኛ\n',
|
||||||
style: style25P400,
|
|
||||||
),
|
|
||||||
TextSpan(
|
|
||||||
text: ' እድሜ ',
|
|
||||||
style: style25P600,
|
style: style25P600,
|
||||||
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text: 'በማንኛውም',
|
||||||
|
style: style25P400,
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: ' እድሜ ',
|
||||||
|
style: style25P600,
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: 'ይማሩ!',
|
||||||
|
style: style25P400,
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
TextSpan(
|
);
|
||||||
text: 'ይማሩ!',
|
|
||||||
style: style25P400,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildImageWrapper() => Expanded(child: _buildImageClipper());
|
Widget _buildImageWrapper() => Expanded(child: _buildImageClipper());
|
||||||
|
|
||||||
Widget _buildImageClipper() => ClipRRect(
|
Widget _buildImageClipper() => ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(25),
|
borderRadius: BorderRadius.circular(25),
|
||||||
child: _buildImage(),
|
child: _buildImage(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildImage() => Image.asset(
|
Widget _buildImage() => Image.asset(
|
||||||
'assets/images/landing_2.png',
|
'assets/images/landing_2.png',
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildSafeWrapper( ) =>
|
|
||||||
SafeArea(child: _buildContinueButtonWrapper());
|
|
||||||
|
|
||||||
Widget _buildContinueButtonWrapper( ) => Align(
|
|
||||||
alignment: Alignment.bottomCenter,
|
|
||||||
child: _buildLoadingTextContainer(),
|
|
||||||
);
|
|
||||||
|
|
||||||
|
Widget _buildSafeWrapper() => SafeArea(child: _buildContinueButtonWrapper());
|
||||||
|
|
||||||
|
Widget _buildContinueButtonWrapper() => Align(
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
child: _buildLoadingTextContainer(),
|
||||||
|
);
|
||||||
|
|
||||||
Widget _buildLoadingTextContainer() => Padding(
|
Widget _buildLoadingTextContainer() => Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 50),
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
child: _buildLoadingTextWrapper(),
|
child: _buildLoadingTextWrapper(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildLoadingTextWrapper() => Row(
|
Widget _buildLoadingTextWrapper() => Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: _buildLoadingTextChildren(),
|
children: _buildLoadingTextChildren(),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildLoadingTextChildren() => [
|
List<Widget> _buildLoadingTextChildren() => [
|
||||||
_buildLoadingText(),
|
_buildLoadingText(),
|
||||||
horizontalSpaceSmall,
|
horizontalSpaceSmall,
|
||||||
_buildIndicatorWrapper(),
|
_buildIndicatorWrapper(),
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildLoadingText() =>
|
Widget _buildLoadingText() =>
|
||||||
Text('${label ?? LocaleKeys.loading.tr()} ...', style: style16P600);
|
Text('${label ?? LocaleKeys.loading.tr()} ...', style: style16P600);
|
||||||
|
|
||||||
Widget _buildIndicatorWrapper() => SizedBox(
|
Widget _buildIndicatorWrapper() => SizedBox(
|
||||||
width: 16,
|
width: 16,
|
||||||
height: 16,
|
height: 16,
|
||||||
child: _buildIndicator(),
|
child: _buildIndicator(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildIndicator() =>
|
Widget _buildIndicator() =>
|
||||||
const CustomCircularProgressIndicator(color: kcPrimaryColor);
|
const CustomCircularProgressIndicator(color: kcPrimaryColor);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
|
||||||
|
|
||||||
import '../../../common/translations/locale_keys.g.dart';
|
import '../../../common/translations/locale_keys.g.dart';
|
||||||
import '../../../widgets/custom_circular_progress_indicator.dart';
|
import '../../../widgets/custom_circular_progress_indicator.dart';
|
||||||
|
|
@ -13,141 +12,134 @@ import '../startup_viewmodel.dart';
|
||||||
class ThirdStartupScreen extends ViewModelWidget<StartupViewModel> {
|
class ThirdStartupScreen extends ViewModelWidget<StartupViewModel> {
|
||||||
final String? label;
|
final String? label;
|
||||||
|
|
||||||
const ThirdStartupScreen({super.key,this.label});
|
const ThirdStartupScreen({super.key, this.label});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, StartupViewModel viewModel) =>
|
Widget build(BuildContext context, StartupViewModel viewModel) =>
|
||||||
_buildScaffoldWrapper();
|
_buildScaffoldWrapper();
|
||||||
|
|
||||||
Widget _buildScaffoldWrapper( ) => Scaffold(
|
Widget _buildScaffoldWrapper() => Scaffold(
|
||||||
backgroundColor:kcWhite,
|
backgroundColor: kcWhite,
|
||||||
body: _buildScaffoldPadding(),
|
body: _buildScaffoldPadding(),
|
||||||
);
|
);
|
||||||
Widget _buildScaffoldPadding( ) => Padding(
|
Widget _buildScaffoldPadding() => Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
child: _buildScaffold(),
|
child: _buildScaffold(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildScaffold( ) => Column(
|
Widget _buildScaffold() => Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: _buildScaffoldChildren(),
|
children: _buildScaffoldChildren(),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildScaffoldChildren( ) =>
|
List<Widget> _buildScaffoldChildren() =>
|
||||||
[_buildUpperColumn(), _buildLowerColumnWrapper()];
|
[_buildUpperColumn(), _buildLowerColumnWrapper()];
|
||||||
|
|
||||||
Widget _buildUpperColumn() => Column(
|
Widget _buildUpperColumn() => Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: _buildUpperColumnChildren(),
|
children: _buildUpperColumnChildren(),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildUpperColumnChildren() =>
|
List<Widget> _buildUpperColumnChildren() =>
|
||||||
[verticalSpaceLarge, _buildIconWrapper(), verticalSpaceLarge];
|
[verticalSpaceLarge, _buildIconWrapper(), verticalSpaceLarge];
|
||||||
|
|
||||||
Widget _buildIconWrapper() => Align(
|
Widget _buildIconWrapper() => Align(
|
||||||
alignment: Alignment.topLeft,
|
alignment: Alignment.topLeft,
|
||||||
child: _buildIcon(),
|
child: _buildIcon(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildIcon() => SvgPicture.asset(
|
Widget _buildIcon() => SvgPicture.asset(
|
||||||
'assets/icons/logo_purple.svg',
|
'assets/icons/logo_purple.svg',
|
||||||
height: 25,
|
height: 25,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildLowerColumnWrapper( ) => Expanded(
|
Widget _buildLowerColumnWrapper() => Expanded(
|
||||||
child: _buildLowerColumn(),
|
child: _buildLowerColumn(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildLowerColumn( ) => Column(
|
Widget _buildLowerColumn() => Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: _buildLowerColumnChildren(),
|
children: _buildLowerColumnChildren(),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildLowerColumnChildren( ) => [
|
|
||||||
_buildTitle(),
|
|
||||||
verticalSpaceMedium,
|
|
||||||
_buildImageWrapper(),
|
|
||||||
verticalSpaceMedium,
|
|
||||||
_buildSafeWrapper()
|
|
||||||
];
|
|
||||||
|
|
||||||
|
List<Widget> _buildLowerColumnChildren() => [
|
||||||
|
_buildTitle(),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildImageWrapper(),
|
||||||
|
verticalSpaceMedium,
|
||||||
|
_buildSafeWrapper()
|
||||||
|
];
|
||||||
|
|
||||||
Widget _buildTitle() => Text.rich(
|
Widget _buildTitle() => Text.rich(
|
||||||
TextSpan(
|
|
||||||
text: 'እንግሊዝኛ\n',
|
|
||||||
style: style25P600,
|
|
||||||
children: [
|
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: 'በማንኛውም',
|
text: 'እንግሊዝኛ\n',
|
||||||
style: style25P400,
|
|
||||||
),
|
|
||||||
TextSpan(
|
|
||||||
text: ' ቦታ ',
|
|
||||||
style: style25P600,
|
style: style25P600,
|
||||||
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text: 'በማንኛውም',
|
||||||
|
style: style25P400,
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: ' ቦታ ',
|
||||||
|
style: style25P600,
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: 'ይማሩ!',
|
||||||
|
style: style25P400,
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
TextSpan(
|
);
|
||||||
text: 'ይማሩ!',
|
|
||||||
style: style25P400,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildImageWrapper() => Expanded(child: _buildImageClipper());
|
Widget _buildImageWrapper() => Expanded(child: _buildImageClipper());
|
||||||
|
|
||||||
Widget _buildImageClipper() => ClipRRect(
|
Widget _buildImageClipper() => ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(25),
|
borderRadius: BorderRadius.circular(25),
|
||||||
child: _buildImage(),
|
child: _buildImage(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildImage() => Image.asset(
|
Widget _buildImage() => Image.asset(
|
||||||
'assets/images/landing_3.png',
|
'assets/images/landing_3.png',
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildSafeWrapper( ) =>
|
|
||||||
SafeArea(child: _buildContinueButtonWrapper());
|
|
||||||
|
|
||||||
Widget _buildContinueButtonWrapper( ) => Align(
|
|
||||||
alignment: Alignment.bottomCenter,
|
|
||||||
child: _buildLoadingTextContainer(),
|
|
||||||
);
|
|
||||||
|
|
||||||
|
Widget _buildSafeWrapper() => SafeArea(child: _buildContinueButtonWrapper());
|
||||||
|
|
||||||
|
Widget _buildContinueButtonWrapper() => Align(
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
child: _buildLoadingTextContainer(),
|
||||||
|
);
|
||||||
|
|
||||||
Widget _buildLoadingTextContainer() => Padding(
|
Widget _buildLoadingTextContainer() => Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 50),
|
padding: const EdgeInsets.only(bottom: 50),
|
||||||
child: _buildLoadingTextWrapper(),
|
child: _buildLoadingTextWrapper(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildLoadingTextWrapper() => Row(
|
Widget _buildLoadingTextWrapper() => Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: _buildLoadingTextChildren(),
|
children: _buildLoadingTextChildren(),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildLoadingTextChildren() => [
|
List<Widget> _buildLoadingTextChildren() => [
|
||||||
_buildLoadingText(),
|
_buildLoadingText(),
|
||||||
horizontalSpaceSmall,
|
horizontalSpaceSmall,
|
||||||
_buildIndicatorWrapper(),
|
_buildIndicatorWrapper(),
|
||||||
];
|
];
|
||||||
|
|
||||||
Widget _buildLoadingText() =>
|
Widget _buildLoadingText() =>
|
||||||
Text('${label ?? LocaleKeys.loading.tr()} ...', style: style16P600);
|
Text('${label ?? LocaleKeys.loading.tr()} ...', style: style16P600);
|
||||||
|
|
||||||
Widget _buildIndicatorWrapper() => SizedBox(
|
Widget _buildIndicatorWrapper() => SizedBox(
|
||||||
width: 16,
|
width: 16,
|
||||||
height: 16,
|
height: 16,
|
||||||
child: _buildIndicator(),
|
child: _buildIndicator(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildIndicator() =>
|
Widget _buildIndicator() =>
|
||||||
const CustomCircularProgressIndicator(color: kcPrimaryColor);
|
const CustomCircularProgressIndicator(color: kcPrimaryColor);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,12 @@
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/scheduler.dart';
|
import 'package:flutter/scheduler.dart';
|
||||||
import 'package:flutter_carousel_widget/flutter_carousel_widget.dart';
|
import 'package:flutter_carousel_widget/flutter_carousel_widget.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
|
||||||
import 'package:yimaru_app/ui/views/startup/screens/first_startup_screen.dart';
|
import 'package:yimaru_app/ui/views/startup/screens/first_startup_screen.dart';
|
||||||
import 'package:yimaru_app/ui/views/startup/screens/second_startup_screen.dart';
|
import 'package:yimaru_app/ui/views/startup/screens/second_startup_screen.dart';
|
||||||
import 'package:yimaru_app/ui/views/startup/screens/third_startup_screen.dart';
|
import 'package:yimaru_app/ui/views/startup/screens/third_startup_screen.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/custom_circular_progress_indicator.dart';
|
|
||||||
|
|
||||||
import '../../common/app_colors.dart';
|
import '../../common/app_colors.dart';
|
||||||
import '../../common/enmus.dart';
|
|
||||||
import '../../common/translations/locale_keys.g.dart';
|
|
||||||
import 'startup_viewmodel.dart';
|
import 'startup_viewmodel.dart';
|
||||||
|
|
||||||
class StartupView extends StackedView<StartupViewModel> {
|
class StartupView extends StackedView<StartupViewModel> {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import '../../../app/app.router.dart';
|
||||||
import '../../../models/user.dart';
|
import '../../../models/user.dart';
|
||||||
import '../../../services/api_service.dart';
|
import '../../../services/api_service.dart';
|
||||||
import '../../../services/image_downloader_service.dart';
|
import '../../../services/image_downloader_service.dart';
|
||||||
import '../../../services/in_app_notification_service.dart';
|
|
||||||
import '../../../services/localization_service.dart';
|
import '../../../services/localization_service.dart';
|
||||||
import '../../../services/status_checker_service.dart';
|
import '../../../services/status_checker_service.dart';
|
||||||
import '../../common/enmus.dart';
|
import '../../common/enmus.dart';
|
||||||
|
|
@ -29,7 +28,6 @@ class StartupViewModel extends ReactiveViewModel {
|
||||||
|
|
||||||
final _imageDownloaderService = locator<ImageDownloaderService>();
|
final _imageDownloaderService = locator<ImageDownloaderService>();
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<ListenableServiceMixin> get listenableServices =>
|
List<ListenableServiceMixin> get listenableServices =>
|
||||||
[_onboardingService, _authenticationService];
|
[_onboardingService, _authenticationService];
|
||||||
|
|
@ -144,6 +142,4 @@ class StartupViewModel extends ReactiveViewModel {
|
||||||
await replaceWithFailure();
|
await replaceWithFailure();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,13 @@ import '../common/ui_helpers.dart';
|
||||||
import 'custom_bottom_sheet.dart';
|
import 'custom_bottom_sheet.dart';
|
||||||
import 'custom_elevated_button.dart';
|
import 'custom_elevated_button.dart';
|
||||||
|
|
||||||
class CancelLearnPracticeSheet extends StatelessWidget {
|
class CancelPracticeSheet extends StatelessWidget {
|
||||||
final String user;
|
final String user;
|
||||||
final GestureTapCallback? onClose;
|
final GestureTapCallback? onClose;
|
||||||
final GestureTapCallback? onCancel;
|
final GestureTapCallback? onCancel;
|
||||||
final GestureTapCallback? onContinue;
|
final GestureTapCallback? onContinue;
|
||||||
|
|
||||||
const CancelLearnPracticeSheet(
|
const CancelPracticeSheet(
|
||||||
{super.key,
|
{super.key,
|
||||||
this.onClose,
|
this.onClose,
|
||||||
this.onCancel,
|
this.onCancel,
|
||||||
|
|
@ -1,40 +1,76 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_timer_countdown/flutter_timer_countdown.dart';
|
||||||
|
import 'package:stacked/stacked.dart';
|
||||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/course_practice_viewmodel.dart';
|
||||||
|
|
||||||
|
import '../common/helper_functions.dart';
|
||||||
import '../common/ui_helpers.dart';
|
import '../common/ui_helpers.dart';
|
||||||
|
|
||||||
class CountdownTimer extends StatelessWidget {
|
class CountdownTimer extends ViewModelWidget<CoursePracticeViewModel> {
|
||||||
const CountdownTimer({super.key});
|
final String? time;
|
||||||
|
const CountdownTimer({super.key, this.time});
|
||||||
|
|
||||||
|
Future<void> _stopRecording(CoursePracticeViewModel viewModel)async{
|
||||||
|
await viewModel.stopRecording();
|
||||||
|
viewModel.setNextButton();
|
||||||
|
}
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => _buildContainer();
|
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||||
|
_buildContainer(viewModel);
|
||||||
|
|
||||||
Widget _buildContainer() => Container(
|
Widget _buildContainer(CoursePracticeViewModel viewModel) => Container(
|
||||||
width: 100,
|
width: 100,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(25),
|
borderRadius: BorderRadius.circular(25),
|
||||||
color: kcPrimaryColor.withValues(alpha: 0.1),
|
color: kcPrimaryColor.withValues(alpha: 0.1),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
|
||||||
|
child: _buildRow(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildRow(CoursePracticeViewModel viewModel) => Row(
|
||||||
|
children: [
|
||||||
|
_buildClockIcon(),
|
||||||
|
horizontalSpaceTiny,
|
||||||
|
_buildCountdownState(viewModel),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
Widget _buildCountdownState(CoursePracticeViewModel viewModel) => time == null ? _buildCountdownTime(viewModel):_buildCountdownText();
|
||||||
|
|
||||||
|
Widget _buildCountdownText() => Text(
|
||||||
|
time ?? '',
|
||||||
|
style: style16P600,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildCountdownTime(CoursePracticeViewModel viewModel) =>
|
||||||
|
TimerCountdown(
|
||||||
|
spacerWidth: 0,
|
||||||
|
enableDescriptions: false,
|
||||||
|
timeTextStyle: style14P600,
|
||||||
|
colonsTextStyle: style14P400,
|
||||||
|
format: CountDownTimerFormat.minutesSeconds,
|
||||||
|
onEnd:()async => await _stopRecording(viewModel) ,
|
||||||
|
endTime: DateTime.now().add(
|
||||||
|
Duration(
|
||||||
|
minutes: getMinutes(
|
||||||
|
viewModel.questions[viewModel.currentQuestion].dynamicPayload
|
||||||
|
?.stimulus?.last.value['seconds'],
|
||||||
|
),
|
||||||
|
seconds: getSeconds(
|
||||||
|
viewModel.questions[viewModel.currentQuestion].dynamicPayload
|
||||||
|
?.stimulus?.last.value['seconds'],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
|
|
||||||
child: _buildRow(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildRow() => Row(
|
|
||||||
children: [
|
|
||||||
_buildClockIcon(),
|
|
||||||
horizontalSpaceTiny,
|
|
||||||
_buildCountdownTime(),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildCountdownTime() => Text(
|
|
||||||
'0:20',
|
|
||||||
style: style16P600,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildClockIcon() => const Icon(
|
Widget _buildClockIcon() => const Icon(
|
||||||
Icons.timer_outlined,
|
Icons.timer_outlined,
|
||||||
color: kcPrimaryColor,
|
color: kcPrimaryColor,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -191,14 +191,14 @@ class CourseModuleTileLarge extends ViewModelWidget<CourseModuleViewModel> {
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: lessons.length,
|
itemCount: lessons.length,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
itemBuilder: (context, index) => _buildCourseModuleCard(
|
itemBuilder: (context, index) => _buildCourseLessonTile(
|
||||||
lesson: lessons[index],
|
lesson: lessons[index],
|
||||||
onVideoTap: () async =>
|
onVideoTap: () async =>
|
||||||
await viewModel.navigateToCourseLessonDetail(lessons[index]),
|
await viewModel.navigateToCourseLessonDetail(lessons[index]),
|
||||||
onPracticeTap: () {}),
|
onPracticeTap: ()async=>await viewModel.navigateToCoursePractice(lessons[index].id ?? 0)),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildCourseModuleCard({
|
Widget _buildCourseLessonTile({
|
||||||
required CourseLesson lesson,
|
required CourseLesson lesson,
|
||||||
required GestureTapCallback onVideoTap,
|
required GestureTapCallback onVideoTap,
|
||||||
required GestureTapCallback onPracticeTap,
|
required GestureTapCallback onPracticeTap,
|
||||||
|
|
|
||||||
|
|
@ -1,68 +1,89 @@
|
||||||
|
import 'package:audioplayers/audioplayers.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:stacked/stacked.dart';
|
||||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
|
import 'package:yimaru_app/ui/views/course_practice/course_practice_viewmodel.dart';
|
||||||
|
|
||||||
class CustomResponseCard extends StatelessWidget {
|
import '../common/enmus.dart';
|
||||||
|
import 'custom_circular_progress_indicator.dart';
|
||||||
|
|
||||||
|
class CustomResponseCard extends ViewModelWidget<CoursePracticeViewModel> {
|
||||||
|
final Voice voice;
|
||||||
final String title;
|
final String title;
|
||||||
final String subtitle;
|
|
||||||
|
|
||||||
const CustomResponseCard(
|
const CustomResponseCard(
|
||||||
{super.key, required this.title, required this.subtitle});
|
{super.key,
|
||||||
|
required this.voice,
|
||||||
|
required this.title,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => _buildContainer();
|
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||||
|
_buildContainer(viewModel);
|
||||||
|
|
||||||
Widget _buildContainer() => Container(
|
Widget _buildContainer(CoursePracticeViewModel viewModel) => Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: kcWhite,
|
color: kcWhite,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
|
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
|
||||||
child: _buildRow(),
|
child: _buildRow(viewModel),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildRow() => Row(
|
Widget _buildRow(CoursePracticeViewModel viewModel) => Row(
|
||||||
children: [_buildPlayButton(), _buildColumnWrapper()],
|
children: [_buildPlayButton(viewModel), _buildColumnWrapper()],
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildPlayButton() => ElevatedButton(
|
Widget _buildPlayButton(CoursePracticeViewModel viewModel) => ElevatedButton(
|
||||||
onPressed: () {},
|
style: const ButtonStyle(
|
||||||
style: const ButtonStyle(
|
shape: WidgetStatePropertyAll(CircleBorder()),
|
||||||
shape: WidgetStatePropertyAll(CircleBorder()),
|
padding: WidgetStatePropertyAll(EdgeInsets.all(5)),
|
||||||
padding: WidgetStatePropertyAll(EdgeInsets.all(5)),
|
shadowColor: WidgetStatePropertyAll(kcPrimaryColor),
|
||||||
shadowColor: WidgetStatePropertyAll(kcPrimaryColor),
|
backgroundColor: WidgetStatePropertyAll(kcPrimaryColor),
|
||||||
backgroundColor: WidgetStatePropertyAll(kcPrimaryColor),
|
),
|
||||||
),
|
onPressed: () async =>
|
||||||
child: _buildPlayIcon(),
|
viewModel.player.state == PlayerState.playing
|
||||||
);
|
? await viewModel.pauseAudio()
|
||||||
|
: await viewModel.playResult( voice),
|
||||||
|
child: _buildButtonState(viewModel),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildButtonState(CoursePracticeViewModel viewModel) =>
|
||||||
|
|
||||||
|
viewModel.playing == voice
|
||||||
|
? viewModel.busy(StateObjects.coursePracticeReview)
|
||||||
|
? _buildProgressIndicatorWrapper()
|
||||||
|
: viewModel.player.state == PlayerState.playing
|
||||||
|
? _buildPauseIcon()
|
||||||
|
: _buildPlayIcon()
|
||||||
|
: _buildPlayIcon();
|
||||||
|
|
||||||
|
Widget _buildProgressIndicatorWrapper() => Center(
|
||||||
|
child: _buildProgressIndicator(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildProgressIndicator() =>
|
||||||
|
const CustomCircularProgressIndicator(color: kcWhite);
|
||||||
|
|
||||||
Widget _buildPlayIcon() => const Icon(
|
Widget _buildPlayIcon() => const Icon(
|
||||||
Icons.play_arrow_rounded,
|
Icons.play_arrow_rounded,
|
||||||
size: 25,
|
size: 25,
|
||||||
color: kcWhite,
|
color: kcWhite,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildColumnWrapper() => Expanded(child: _buildColumn());
|
Widget _buildPauseIcon() => const Icon(
|
||||||
|
Icons.pause,
|
||||||
|
size: 25,
|
||||||
|
color: kcWhite,
|
||||||
|
);
|
||||||
|
|
||||||
Widget _buildColumn() => Column(
|
Widget _buildColumnWrapper() => Expanded(child: _buildTitle());
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: _buildColumnChildren(),
|
|
||||||
);
|
|
||||||
|
|
||||||
List<Widget> _buildColumnChildren() => [_buildTitle(), _buildSubtitle()];
|
|
||||||
|
|
||||||
Widget _buildTitle() => Text(
|
Widget _buildTitle() => Text(
|
||||||
title,
|
title,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
softWrap: false,
|
softWrap: false,
|
||||||
style: style12RP600,
|
style: style12RP600,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildSubtitle() => Text(
|
|
||||||
subtitle,
|
|
||||||
maxLines: 1,
|
|
||||||
softWrap: false,
|
|
||||||
style: style12RP600,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
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';
|
||||||
|
|
||||||
class DuolingoAssessmentAppBar extends StatelessWidget {
|
class DuolingoPracticeAppBar extends StatelessWidget {
|
||||||
final String? title;
|
final String? title;
|
||||||
final GestureTapCallback? onClose;
|
final GestureTapCallback? onClose;
|
||||||
|
|
||||||
const DuolingoAssessmentAppBar({super.key, this.onClose, this.title});
|
const DuolingoPracticeAppBar({super.key, this.onClose, this.title});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => _buildAppBar();
|
Widget build(BuildContext context) => _buildAppBar();
|
||||||
|
|
@ -4,11 +4,11 @@ import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||||
import '../common/ui_helpers.dart';
|
import '../common/ui_helpers.dart';
|
||||||
import 'custom_elevated_button.dart';
|
import 'custom_elevated_button.dart';
|
||||||
|
|
||||||
class DuolingoAssessmentCard extends StatelessWidget {
|
class DuolingoPracticeCard extends StatelessWidget {
|
||||||
final String title;
|
final String title;
|
||||||
final GestureTapCallback? onTap;
|
final GestureTapCallback? onTap;
|
||||||
|
|
||||||
const DuolingoAssessmentCard({super.key, this.onTap, required this.title});
|
const DuolingoPracticeCard({super.key, this.onTap, required this.title});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => _buildContainer();
|
Widget build(BuildContext context) => _buildContainer();
|
||||||
|
|
@ -2,11 +2,11 @@ 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/common/ui_helpers.dart';
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
|
|
||||||
class DuolingoAssessmentQuestionCard extends StatelessWidget {
|
class DuolingoPracticeQuestionCard extends StatelessWidget {
|
||||||
final String? title;
|
final String? title;
|
||||||
final String? subtitle;
|
final String? subtitle;
|
||||||
|
|
||||||
const DuolingoAssessmentQuestionCard({super.key, this.title, this.subtitle});
|
const DuolingoPracticeQuestionCard({super.key, this.title, this.subtitle});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => _buildContainer();
|
Widget build(BuildContext context) => _buildContainer();
|
||||||
|
|
@ -4,9 +4,9 @@ import '../common/app_colors.dart';
|
||||||
import '../common/ui_helpers.dart';
|
import '../common/ui_helpers.dart';
|
||||||
import 'custom_elevated_button.dart';
|
import 'custom_elevated_button.dart';
|
||||||
|
|
||||||
class DuolingoAssessmentReviewSection extends StatelessWidget {
|
class DuolingoPracticeReviewSection extends StatelessWidget {
|
||||||
final GestureTapCallback? onTap;
|
final GestureTapCallback? onTap;
|
||||||
const DuolingoAssessmentReviewSection({super.key, this.onTap});
|
const DuolingoPracticeReviewSection({super.key, this.onTap});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => _buildReviewContainer();
|
Widget build(BuildContext context) => _buildReviewContainer();
|
||||||
|
|
@ -43,7 +43,7 @@ class LearnPracticeAnswerCard extends ViewModelWidget<LearnPracticeViewModel> {
|
||||||
shadowColor: WidgetStatePropertyAll(kcPrimaryColor),
|
shadowColor: WidgetStatePropertyAll(kcPrimaryColor),
|
||||||
backgroundColor: WidgetStatePropertyAll(kcPrimaryColor),
|
backgroundColor: WidgetStatePropertyAll(kcPrimaryColor),
|
||||||
),
|
),
|
||||||
onPressed: () async => viewModel.busyObject == answer['busy_object'] &&
|
onPressed: () async =>
|
||||||
viewModel.player.state == PlayerState.playing
|
viewModel.player.state == PlayerState.playing
|
||||||
? await viewModel.pauseAudio()
|
? await viewModel.pauseAudio()
|
||||||
: await viewModel.playResult(answer: answer, voice: voice),
|
: await viewModel.playResult(answer: answer, voice: voice),
|
||||||
|
|
@ -51,7 +51,7 @@ class LearnPracticeAnswerCard extends ViewModelWidget<LearnPracticeViewModel> {
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildButtonState(LearnPracticeViewModel viewModel) =>
|
Widget _buildButtonState(LearnPracticeViewModel viewModel) =>
|
||||||
viewModel.busyObject == answer['busy_object'] &&
|
|
||||||
viewModel.playing == voice
|
viewModel.playing == voice
|
||||||
? viewModel.busy(answer['busy_object'])
|
? viewModel.busy(answer['busy_object'])
|
||||||
? _buildProgressIndicatorWrapper()
|
? _buildProgressIndicatorWrapper()
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@ import 'package:yimaru_app/ui/widgets/custom_linear_progress_indicator.dart';
|
||||||
|
|
||||||
import '../common/app_colors.dart';
|
import '../common/app_colors.dart';
|
||||||
|
|
||||||
class ListenableAssessmentCard extends StatelessWidget {
|
class ListenablePracticeCard extends StatelessWidget {
|
||||||
const ListenableAssessmentCard({super.key});
|
const ListenablePracticeCard({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => _buildRow();
|
Widget build(BuildContext context) => _buildRow();
|
||||||
|
|
@ -11,13 +11,11 @@ class NotificationCard extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => _buildContainer();
|
Widget build(BuildContext context) => _buildContainer();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Widget _buildContainer() => Container(
|
Widget _buildContainer() => Container(
|
||||||
height: 100,
|
height: 100,
|
||||||
width: double.maxFinite,
|
width: double.maxFinite,
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 15),
|
margin: const EdgeInsets.symmetric(horizontal: 15),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 15,vertical: 15),
|
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(4),
|
||||||
color: (notification.isRead ?? false)
|
color: (notification.isRead ?? false)
|
||||||
|
|
@ -33,15 +31,12 @@ class NotificationCard extends StatelessWidget {
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildRow() => Row(
|
Widget _buildRow() => Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: _buildRowChildren(),
|
children: _buildRowChildren(),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildRowChildren()=> [
|
List<Widget> _buildRowChildren() =>
|
||||||
_buildIcon(),
|
[_buildIcon(), horizontalSpaceSmall, _buildColumnWrapper()];
|
||||||
horizontalSpaceSmall,
|
|
||||||
_buildColumnWrapper()
|
|
||||||
];
|
|
||||||
|
|
||||||
Widget _buildIcon() => const Icon(
|
Widget _buildIcon() => const Icon(
|
||||||
Icons.notifications_none,
|
Icons.notifications_none,
|
||||||
|
|
@ -49,15 +44,14 @@ class NotificationCard extends StatelessWidget {
|
||||||
color: kcMediumGrey,
|
color: kcMediumGrey,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildColumnWrapper()=> Expanded(child: _buildColumn());
|
Widget _buildColumnWrapper() => Expanded(child: _buildColumn());
|
||||||
|
|
||||||
Widget _buildColumn() => Column(
|
Widget _buildColumn() => Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: _buildColumnChildren(),
|
children: _buildColumnChildren(),
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Widget> _buildColumnChildren() =>
|
List<Widget> _buildColumnChildren() => [_buildTitle(), _buildSubtitle()];
|
||||||
[ _buildTitle(), _buildSubtitle()];
|
|
||||||
|
|
||||||
Widget _buildTitle() => Text(
|
Widget _buildTitle() => Text(
|
||||||
notification.payload?.headline ?? '',
|
notification.payload?.headline ?? '',
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,33 @@
|
||||||
import 'package:badges/badges.dart' as badges;
|
import 'package:badges/badges.dart' as badges;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:badges/badges.dart';
|
|
||||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||||
import '../common/app_colors.dart';
|
import '../common/app_colors.dart';
|
||||||
|
|
||||||
class NotificationIcon extends StatelessWidget {
|
class NotificationIcon extends StatelessWidget {
|
||||||
final String count;
|
final String count;
|
||||||
final GestureTapCallback? onTap;
|
final GestureTapCallback? onTap;
|
||||||
const NotificationIcon({super.key,this.onTap,required this.count});
|
const NotificationIcon({super.key, this.onTap, required this.count});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => _buildNotificationIconWrapper();
|
Widget build(BuildContext context) => _buildNotificationIconWrapper();
|
||||||
|
|
||||||
|
|
||||||
Widget _buildNotificationIconWrapper() => Align(
|
Widget _buildNotificationIconWrapper() => Align(
|
||||||
alignment: Alignment.bottomRight,
|
alignment: Alignment.bottomRight, child: _buildNotificationButton());
|
||||||
child: _buildNotificationButton());
|
|
||||||
|
|
||||||
Widget _buildNotificationButton() =>
|
Widget _buildNotificationButton() => GestureDetector(
|
||||||
GestureDetector(
|
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: _buildNotificationBadge(),
|
child: _buildNotificationBadge(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildNotificationBadge()=> badges.Badge(
|
Widget _buildNotificationBadge() => badges.Badge(
|
||||||
badgeContent: Text(count,style: style12W600,),
|
badgeContent: Text(
|
||||||
|
count,
|
||||||
child: _buildNotificationIcon(),
|
style: style12W600,
|
||||||
);
|
),
|
||||||
|
child: _buildNotificationIcon(),
|
||||||
|
);
|
||||||
Widget _buildNotificationIcon() => const Icon(
|
Widget _buildNotificationIcon() => const Icon(
|
||||||
Icons.notifications_none,
|
Icons.notifications_none,
|
||||||
color: kcDarkGrey,
|
color: kcDarkGrey,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,17 +4,17 @@ import 'package:yimaru_app/ui/widgets/no_data_indicator.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/page_loading_indicator.dart';
|
import 'package:yimaru_app/ui/widgets/page_loading_indicator.dart';
|
||||||
import 'package:yimaru_app/ui/widgets/small_app_bar.dart';
|
import 'package:yimaru_app/ui/widgets/small_app_bar.dart';
|
||||||
|
|
||||||
import '../../../common/app_colors.dart';
|
import '../common/app_colors.dart';
|
||||||
import '../../../common/translations/locale_keys.g.dart';
|
import '../common/translations/locale_keys.g.dart';
|
||||||
import '../../../common/ui_helpers.dart';
|
import '../common/ui_helpers.dart';
|
||||||
|
|
||||||
class LearnLoadingScreen extends StatelessWidget {
|
class PracticeLoadingScreen extends StatelessWidget {
|
||||||
final bool isEmpty;
|
final bool isEmpty;
|
||||||
final bool isLoading;
|
final bool isLoading;
|
||||||
final GestureTapCallback? onPop;
|
final GestureTapCallback? onPop;
|
||||||
final GestureTapCallback? onTap;
|
final GestureTapCallback? onTap;
|
||||||
|
|
||||||
const LearnLoadingScreen(
|
const PracticeLoadingScreen(
|
||||||
{super.key,
|
{super.key,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
this.onPop,
|
this.onPop,
|
||||||
|
|
@ -1,56 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:stacked/stacked.dart';
|
|
||||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
|
||||||
import 'package:yimaru_app/ui/views/learn_practice/learn_practice_viewmodel.dart';
|
|
||||||
import 'package:yimaru_app/ui/widgets/custom_response_card.dart';
|
|
||||||
|
|
||||||
class PracticeResultCard extends ViewModelWidget<LearnPracticeViewModel> {
|
|
||||||
final Map<String, dynamic> data;
|
|
||||||
const PracticeResultCard({super.key, required this.data});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, LearnPracticeViewModel viewModel) =>
|
|
||||||
_buildColumnWrapper();
|
|
||||||
|
|
||||||
Widget _buildColumnWrapper() => SizedBox(
|
|
||||||
height: 100,
|
|
||||||
width: double.maxFinite,
|
|
||||||
child: _buildColumn(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildColumn() => Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: _buildColumnChildren(),
|
|
||||||
);
|
|
||||||
|
|
||||||
List<Widget> _buildColumnChildren() =>
|
|
||||||
[_buildQuestion(), verticalSpaceSmall, _buildRow()];
|
|
||||||
|
|
||||||
Widget _buildQuestion() => Text(
|
|
||||||
data['question_text'],
|
|
||||||
style: style14DG400,
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget _buildRow() => Row(
|
|
||||||
children: _buildRowChildren(),
|
|
||||||
);
|
|
||||||
|
|
||||||
List<Widget> _buildRowChildren() => [
|
|
||||||
_buildSampleResponseWrapper(),
|
|
||||||
horizontalSpaceSmall,
|
|
||||||
_buildActualResponseWrapper()
|
|
||||||
];
|
|
||||||
|
|
||||||
Widget _buildSampleResponseWrapper() =>
|
|
||||||
Expanded(child: _buildSampleResponse());
|
|
||||||
|
|
||||||
Widget _buildSampleResponse() =>
|
|
||||||
const CustomResponseCard(title: 'Sample Answer', subtitle: '0:54');
|
|
||||||
|
|
||||||
Widget _buildActualResponseWrapper() =>
|
|
||||||
Expanded(child: _buildActualResponse());
|
|
||||||
|
|
||||||
Widget _buildActualResponse() =>
|
|
||||||
const CustomResponseCard(title: 'Sample Answer', subtitle: '0:54');
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user