feat(course): Add lesson level duolingo practice.
This commit is contained in:
parent
3fc9635207
commit
813720db89
|
|
@ -194,5 +194,17 @@
|
|||
"completed_practices": "Completed Practices",
|
||||
"total_practices": "Total Practices",
|
||||
"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/services/in_app_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
|
||||
|
||||
@StackedApp(
|
||||
|
|
@ -100,6 +101,7 @@ import 'package:yimaru_app/services/push_notification_service.dart';
|
|||
MaterialRoute(page: LearnCourseView),
|
||||
MaterialRoute(page: PaymentView),
|
||||
MaterialRoute(page: NotificationView),
|
||||
MaterialRoute(page: CoursePracticeView),
|
||||
// @stacked-route
|
||||
],
|
||||
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 '../models/course_module.dart';
|
||||
import '../models/course_practice.dart';
|
||||
import '../models/course_question.dart';
|
||||
import '../models/course_unit.dart';
|
||||
import '../models/field_option.dart';
|
||||
import '../models/learn_course.dart';
|
||||
|
|
@ -903,7 +905,7 @@ class ApiService {
|
|||
print('Here');
|
||||
|
||||
final Response response = await _service.dio.get(
|
||||
'$kBaseUrl/api/$kApiVersionUrl/$kQuestionSetsUrl/$id/$kQuestionsUrl');
|
||||
'$kBaseUrl/$kApiUrl/$kApiVersionUrl/$kQuestionSetsUrl/$id/$kQuestionsUrl');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
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
|
||||
Future<Map<String, dynamic>> checkUpdate(Map<String, dynamic> data) async {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import '../models/course_catalog.dart';
|
|||
import '../models/course_lesson.dart';
|
||||
import '../models/course_module.dart';
|
||||
import '../models/course_unit.dart';
|
||||
import '../models/refresh_object.dart';
|
||||
import '../ui/common/enmus.dart';
|
||||
|
||||
class CourseService with ListenableServiceMixin {
|
||||
// Dependency injection
|
||||
|
|
@ -76,4 +78,16 @@ class CourseService with ListenableServiceMixin {
|
|||
_lessons.sort((a, b) => (a.sortOrder ?? 0).compareTo(b.sortOrder ?? 0));
|
||||
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>().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 {
|
||||
// Initialize FCM token
|
||||
await updateFCMToken();
|
||||
final _localNotifications = FlutterLocalNotificationsPlugin();
|
||||
|
||||
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
|
||||
Future<void> initialize() async {
|
||||
// Initialize FCM token
|
||||
await updateFCMToken();
|
||||
|
||||
// Request permission
|
||||
await _requestPermission();
|
||||
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
|
||||
|
||||
// setup message handle
|
||||
await _setupMessageHandler();
|
||||
// Request permission
|
||||
await _requestPermission();
|
||||
|
||||
// Subscribe to all devices
|
||||
subscribeToTopic('yimaru');
|
||||
}
|
||||
// setup message handle
|
||||
await _setupMessageHandler();
|
||||
|
||||
Future<void> _requestPermission() async {
|
||||
await _messaging.requestPermission(
|
||||
alert: true,
|
||||
badge: true,
|
||||
sound: true,
|
||||
carPlay: false,
|
||||
provisional: false,
|
||||
announcement: false,
|
||||
criticalAlert: false);
|
||||
}
|
||||
|
||||
Future<void> setupFlutterNotifications() async {
|
||||
if (_isFlutterLocalNotificationInitialized) {
|
||||
return;
|
||||
// Subscribe to all devices
|
||||
subscribeToTopic('yimaru');
|
||||
}
|
||||
|
||||
// Android setup
|
||||
const channel = AndroidNotificationChannel(
|
||||
'yimaru', // id
|
||||
'Yimaru', // title
|
||||
importance: Importance.high,
|
||||
);
|
||||
Future<void> _requestPermission() async {
|
||||
await _messaging.requestPermission(
|
||||
alert: true,
|
||||
badge: true,
|
||||
sound: true,
|
||||
carPlay: false,
|
||||
provisional: false,
|
||||
announcement: false,
|
||||
criticalAlert: false);
|
||||
}
|
||||
|
||||
await _localNotifications
|
||||
.resolvePlatformSpecificImplementation<
|
||||
AndroidFlutterLocalNotificationsPlugin>()
|
||||
?.createNotificationChannel(channel);
|
||||
Future<void> setupFlutterNotifications() async {
|
||||
if (_isFlutterLocalNotificationInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
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)),
|
||||
// Android setup
|
||||
const channel = AndroidNotificationChannel(
|
||||
'yimaru', // id
|
||||
'Yimaru', // title
|
||||
importance: Importance.high,
|
||||
);
|
||||
|
||||
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 kModulesUrl = 'modules';
|
||||
|
||||
String kLessonsUrl = 'lessons';
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ enum LearnPractices { course, module, lesson }
|
|||
// Voice recording state
|
||||
enum VoiceRecordingState { pending, recording }
|
||||
|
||||
// // Levels
|
||||
// enum ProficiencyLevels { a1, a2, b1, b2, none }
|
||||
// Course practice
|
||||
enum CoursePractices { courseCatalog, unit, lesson }
|
||||
|
||||
// Progress status
|
||||
enum ProgressStatuses { pending, started, completed }
|
||||
|
|
@ -67,10 +67,13 @@ enum StateObjects {
|
|||
learnPracticeAnswer,
|
||||
loginWithPhoneNumber,
|
||||
assessmentQuestions,
|
||||
coursePracticeReview,
|
||||
learnPracticeQuestion,
|
||||
completeLearnPractice,
|
||||
coursePracticeQuestion,
|
||||
coursePracticeQuestions,
|
||||
recordLearnPracticeAnswer,
|
||||
finishLearnPracticeQuestion
|
||||
recordCoursePracticeAnswer,
|
||||
finishLearnPracticeQuestion,
|
||||
finishCoursePracticeQuestion
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,24 @@ import 'dart:ui';
|
|||
|
||||
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
|
||||
Map<String, String> splitFullName(String fullName) {
|
||||
final parts = fullName.trim().split(RegExp(r'\s+'));
|
||||
|
|
|
|||
|
|
@ -236,6 +236,11 @@ TextStyle style14B400 = const TextStyle(
|
|||
color: kcBlue,
|
||||
);
|
||||
|
||||
TextStyle style0Ts = const TextStyle(
|
||||
fontSize: 0,
|
||||
color: kcTransparent,
|
||||
);
|
||||
|
||||
TextStyle style14P600 = const TextStyle(
|
||||
color: kcPrimaryColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
|
|
@ -333,11 +338,8 @@ TextStyle style14LG400 = const TextStyle(
|
|||
color: kcLightGrey,
|
||||
);
|
||||
|
||||
TextStyle style12W600 = const TextStyle(
|
||||
fontSize: 12,
|
||||
color: kcWhite,
|
||||
fontWeight: FontWeight.w600
|
||||
);
|
||||
TextStyle style12W600 =
|
||||
const TextStyle(fontSize: 12, color: kcWhite, fontWeight: FontWeight.w600);
|
||||
|
||||
TextStyle style14MG400 = const TextStyle(
|
||||
color: kcMediumGrey,
|
||||
|
|
|
|||
|
|
@ -51,8 +51,8 @@ class CourseView extends StackedView<CourseViewModel> {
|
|||
Widget _buildAppBar(CourseViewModel viewModel) => ProfileAppBar(
|
||||
name: viewModel.user?.firstName,
|
||||
profileImage: viewModel.user?.profilePicture,
|
||||
unreadCount: viewModel.unreadCount.toString(),
|
||||
onTap: () async => await viewModel.navigateToNotification(),
|
||||
unreadCount: viewModel.unreadCount.toString(),
|
||||
onTap: () async => await viewModel.navigateToNotification(),
|
||||
);
|
||||
|
||||
Widget _buildCategoryColumnWrapper(CourseViewModel viewModel) =>
|
||||
|
|
|
|||
|
|
@ -16,10 +16,9 @@ class CourseViewModel extends ReactiveViewModel {
|
|||
|
||||
final _inAppNotificationService = locator<InAppNotificationService>();
|
||||
|
||||
|
||||
@override
|
||||
List<ListenableServiceMixin> get listenableServices =>
|
||||
[_authenticationService,_inAppNotificationService];
|
||||
[_authenticationService, _inAppNotificationService];
|
||||
|
||||
// Current user
|
||||
User? get _user => _authenticationService.user;
|
||||
|
|
@ -55,6 +54,4 @@ class CourseViewModel extends ReactiveViewModel {
|
|||
|
||||
Future<void> navigateToCourseCatalog() async =>
|
||||
await _navigationService.navigateToCourseCatalogView();
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ class CourseModuleViewModel extends ReactiveViewModel {
|
|||
Future<void> navigateToCourseLessonDetail(CourseLesson lesson) async =>
|
||||
await _navigationService.navigateToCourseLessonDetailView(lesson: lesson);
|
||||
|
||||
Future<void> navigateToCoursePractice(int id) async =>
|
||||
await _navigationService.navigateToCoursePracticeView(id: id, practice: CoursePractices.lesson,);
|
||||
// Remote api call
|
||||
|
||||
// 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: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/widgets/listenable_assessment_card.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_assessment_app_bar.dart';
|
||||
import '../duolingo_viewmodel.dart';
|
||||
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||
import '../course_practice_viewmodel.dart';
|
||||
|
||||
class DuolingoListeningAssessment1Question
|
||||
extends ViewModelWidget<DuolingoViewModel> {
|
||||
final TextEditingController assessmentController;
|
||||
class DuolingoListeningPractice1Question
|
||||
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||
final TextEditingController practiceController;
|
||||
|
||||
const DuolingoListeningAssessment1Question(
|
||||
{super.key, required this.assessmentController});
|
||||
const DuolingoListeningPractice1Question(
|
||||
{super.key, required this.practiceController});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
||||
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||
_buildScaffoldWrapper(viewModel);
|
||||
|
||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
||||
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffold(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
||||
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
||||
|
||||
Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding(
|
||||
Widget _buildBodyColumnWrapper(CoursePracticeViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildBodyColumn(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: _buildBodyColumnChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
||||
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||
_buildAppBarWrapper(viewModel),
|
||||
_buildQuestionWrapper(viewModel),
|
||||
_buildContinueButtonWrapper(viewModel)
|
||||
];
|
||||
|
||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||
children: [
|
||||
verticalSpaceMedium,
|
||||
_buildAppBar(viewModel),
|
||||
],
|
||||
);
|
||||
|
||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
||||
title: 'Listening Assessment',
|
||||
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||
DuolingoPracticeAppBar(
|
||||
title: 'Listening practice',
|
||||
onClose: () => viewModel.goTo(0),
|
||||
);
|
||||
|
||||
Widget _buildQuestionWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildQuestionWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: _buildQuestionChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildQuestionChildren(DuolingoViewModel viewModel) => [
|
||||
List<Widget> _buildQuestionChildren(CoursePracticeViewModel viewModel) => [
|
||||
_buildTitle(),
|
||||
verticalSpaceMedium,
|
||||
_buildQuestion(),
|
||||
verticalSpaceLarge,
|
||||
_buildLabel(),
|
||||
verticalSpaceSmall,
|
||||
_buildAssessmentFormField(viewModel),
|
||||
if (viewModel.hasAssessmentValidationMessage &&
|
||||
viewModel.focusAssessment)
|
||||
_buildPracticeFormField(viewModel),
|
||||
if (viewModel.hasPracticeValidationMessage &&
|
||||
viewModel.focusPractice)
|
||||
verticalSpaceTiny,
|
||||
if (viewModel.hasAssessmentValidationMessage &&
|
||||
viewModel.focusAssessment)
|
||||
_buildAssessmentWrapper(viewModel),
|
||||
if (viewModel.hasPracticeValidationMessage &&
|
||||
viewModel.focusPractice)
|
||||
_buildPracticeWrapper(viewModel),
|
||||
];
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
|
|
@ -85,7 +87,7 @@ class DuolingoListeningAssessment1Question
|
|||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
||||
Widget _buildQuestion() => const ListenableAssessmentCard();
|
||||
Widget _buildQuestion() => const ListenablePracticeCard();
|
||||
|
||||
Widget _buildLabel() => Text(
|
||||
'Your Answer',
|
||||
|
|
@ -93,34 +95,35 @@ class DuolingoListeningAssessment1Question
|
|||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
||||
Widget _buildAssessmentFormField(DuolingoViewModel viewModel) =>
|
||||
Widget _buildPracticeFormField(CoursePracticeViewModel viewModel) =>
|
||||
TextFormField(
|
||||
maxLines: 5,
|
||||
maxLength: 250,
|
||||
controller: assessmentController,
|
||||
onTap: viewModel.setAssessmentFocus,
|
||||
controller: practiceController,
|
||||
onTap: viewModel.setPracticeFocus,
|
||||
decoration: inputDecoration(
|
||||
focus: true,
|
||||
hint: 'Start writing here...',
|
||||
filled: assessmentController.text.isNotEmpty),
|
||||
filled: practiceController.text.isNotEmpty),
|
||||
);
|
||||
|
||||
Widget _buildAssessmentWrapper(DuolingoViewModel viewModel) =>
|
||||
viewModel.hasAssessmentValidationMessage
|
||||
? _buildAssessmentValidator(viewModel)
|
||||
Widget _buildPracticeWrapper(CoursePracticeViewModel viewModel) =>
|
||||
viewModel.hasPracticeValidationMessage
|
||||
? _buildPracticeValidator(viewModel)
|
||||
: Container();
|
||||
|
||||
Widget _buildAssessmentValidator(DuolingoViewModel viewModel) => Text(
|
||||
viewModel.assessmentValidationMessage!,
|
||||
Widget _buildPracticeValidator(CoursePracticeViewModel viewModel) => Text(
|
||||
viewModel.practiceValidationMessage!,
|
||||
style: style12R700,
|
||||
);
|
||||
|
||||
Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding(
|
||||
Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) =>
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 50),
|
||||
child: _buildContinueButton(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildContinueButton(DuolingoViewModel viewModel) =>
|
||||
Widget _buildContinueButton(CoursePracticeViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
text: 'Submit',
|
||||
|
|
@ -1,48 +1,48 @@
|
|||
import 'package:flutter/material.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/ui_helpers.dart';
|
||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
||||
import '../../../widgets/listenable_assessment_card.dart';
|
||||
import '../duolingo_viewmodel.dart';
|
||||
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||
import '../../../widgets/listenable_practice_card.dart';
|
||||
import '../course_practice_viewmodel.dart';
|
||||
|
||||
class DuolingoListeningAssessment1Review
|
||||
extends ViewModelWidget<DuolingoViewModel> {
|
||||
final TextEditingController assessmentController;
|
||||
class DuolingoListeningPractice1Review
|
||||
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||
final TextEditingController practiceController;
|
||||
|
||||
const DuolingoListeningAssessment1Review(
|
||||
{super.key, required this.assessmentController});
|
||||
const DuolingoListeningPractice1Review(
|
||||
{super.key, required this.practiceController});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
||||
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||
_buildScaffoldWrapper(viewModel);
|
||||
|
||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
||||
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffold(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
||||
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||
SafeArea(child: _buildBodyColumn(viewModel));
|
||||
|
||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: _buildBodyColumnChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
||||
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||
_buildAppBarIndenter(viewModel),
|
||||
_buildExpandedBody(viewModel),
|
||||
];
|
||||
|
||||
Widget _buildAppBarIndenter(DuolingoViewModel viewModel) => Padding(
|
||||
Widget _buildAppBarIndenter(CoursePracticeViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildAppBarWrapper(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||
children: [
|
||||
verticalSpaceMedium,
|
||||
_buildAppBar(viewModel),
|
||||
|
|
@ -50,20 +50,22 @@ class DuolingoListeningAssessment1Review
|
|||
],
|
||||
);
|
||||
|
||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
||||
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||
DuolingoPracticeAppBar(
|
||||
title: 'Feedback',
|
||||
onClose: () => viewModel.goTo(0),
|
||||
);
|
||||
|
||||
Widget _buildExpandedBody(DuolingoViewModel viewModel) =>
|
||||
Widget _buildExpandedBody(CoursePracticeViewModel viewModel) =>
|
||||
Expanded(child: _buildBodyScroller(viewModel));
|
||||
|
||||
Widget _buildBodyScroller(DuolingoViewModel viewModel) =>
|
||||
Widget _buildBodyScroller(CoursePracticeViewModel viewModel) =>
|
||||
SingleChildScrollView(
|
||||
child: _buildQuestionSectionWrapper(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildQuestionSectionWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildQuestionSectionWrapper(CoursePracticeViewModel viewModel) =>
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
|
@ -71,7 +73,7 @@ class DuolingoListeningAssessment1Review
|
|||
);
|
||||
|
||||
List<Widget> _buildQuestionQuestionSectionChildren(
|
||||
DuolingoViewModel viewModel) =>
|
||||
CoursePracticeViewModel viewModel) =>
|
||||
[
|
||||
_buildTitleWrapper(),
|
||||
verticalSpaceMedium,
|
||||
|
|
@ -100,7 +102,7 @@ class DuolingoListeningAssessment1Review
|
|||
child: _buildQuestion(),
|
||||
);
|
||||
|
||||
Widget _buildQuestion() => const ListenableAssessmentCard();
|
||||
Widget _buildQuestion() => const ListenablePracticeCard();
|
||||
|
||||
Widget _buildLabelWrapper() => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
|
|
@ -113,24 +115,24 @@ class DuolingoListeningAssessment1Review
|
|||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
||||
Widget _buildAssessmentFormFieldWrapper(DuolingoViewModel viewModel) =>
|
||||
Widget _buildAssessmentFormFieldWrapper(CoursePracticeViewModel viewModel) =>
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildAssessmentFormField(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildAssessmentFormField(DuolingoViewModel viewModel) =>
|
||||
Widget _buildAssessmentFormField(CoursePracticeViewModel viewModel) =>
|
||||
TextFormField(
|
||||
maxLines: 5,
|
||||
maxLength: 250,
|
||||
controller: assessmentController,
|
||||
onTap: viewModel.setAssessmentFocus,
|
||||
controller: practiceController,
|
||||
onTap: viewModel.setPracticeFocus,
|
||||
decoration: inputDecoration(
|
||||
focus: true,
|
||||
hint: 'Start writing here...',
|
||||
filled: assessmentController.text.isNotEmpty),
|
||||
filled: practiceController.text.isNotEmpty),
|
||||
);
|
||||
|
||||
Widget _buildAssessmentReviewSection(DuolingoViewModel viewModel) =>
|
||||
DuolingoAssessmentReviewSection(onTap: () => viewModel.goTo(5));
|
||||
Widget _buildAssessmentReviewSection(CoursePracticeViewModel viewModel) =>
|
||||
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: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/ui_helpers.dart';
|
||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
||||
import '../../../widgets/listenable_assessment_card.dart';
|
||||
import '../duolingo_viewmodel.dart';
|
||||
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||
import '../../../widgets/listenable_practice_card.dart';
|
||||
import '../course_practice_viewmodel.dart';
|
||||
|
||||
class DuolingoListeningAssessment2Review
|
||||
extends ViewModelWidget<DuolingoViewModel> {
|
||||
final TextEditingController assessmentController;
|
||||
class DuolingoListeningPractice2Review
|
||||
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||
final TextEditingController practiceController;
|
||||
|
||||
const DuolingoListeningAssessment2Review(
|
||||
{super.key, required this.assessmentController});
|
||||
const DuolingoListeningPractice2Review(
|
||||
{super.key, required this.practiceController});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
||||
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||
_buildScaffoldWrapper(viewModel);
|
||||
|
||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
||||
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffold(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
||||
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||
SafeArea(child: _buildBodyColumn(viewModel));
|
||||
|
||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: _buildBodyColumnChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
||||
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||
_buildAppBarIndenter(viewModel),
|
||||
_buildExpandedBody(viewModel),
|
||||
];
|
||||
|
||||
Widget _buildAppBarIndenter(DuolingoViewModel viewModel) => Padding(
|
||||
Widget _buildAppBarIndenter(CoursePracticeViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildAppBarWrapper(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||
children: [
|
||||
verticalSpaceMedium,
|
||||
_buildAppBar(viewModel),
|
||||
|
|
@ -50,20 +50,22 @@ class DuolingoListeningAssessment2Review
|
|||
],
|
||||
);
|
||||
|
||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
||||
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||
DuolingoPracticeAppBar(
|
||||
title: 'Feedback',
|
||||
onClose: () => viewModel.goTo(0),
|
||||
);
|
||||
|
||||
Widget _buildExpandedBody(DuolingoViewModel viewModel) =>
|
||||
Widget _buildExpandedBody(CoursePracticeViewModel viewModel) =>
|
||||
Expanded(child: _buildBodyScroller(viewModel));
|
||||
|
||||
Widget _buildBodyScroller(DuolingoViewModel viewModel) =>
|
||||
Widget _buildBodyScroller(CoursePracticeViewModel viewModel) =>
|
||||
SingleChildScrollView(
|
||||
child: _buildQuestionSectionWrapper(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildQuestionSectionWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildQuestionSectionWrapper(CoursePracticeViewModel viewModel) =>
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
|
@ -71,7 +73,7 @@ class DuolingoListeningAssessment2Review
|
|||
);
|
||||
|
||||
List<Widget> _buildQuestionQuestionSectionChildren(
|
||||
DuolingoViewModel viewModel) =>
|
||||
CoursePracticeViewModel viewModel) =>
|
||||
[
|
||||
_buildTitleWrapper(),
|
||||
verticalSpaceMedium,
|
||||
|
|
@ -79,17 +81,17 @@ class DuolingoListeningAssessment2Review
|
|||
verticalSpaceLarge,
|
||||
_buildLabelWrapper('1. Focus of the conference:'),
|
||||
verticalSpaceTiny,
|
||||
_buildAssessmentFormFieldWrapper(viewModel),
|
||||
_buildPracticeFormFieldWrapper(viewModel),
|
||||
verticalSpaceSmall,
|
||||
_buildLabelWrapper('2. Number of presentations:'),
|
||||
verticalSpaceTiny,
|
||||
_buildAssessmentFormFieldWrapper(viewModel),
|
||||
_buildPracticeFormFieldWrapper(viewModel),
|
||||
verticalSpaceSmall,
|
||||
_buildLabelWrapper('3. Keynote speaker’s field:'),
|
||||
verticalSpaceTiny,
|
||||
_buildAssessmentFormFieldWrapper(viewModel),
|
||||
_buildPracticeFormFieldWrapper(viewModel),
|
||||
verticalSpaceLarge,
|
||||
_buildAssessmentReviewSection(viewModel)
|
||||
_buildPracticeReviewSection(viewModel)
|
||||
];
|
||||
|
||||
Widget _buildTitleWrapper() => Padding(
|
||||
|
|
@ -108,7 +110,7 @@ class DuolingoListeningAssessment2Review
|
|||
child: _buildQuestion(),
|
||||
);
|
||||
|
||||
Widget _buildQuestion() => const ListenableAssessmentCard();
|
||||
Widget _buildQuestion() => const ListenablePracticeCard();
|
||||
|
||||
Widget _buildLabelWrapper(String question) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
|
|
@ -121,22 +123,22 @@ class DuolingoListeningAssessment2Review
|
|||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
||||
Widget _buildAssessmentFormFieldWrapper(DuolingoViewModel viewModel) =>
|
||||
Widget _buildPracticeFormFieldWrapper(CoursePracticeViewModel viewModel) =>
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildAssessmentFormField(viewModel),
|
||||
child: _buildPracticeFormField(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildAssessmentFormField(DuolingoViewModel viewModel) =>
|
||||
Widget _buildPracticeFormField(CoursePracticeViewModel viewModel) =>
|
||||
TextFormField(
|
||||
controller: assessmentController,
|
||||
onTap: viewModel.setAssessmentFocus,
|
||||
controller: practiceController,
|
||||
onTap: viewModel.setPracticeFocus,
|
||||
decoration: inputDecoration(
|
||||
focus: true,
|
||||
hint: 'Start writing here...',
|
||||
filled: assessmentController.text.isNotEmpty),
|
||||
filled: practiceController.text.isNotEmpty),
|
||||
);
|
||||
|
||||
Widget _buildAssessmentReviewSection(DuolingoViewModel viewModel) =>
|
||||
DuolingoAssessmentReviewSection(onTap: () => viewModel.goTo(5));
|
||||
Widget _buildPracticeReviewSection(CoursePracticeViewModel viewModel) =>
|
||||
DuolingoPracticeReviewSection(onTap: () => viewModel.goTo(5));
|
||||
}
|
||||
|
|
@ -1,85 +1,86 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.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/ui_helpers.dart';
|
||||
import '../../../widgets/custom_elevated_button.dart';
|
||||
import '../../../widgets/custom_small_radio_button.dart';
|
||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
||||
import '../duolingo_viewmodel.dart';
|
||||
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||
import '../course_practice_viewmodel.dart';
|
||||
|
||||
class DuolingoListeningAssessment3Question
|
||||
extends ViewModelWidget<DuolingoViewModel> {
|
||||
final TextEditingController assessmentController;
|
||||
class DuolingoListeningPractice3Question
|
||||
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||
final TextEditingController practiceController;
|
||||
|
||||
const DuolingoListeningAssessment3Question(
|
||||
{super.key, required this.assessmentController});
|
||||
const DuolingoListeningPractice3Question(
|
||||
{super.key, required this.practiceController});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
||||
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||
_buildScaffoldWrapper(viewModel);
|
||||
|
||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
||||
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffold(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
||||
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||
SafeArea(child: _buildBodyColumn(viewModel));
|
||||
|
||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: _buildBodyColumnChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
||||
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||
_buildAppBarWrapper(viewModel),
|
||||
_buildExpandedBody(viewModel),
|
||||
];
|
||||
|
||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: _buildAppBarChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildAppBarChildren(DuolingoViewModel viewModel) => [
|
||||
List<Widget> _buildAppBarChildren(CoursePracticeViewModel viewModel) => [
|
||||
verticalSpaceMedium,
|
||||
_buildAppBarIndenter(viewModel),
|
||||
];
|
||||
|
||||
Widget _buildAppBarIndenter(DuolingoViewModel viewModel) => Padding(
|
||||
Widget _buildAppBarIndenter(CoursePracticeViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildAppBar(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
||||
title: 'Listening Assessment',
|
||||
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||
DuolingoPracticeAppBar(
|
||||
title: 'Listening practice',
|
||||
onClose: () => viewModel.goTo(0),
|
||||
);
|
||||
|
||||
Widget _buildExpandedBody(DuolingoViewModel viewModel) =>
|
||||
Widget _buildExpandedBody(CoursePracticeViewModel viewModel) =>
|
||||
Expanded(child: _buildBodyScroller(viewModel));
|
||||
|
||||
Widget _buildBodyScroller(DuolingoViewModel viewModel) =>
|
||||
Widget _buildBodyScroller(CoursePracticeViewModel viewModel) =>
|
||||
SingleChildScrollView(
|
||||
child: _buildQuestionIndenter(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildQuestionIndenter(DuolingoViewModel viewModel) => Padding(
|
||||
Widget _buildQuestionIndenter(CoursePracticeViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildQuestionWrapper(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildQuestionWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildQuestionWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: _buildQuestionChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildQuestionChildren(DuolingoViewModel viewModel) => [
|
||||
List<Widget> _buildQuestionChildren(CoursePracticeViewModel viewModel) => [
|
||||
verticalSpaceLarge,
|
||||
_buildTitle(),
|
||||
verticalSpaceMedium,
|
||||
|
|
@ -87,7 +88,7 @@ class DuolingoListeningAssessment3Question
|
|||
verticalSpaceLarge,
|
||||
_buildLabel(),
|
||||
verticalSpaceTiny,
|
||||
_buildAssessmentQuestions(viewModel),
|
||||
// _buildPracticeQuestions(viewModel),
|
||||
verticalSpaceMedium,
|
||||
_buildContinueButtonWrapper(viewModel)
|
||||
];
|
||||
|
|
@ -99,10 +100,10 @@ class DuolingoListeningAssessment3Question
|
|||
);
|
||||
|
||||
Widget _buildQuestion() =>
|
||||
DwarfTile(small: true, child: _buildListenableAssessmentCard());
|
||||
DwarfTile(small: true, child: _buildListenablePracticeCard());
|
||||
|
||||
Widget _buildListenableAssessmentCard() =>
|
||||
const Expanded(child: ListenableAssessmentCard());
|
||||
Widget _buildListenablePracticeCard() =>
|
||||
const Expanded(child: ListenablePracticeCard());
|
||||
|
||||
Widget _buildLabel() => Text(
|
||||
'Select the best response',
|
||||
|
|
@ -110,21 +111,21 @@ class DuolingoListeningAssessment3Question
|
|||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
||||
Widget _buildAssessmentQuestions(DuolingoViewModel viewModel) =>
|
||||
ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: viewModel.listeningAssessments.length,
|
||||
itemBuilder: (context, index) => _buildAssessmentCard(
|
||||
title: viewModel.listeningAssessments[index],
|
||||
selected: viewModel.isSelectedListeningAssessment(
|
||||
viewModel.listeningAssessments[index]),
|
||||
onTap: () => viewModel.setSelectedListeningAssessment(
|
||||
viewModel.listeningAssessments[index]),
|
||||
),
|
||||
);
|
||||
// Widget _buildPracticeQuestions(CoursePracticeViewModel viewModel) =>
|
||||
// ListView.builder(
|
||||
// shrinkWrap: true,
|
||||
// physics: const NeverScrollableScrollPhysics(),
|
||||
// itemCount: viewModel.questions.length,
|
||||
// itemBuilder: (context, index) => _buildAssessmentCard(
|
||||
// title: viewModel.listeningAssessments[index],
|
||||
// selected: viewModel.isSelectedListeningAssessment(
|
||||
// viewModel.listeningAssessments[index]),
|
||||
// onTap: () => viewModel.setSelectedListeningAssessment(
|
||||
// viewModel.listeningAssessments[index]),
|
||||
// ),
|
||||
// );
|
||||
|
||||
Widget _buildAssessmentCard(
|
||||
Widget _buildPracticeCard(
|
||||
{required String title,
|
||||
required bool selected,
|
||||
required GestureTapCallback onTap}) =>
|
||||
|
|
@ -134,12 +135,13 @@ class DuolingoListeningAssessment3Question
|
|||
selected: selected,
|
||||
);
|
||||
|
||||
Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding(
|
||||
Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) =>
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 50),
|
||||
child: _buildContinueButton(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildContinueButton(DuolingoViewModel viewModel) =>
|
||||
Widget _buildContinueButton(CoursePracticeViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
text: 'Submit',
|
||||
|
|
@ -3,31 +3,32 @@ import 'package:stacked/stacked.dart';
|
|||
|
||||
import '../../../common/app_colors.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 '../duolingo_viewmodel.dart';
|
||||
import '../course_practice_viewmodel.dart';
|
||||
|
||||
class DuolingoAssessmentsScreens extends ViewModelWidget<DuolingoViewModel> {
|
||||
const DuolingoAssessmentsScreens({super.key});
|
||||
class DuolingoPracticesScreens
|
||||
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||
const DuolingoPracticesScreens({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
||||
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||
_buildScaffoldWrapper(viewModel);
|
||||
|
||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
||||
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffold(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
||||
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||
SafeArea(child: _buildBody(viewModel));
|
||||
|
||||
Widget _buildBody(DuolingoViewModel viewModel) => Padding(
|
||||
Widget _buildBody(CoursePracticeViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildColumn(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildColumn(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildColumn(CoursePracticeViewModel viewModel) => Column(
|
||||
children: [
|
||||
verticalSpaceMedium,
|
||||
_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,
|
||||
);
|
||||
|
||||
Widget _buildPracticeColumnWrapper(DuolingoViewModel viewModel) =>
|
||||
Widget _buildPracticeColumnWrapper(CoursePracticeViewModel viewModel) =>
|
||||
Expanded(child: _buildPracticeColumnScrollView(viewModel));
|
||||
|
||||
Widget _buildPracticeColumnScrollView(DuolingoViewModel viewModel) =>
|
||||
Widget _buildPracticeColumnScrollView(CoursePracticeViewModel viewModel) =>
|
||||
SingleChildScrollView(
|
||||
child: _buildPracticeColumn(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildPracticeColumn(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildPracticeColumn(CoursePracticeViewModel viewModel) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: _buildPracticeColumnChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildPracticeColumnChildren(DuolingoViewModel viewModel) => [
|
||||
List<Widget> _buildPracticeColumnChildren(
|
||||
CoursePracticeViewModel viewModel) =>
|
||||
[
|
||||
verticalSpaceMedium,
|
||||
_buildTitle(),
|
||||
_buildSubtitle(),
|
||||
|
|
@ -72,14 +75,15 @@ class DuolingoAssessmentsScreens extends ViewModelWidget<DuolingoViewModel> {
|
|||
style: style14DG400,
|
||||
);
|
||||
|
||||
Widget _buildListView(DuolingoViewModel viewModel) => GridView.builder(
|
||||
Widget _buildListView(CoursePracticeViewModel viewModel) => GridView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: viewModel.assessments.length,
|
||||
itemCount: viewModel.practices.length,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) => _buildCard(
|
||||
title: viewModel.assessments[index]['label'],
|
||||
onTap: () => viewModel.setSelectedAssessment(
|
||||
page: 1, assessment: viewModel.assessments[index])),
|
||||
title: viewModel.practices[index].title ?? '', onTap: () {}
|
||||
// onTap: () => viewModel.setSelectedAssessment(
|
||||
// page: 1, assessment: viewModel.assessments[index])
|
||||
),
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
mainAxisSpacing: 15,
|
||||
|
|
@ -92,7 +96,7 @@ class DuolingoAssessmentsScreens extends ViewModelWidget<DuolingoViewModel> {
|
|||
required String title,
|
||||
required GestureTapCallback onTap,
|
||||
}) =>
|
||||
DuolingoAssessmentCard(
|
||||
DuolingoPracticeCard(
|
||||
title: title,
|
||||
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: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/ui_helpers.dart';
|
||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
||||
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||
import '../../../widgets/countdown_timer.dart';
|
||||
import '../../../widgets/custom_elevated_button.dart';
|
||||
import '../../../widgets/speaking_indicator.dart';
|
||||
import '../../../widgets/speaking_label.dart';
|
||||
import '../duolingo_viewmodel.dart';
|
||||
import '../course_practice_viewmodel.dart';
|
||||
|
||||
class DuolingoSpeakingAssessment2Answer
|
||||
extends ViewModelWidget<DuolingoViewModel> {
|
||||
const DuolingoSpeakingAssessment2Answer({super.key});
|
||||
class DuolingoSpeakingPractice2Answer
|
||||
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||
const DuolingoSpeakingPractice2Answer({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
||||
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||
_buildScaffoldWrapper(viewModel);
|
||||
|
||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
||||
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffold(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
||||
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
||||
|
||||
Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding(
|
||||
Widget _buildBodyColumnWrapper(CoursePracticeViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildBodyColumn(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: _buildBodyColumnChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
||||
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||
_buildAppBarWrapper(viewModel),
|
||||
_buildSpeakingIndicatorWrapper(viewModel),
|
||||
_buildContinueButtonWrapper(viewModel)
|
||||
];
|
||||
|
||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||
children: [
|
||||
verticalSpaceMedium,
|
||||
_buildAppBar(viewModel),
|
||||
|
|
@ -52,12 +52,14 @@ class DuolingoSpeakingAssessment2Answer
|
|||
],
|
||||
);
|
||||
|
||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
||||
title: 'Speaking Assessment',
|
||||
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||
DuolingoPracticeAppBar(
|
||||
title: 'Speaking practice',
|
||||
onClose: () => viewModel.goTo(0),
|
||||
);
|
||||
|
||||
Widget _buildSpeakingIndicatorWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildSpeakingIndicatorWrapper(CoursePracticeViewModel viewModel) =>
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: _buildSpeakingIndicatorChildren(),
|
||||
|
|
@ -89,7 +91,7 @@ class DuolingoSpeakingAssessment2Answer
|
|||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
||||
Widget _buildQuestion() => const DuolingoAssessmentQuestionCard(
|
||||
Widget _buildQuestion() => const DuolingoPracticeQuestionCard(
|
||||
title: 'How has growing up in your hometown influenced you?',
|
||||
subtitle:
|
||||
'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,
|
||||
);
|
||||
|
||||
Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding(
|
||||
Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) =>
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 50),
|
||||
child: _buildContinueButton(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildContinueButton(DuolingoViewModel viewModel) =>
|
||||
Widget _buildContinueButton(CoursePracticeViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
text: 'Submit',
|
||||
|
|
@ -1,17 +1,17 @@
|
|||
import 'package:flutter/material.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/ui_helpers.dart';
|
||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
||||
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||
import '../../../widgets/countdown_timer.dart';
|
||||
import '../../../widgets/custom_elevated_button.dart';
|
||||
import '../duolingo_viewmodel.dart';
|
||||
import '../../duolingo/duolingo_viewmodel.dart';
|
||||
|
||||
class DuolingoSpeakingAssessment2Question
|
||||
class DuolingoSpeakingPractice2Question
|
||||
extends ViewModelWidget<DuolingoViewModel> {
|
||||
const DuolingoSpeakingAssessment2Question({
|
||||
const DuolingoSpeakingPractice2Question({
|
||||
super.key,
|
||||
});
|
||||
|
||||
|
|
@ -52,8 +52,8 @@ class DuolingoSpeakingAssessment2Question
|
|||
],
|
||||
);
|
||||
|
||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
||||
title: 'Speaking Assessment',
|
||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoPracticeAppBar(
|
||||
title: 'Speaking practice',
|
||||
onClose: () => viewModel.goTo(0),
|
||||
);
|
||||
|
||||
|
|
@ -85,7 +85,7 @@ class DuolingoSpeakingAssessment2Question
|
|||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
||||
Widget _buildQuestion() => const DuolingoAssessmentQuestionCard(
|
||||
Widget _buildQuestion() => const DuolingoPracticeQuestionCard(
|
||||
title: 'How has growing up in your hometown influenced you?',
|
||||
subtitle:
|
||||
'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/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';
|
||||
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 DuolingoSpeakingAssessment2Review
|
||||
extends ViewModelWidget<DuolingoViewModel> {
|
||||
const DuolingoSpeakingAssessment2Review({super.key});
|
||||
class DuolingoSpeakingPractice2Review
|
||||
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||
const DuolingoSpeakingPractice2Review({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
||||
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||
_buildScaffoldWrapper(viewModel);
|
||||
|
||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
||||
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffold(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
||||
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||
SafeArea(child: _buildBodyColumn(viewModel));
|
||||
|
||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: _buildBodyColumnChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
||||
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||
_buildAppBarIndenter(viewModel),
|
||||
_buildImageSectionIndenter(viewModel),
|
||||
_buildAssessmentReviewSection(viewModel)
|
||||
_buildPracticeReviewSection(viewModel)
|
||||
];
|
||||
|
||||
Widget _buildAppBarIndenter(DuolingoViewModel viewModel) => Padding(
|
||||
Widget _buildAppBarIndenter(CoursePracticeViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildAppBarWrapper(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||
children: [
|
||||
verticalSpaceMedium,
|
||||
_buildAppBar(viewModel),
|
||||
|
|
@ -50,17 +50,19 @@ class DuolingoSpeakingAssessment2Review
|
|||
],
|
||||
);
|
||||
|
||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
||||
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||
DuolingoPracticeAppBar(
|
||||
title: 'Feedback',
|
||||
onClose: () => viewModel.goTo(0),
|
||||
);
|
||||
|
||||
Widget _buildImageSectionIndenter(DuolingoViewModel viewModel) => Padding(
|
||||
Widget _buildImageSectionIndenter(CoursePracticeViewModel viewModel) =>
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildImageSectionWrapper(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildImageSectionWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildImageSectionWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: _buildImageSectionChildren(),
|
||||
|
|
@ -88,12 +90,12 @@ class DuolingoSpeakingAssessment2Review
|
|||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
||||
Widget _buildQuestion() => const DuolingoAssessmentQuestionCard(
|
||||
Widget _buildQuestion() => const DuolingoPracticeQuestionCard(
|
||||
title: 'How has growing up in your hometown influenced you?',
|
||||
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));
|
||||
Widget _buildPracticeReviewSection(CoursePracticeViewModel viewModel) =>
|
||||
SpeakingPracticeReviewSection(onTap: () => viewModel.goTo(5));
|
||||
}
|
||||
|
|
@ -1,19 +1,19 @@
|
|||
import 'package:flutter/material.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/ui_helpers.dart';
|
||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
||||
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||
import '../../../widgets/countdown_timer.dart';
|
||||
import '../../../widgets/custom_elevated_button.dart';
|
||||
import '../../../widgets/speaking_indicator.dart';
|
||||
import '../../../widgets/speaking_label.dart';
|
||||
import '../duolingo_viewmodel.dart';
|
||||
import '../../duolingo/duolingo_viewmodel.dart';
|
||||
|
||||
class DuolingoSpeakingAssessment3Answer
|
||||
class DuolingoSpeakingPractice3Answer
|
||||
extends ViewModelWidget<DuolingoViewModel> {
|
||||
const DuolingoSpeakingAssessment3Answer({super.key});
|
||||
const DuolingoSpeakingPractice3Answer({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
||||
|
|
@ -52,8 +52,8 @@ class DuolingoSpeakingAssessment3Answer
|
|||
],
|
||||
);
|
||||
|
||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
||||
title: 'Speaking Assessment',
|
||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoPracticeAppBar(
|
||||
title: 'Speaking practice',
|
||||
onClose: () => viewModel.goTo(0),
|
||||
);
|
||||
|
||||
|
|
@ -74,7 +74,7 @@ class DuolingoSpeakingAssessment3Answer
|
|||
Widget _buildCountdownWrapper() =>
|
||||
const Align(alignment: Alignment.centerRight, child: CountdownTimer());
|
||||
|
||||
Widget _buildQuestion() => const DuolingoAssessmentQuestionCard(
|
||||
Widget _buildQuestion() => const DuolingoPracticeQuestionCard(
|
||||
subtitle:
|
||||
'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: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/ui_helpers.dart';
|
||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
||||
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||
import '../../../widgets/countdown_timer.dart';
|
||||
import '../../../widgets/custom_elevated_button.dart';
|
||||
import '../duolingo_viewmodel.dart';
|
||||
import '../course_practice_viewmodel.dart';
|
||||
|
||||
class DuolingoSpeakingAssessment3Question
|
||||
extends ViewModelWidget<DuolingoViewModel> {
|
||||
const DuolingoSpeakingAssessment3Question({
|
||||
class DuolingoSpeakingPractice3Question
|
||||
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||
const DuolingoSpeakingPractice3Question({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
||||
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||
_buildScaffoldWrapper(viewModel);
|
||||
|
||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
||||
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffold(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
||||
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
||||
|
||||
Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding(
|
||||
Widget _buildBodyColumnWrapper(CoursePracticeViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildBodyColumn(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: _buildBodyColumnChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
||||
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||
_buildAppBarWrapper(viewModel),
|
||||
_buildSpeakingIndicatorWrapper(viewModel),
|
||||
_buildContinueButtonWrapper(viewModel)
|
||||
];
|
||||
|
||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||
children: [
|
||||
verticalSpaceMedium,
|
||||
_buildAppBar(viewModel),
|
||||
|
|
@ -52,12 +52,14 @@ class DuolingoSpeakingAssessment3Question
|
|||
],
|
||||
);
|
||||
|
||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
||||
title: 'Speaking Assessment',
|
||||
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||
DuolingoPracticeAppBar(
|
||||
title: 'Speaking practice',
|
||||
onClose: () => viewModel.goTo(0),
|
||||
);
|
||||
|
||||
Widget _buildSpeakingIndicatorWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildSpeakingIndicatorWrapper(CoursePracticeViewModel viewModel) =>
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: _buildSpeakingIndicatorChildren(),
|
||||
|
|
@ -85,17 +87,18 @@ class DuolingoSpeakingAssessment3Question
|
|||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
||||
Widget _buildQuestion() => const DuolingoAssessmentQuestionCard(
|
||||
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 _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding(
|
||||
Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) =>
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 50),
|
||||
child: _buildContinueButton(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildContinueButton(DuolingoViewModel viewModel) =>
|
||||
Widget _buildContinueButton(CoursePracticeViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
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/ui_helpers.dart';
|
||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
||||
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||
import '../../../widgets/countdown_timer.dart';
|
||||
import '../../../widgets/custom_elevated_button.dart';
|
||||
import '../../../widgets/speaking_indicator.dart';
|
||||
import '../../../widgets/speaking_label.dart';
|
||||
import '../duolingo_viewmodel.dart';
|
||||
import '../course_practice_viewmodel.dart';
|
||||
|
||||
class DuolingoSpeakingAssessment4Answer
|
||||
extends ViewModelWidget<DuolingoViewModel> {
|
||||
const DuolingoSpeakingAssessment4Answer({super.key});
|
||||
class DuolingoSpeakingPractice4Answer
|
||||
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||
const DuolingoSpeakingPractice4Answer({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
||||
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||
_buildScaffoldWrapper(viewModel);
|
||||
|
||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
||||
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffold(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
||||
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
||||
|
||||
Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding(
|
||||
Widget _buildBodyColumnWrapper(CoursePracticeViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildBodyColumn(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: _buildBodyColumnChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
||||
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||
_buildAppBarWrapper(viewModel),
|
||||
_buildAnswerSectionWrapper(viewModel),
|
||||
_buildContinueButtonWrapper(viewModel)
|
||||
];
|
||||
|
||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||
children: [
|
||||
verticalSpaceMedium,
|
||||
_buildAppBar(viewModel),
|
||||
|
|
@ -52,15 +52,17 @@ class DuolingoSpeakingAssessment4Answer
|
|||
],
|
||||
);
|
||||
|
||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
||||
title: 'Speaking Assessment',
|
||||
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||
DuolingoPracticeAppBar(
|
||||
title: 'Speaking practice',
|
||||
onClose: () => viewModel.goTo(0),
|
||||
);
|
||||
|
||||
Widget _buildCountdownWrapper() =>
|
||||
const Align(alignment: Alignment.centerRight, child: CountdownTimer());
|
||||
|
||||
Widget _buildAnswerSectionWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildAnswerSectionWrapper(CoursePracticeViewModel viewModel) =>
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: _buildAnswerSectionChildren(),
|
||||
|
|
@ -96,12 +98,13 @@ class DuolingoSpeakingAssessment4Answer
|
|||
color: kcWhite,
|
||||
);
|
||||
|
||||
Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding(
|
||||
Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) =>
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 50),
|
||||
child: _buildContinueButton(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildContinueButton(DuolingoViewModel viewModel) =>
|
||||
Widget _buildContinueButton(CoursePracticeViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
text: 'Submit',
|
||||
|
|
@ -4,17 +4,17 @@ import 'package:yimaru_app/ui/widgets/dwarf_tile.dart';
|
|||
|
||||
import '../../../common/app_colors.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/custom_elevated_button.dart';
|
||||
import '../../../widgets/speaking_indicator.dart';
|
||||
import '../duolingo_viewmodel.dart';
|
||||
import '../course_practice_viewmodel.dart';
|
||||
|
||||
class DuolingoSpeakingAssessment4Question
|
||||
extends ViewModelWidget<DuolingoViewModel> {
|
||||
const DuolingoSpeakingAssessment4Question({super.key});
|
||||
class DuolingoSpeakingPractice4Question
|
||||
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||
const DuolingoSpeakingPractice4Question({super.key});
|
||||
|
||||
void _goTo(DuolingoViewModel viewModel) {
|
||||
void _goTo(CoursePracticeViewModel viewModel) {
|
||||
viewModel.setSpeakingState();
|
||||
if (!viewModel.isSpeaking) {
|
||||
viewModel.goTo(3);
|
||||
|
|
@ -22,34 +22,34 @@ class DuolingoSpeakingAssessment4Question
|
|||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
||||
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||
_buildScaffoldWrapper(viewModel);
|
||||
|
||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
||||
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffold(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
||||
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
||||
|
||||
Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding(
|
||||
Widget _buildBodyColumnWrapper(CoursePracticeViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildBodyColumn(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: _buildBodyColumnChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
||||
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||
_buildAppBarWrapper(viewModel),
|
||||
_buildSpeakingIndicatorState(viewModel),
|
||||
_buildContinueButtonWrapper(viewModel)
|
||||
];
|
||||
|
||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||
children: [
|
||||
verticalSpaceMedium,
|
||||
_buildAppBar(viewModel),
|
||||
|
|
@ -57,17 +57,20 @@ class DuolingoSpeakingAssessment4Question
|
|||
],
|
||||
);
|
||||
|
||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
||||
title: 'Speaking Assessment',
|
||||
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||
DuolingoPracticeAppBar(
|
||||
title: 'Speaking practice',
|
||||
onClose: () => viewModel.goTo(0),
|
||||
);
|
||||
|
||||
Widget _buildSpeakingIndicatorState(DuolingoViewModel viewModel) =>
|
||||
viewModel.isSpeaking
|
||||
? _buildSpeakingState()
|
||||
: _buildQuestionSectionWrapper(viewModel);
|
||||
Widget _buildSpeakingIndicatorState(CoursePracticeViewModel viewModel) =>
|
||||
// viewModel.isSpeaking
|
||||
// ? _buildSpeakingState()
|
||||
// :
|
||||
_buildQuestionSectionWrapper(viewModel);
|
||||
|
||||
Widget _buildQuestionSectionWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildQuestionSectionWrapper(CoursePracticeViewModel viewModel) =>
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: _buildQuestionSectionChildren(),
|
||||
|
|
@ -105,12 +108,13 @@ class DuolingoSpeakingAssessment4Question
|
|||
size: 75,
|
||||
);
|
||||
|
||||
Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding(
|
||||
Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) =>
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 50),
|
||||
child: _buildContinueButton(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildContinueButton(DuolingoViewModel viewModel) =>
|
||||
Widget _buildContinueButton(CoursePracticeViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
text: 'Start',
|
||||
|
|
@ -3,44 +3,44 @@ 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_practice_app_bar.dart';
|
||||
import '../../../widgets/speaking_practice_review_section.dart';
|
||||
import '../../../widgets/dwarf_tile.dart';
|
||||
import '../duolingo_viewmodel.dart';
|
||||
import '../course_practice_viewmodel.dart';
|
||||
|
||||
class DuolingoSpeakingAssessment4Review
|
||||
extends ViewModelWidget<DuolingoViewModel> {
|
||||
const DuolingoSpeakingAssessment4Review({super.key});
|
||||
class DuolingoSpeakingPractice4Review
|
||||
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||
const DuolingoSpeakingPractice4Review({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
||||
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||
_buildScaffoldWrapper(viewModel);
|
||||
|
||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
||||
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffold(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
||||
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||
SafeArea(child: _buildBodyColumn(viewModel));
|
||||
|
||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: _buildBodyColumnChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
||||
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||
_buildAppBarIndenter(viewModel),
|
||||
_buildQuestionIndenter(viewModel),
|
||||
_buildAssessmentReviewSection(viewModel)
|
||||
];
|
||||
|
||||
Widget _buildAppBarIndenter(DuolingoViewModel viewModel) => Padding(
|
||||
Widget _buildAppBarIndenter(CoursePracticeViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildAppBarWrapper(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||
children: [
|
||||
verticalSpaceMedium,
|
||||
_buildAppBar(viewModel),
|
||||
|
|
@ -48,12 +48,13 @@ class DuolingoSpeakingAssessment4Review
|
|||
],
|
||||
);
|
||||
|
||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
||||
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||
DuolingoPracticeAppBar(
|
||||
title: 'Feedback',
|
||||
onClose: () => viewModel.goTo(0),
|
||||
);
|
||||
|
||||
Widget _buildQuestionIndenter(DuolingoViewModel viewModel) => Padding(
|
||||
Widget _buildQuestionIndenter(CoursePracticeViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildDwarfWrapper(),
|
||||
);
|
||||
|
|
@ -69,6 +70,6 @@ class DuolingoSpeakingAssessment4Review
|
|||
style: style16DG600,
|
||||
);
|
||||
|
||||
Widget _buildAssessmentReviewSection(DuolingoViewModel viewModel) =>
|
||||
SpeakingAssessmentReviewSection(onTap: () => viewModel.goTo(5));
|
||||
Widget _buildAssessmentReviewSection(CoursePracticeViewModel viewModel) =>
|
||||
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/ui_helpers.dart';
|
||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
||||
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||
import '../../../widgets/custom_elevated_button.dart';
|
||||
import '../duolingo_viewmodel.dart';
|
||||
import '../course_practice_viewmodel.dart';
|
||||
|
||||
class DuolingoWritingAssessment1Question
|
||||
extends ViewModelWidget<DuolingoViewModel> {
|
||||
final TextEditingController assessmentController;
|
||||
const DuolingoWritingAssessment1Question(
|
||||
{super.key, required this.assessmentController});
|
||||
class DuolingoWritingPractice1Question
|
||||
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||
final TextEditingController practiceController;
|
||||
const DuolingoWritingPractice1Question(
|
||||
{super.key, required this.practiceController});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
||||
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||
_buildScaffoldWrapper(viewModel);
|
||||
|
||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
||||
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffold(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
||||
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
||||
|
||||
Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding(
|
||||
Widget _buildBodyColumnWrapper(CoursePracticeViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildBodyColumn(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: _buildBodyColumnChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
||||
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||
_buildAppBarWrapper(viewModel),
|
||||
_buildQuestionWrapper(viewModel),
|
||||
_buildContinueButtonWrapper(viewModel)
|
||||
];
|
||||
|
||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||
children: [
|
||||
verticalSpaceMedium,
|
||||
_buildAppBar(viewModel),
|
||||
],
|
||||
);
|
||||
|
||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
||||
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||
DuolingoPracticeAppBar(
|
||||
title: 'Writing Assessment',
|
||||
onClose: () => viewModel.goTo(0),
|
||||
);
|
||||
|
||||
Widget _buildQuestionWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildQuestionWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: _buildQuestionChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildQuestionChildren(DuolingoViewModel viewModel) => [
|
||||
List<Widget> _buildQuestionChildren(CoursePracticeViewModel viewModel) => [
|
||||
_buildTitle(),
|
||||
verticalSpaceSmall,
|
||||
_buildImageContainer(),
|
||||
verticalSpaceMedium,
|
||||
_buildAssessmentFormField(viewModel),
|
||||
if (viewModel.hasAssessmentValidationMessage &&
|
||||
viewModel.focusAssessment)
|
||||
viewModel.focusPractice)
|
||||
verticalSpaceTiny,
|
||||
if (viewModel.hasAssessmentValidationMessage &&
|
||||
viewModel.focusAssessment)
|
||||
viewModel.focusPractice)
|
||||
_buildAssessmentWrapper(viewModel),
|
||||
];
|
||||
|
||||
|
|
@ -97,34 +98,35 @@ class DuolingoWritingAssessment1Question
|
|||
width: double.maxFinite,
|
||||
);
|
||||
|
||||
Widget _buildAssessmentFormField(DuolingoViewModel viewModel) =>
|
||||
Widget _buildAssessmentFormField(CoursePracticeViewModel viewModel) =>
|
||||
TextFormField(
|
||||
maxLines: 5,
|
||||
maxLength: 250,
|
||||
controller: assessmentController,
|
||||
onTap: viewModel.setAssessmentFocus,
|
||||
controller: practiceController,
|
||||
onTap: viewModel.setPracticeFocus,
|
||||
decoration: inputDecoration(
|
||||
focus: true,
|
||||
hint: 'Start writing here...',
|
||||
filled: assessmentController.text.isNotEmpty),
|
||||
filled: practiceController.text.isNotEmpty),
|
||||
);
|
||||
|
||||
Widget _buildAssessmentWrapper(DuolingoViewModel viewModel) =>
|
||||
Widget _buildAssessmentWrapper(CoursePracticeViewModel viewModel) =>
|
||||
viewModel.hasAssessmentValidationMessage
|
||||
? _buildAssessmentValidator(viewModel)
|
||||
: Container();
|
||||
|
||||
Widget _buildAssessmentValidator(DuolingoViewModel viewModel) => Text(
|
||||
Widget _buildAssessmentValidator(CoursePracticeViewModel viewModel) => Text(
|
||||
viewModel.assessmentValidationMessage!,
|
||||
style: style12R700,
|
||||
);
|
||||
|
||||
Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding(
|
||||
Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) =>
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 50),
|
||||
child: _buildContinueButton(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildContinueButton(DuolingoViewModel viewModel) =>
|
||||
Widget _buildContinueButton(CoursePracticeViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
text: 'Submit',
|
||||
|
|
@ -1,18 +1,18 @@
|
|||
import 'package:flutter/material.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/ui_helpers.dart';
|
||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
||||
import '../duolingo_viewmodel.dart';
|
||||
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||
import '../../duolingo/duolingo_viewmodel.dart';
|
||||
|
||||
class DuolingoWritingAssessment1Review
|
||||
class DuolingoWritingPractice1Review
|
||||
extends ViewModelWidget<DuolingoViewModel> {
|
||||
final TextEditingController assessmentController;
|
||||
final TextEditingController practiceController;
|
||||
|
||||
const DuolingoWritingAssessment1Review(
|
||||
{super.key, required this.assessmentController});
|
||||
const DuolingoWritingPractice1Review(
|
||||
{super.key, required this.practiceController});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
||||
|
|
@ -49,7 +49,7 @@ class DuolingoWritingAssessment1Review
|
|||
],
|
||||
);
|
||||
|
||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoPracticeAppBar(
|
||||
title: 'Feedback',
|
||||
onClose: () => viewModel.goTo(0),
|
||||
);
|
||||
|
|
@ -112,13 +112,13 @@ class DuolingoWritingAssessment1Review
|
|||
maxLines: 5,
|
||||
enabled: false,
|
||||
maxLength: 250,
|
||||
controller: assessmentController,
|
||||
controller: practiceController,
|
||||
decoration: inputDecoration(
|
||||
focus: true,
|
||||
hint: 'Start writing here...',
|
||||
filled: assessmentController.text.isNotEmpty),
|
||||
filled: practiceController.text.isNotEmpty),
|
||||
);
|
||||
|
||||
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/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/duolingo_assessment_question_card.dart';
|
||||
import '../duolingo_viewmodel.dart';
|
||||
import '../../../widgets/duolingo_practice_question_card.dart';
|
||||
import '../course_practice_viewmodel.dart';
|
||||
|
||||
class DuolingoWritingAssessment2Answer
|
||||
extends ViewModelWidget<DuolingoViewModel> {
|
||||
final TextEditingController assessmentController;
|
||||
class DuolingoWritingPractice2Answer
|
||||
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||
final TextEditingController practiceController;
|
||||
|
||||
const DuolingoWritingAssessment2Answer(
|
||||
{super.key, required this.assessmentController});
|
||||
const DuolingoWritingPractice2Answer(
|
||||
{super.key, required this.practiceController});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
||||
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||
_buildScaffoldWrapper(viewModel);
|
||||
|
||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
||||
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffold(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
||||
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
||||
|
||||
Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding(
|
||||
Widget _buildBodyColumnWrapper(CoursePracticeViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildBodyColumn(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: _buildBodyColumnChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
||||
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||
_buildAppBarWrapper(viewModel),
|
||||
_buildQuestionWrapper(viewModel),
|
||||
_buildContinueButtonWrapper(viewModel)
|
||||
];
|
||||
|
||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||
children: [
|
||||
verticalSpaceMedium,
|
||||
_buildAppBar(viewModel),
|
||||
],
|
||||
);
|
||||
|
||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
||||
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||
DuolingoPracticeAppBar(
|
||||
title: 'Writing Assessment',
|
||||
onClose: () => viewModel.goTo(0),
|
||||
);
|
||||
|
||||
Widget _buildQuestionWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildQuestionWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: _buildQuestionChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildQuestionChildren(DuolingoViewModel viewModel) => [
|
||||
List<Widget> _buildQuestionChildren(CoursePracticeViewModel viewModel) => [
|
||||
_buildTitle(),
|
||||
verticalSpaceMedium,
|
||||
_buildQuestion(),
|
||||
verticalSpaceMedium,
|
||||
_buildAssessmentFormField(viewModel),
|
||||
if (viewModel.hasAssessmentValidationMessage &&
|
||||
viewModel.focusAssessment)
|
||||
viewModel.focusPractice)
|
||||
verticalSpaceTiny,
|
||||
if (viewModel.hasAssessmentValidationMessage &&
|
||||
viewModel.focusAssessment)
|
||||
viewModel.focusPractice)
|
||||
_buildAssessmentWrapper(viewModel),
|
||||
];
|
||||
|
||||
|
|
@ -82,38 +83,39 @@ class DuolingoWritingAssessment2Answer
|
|||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
||||
Widget _buildQuestion() => const DuolingoAssessmentQuestionCard(
|
||||
Widget _buildQuestion() => const DuolingoPracticeQuestionCard(
|
||||
subtitle:
|
||||
'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(
|
||||
maxLines: 5,
|
||||
maxLength: 250,
|
||||
controller: assessmentController,
|
||||
onTap: viewModel.setAssessmentFocus,
|
||||
controller: practiceController,
|
||||
onTap: viewModel.setPracticeFocus,
|
||||
decoration: inputDecoration(
|
||||
focus: true,
|
||||
hint: 'Start writing here...',
|
||||
filled: assessmentController.text.isNotEmpty),
|
||||
filled: practiceController.text.isNotEmpty),
|
||||
);
|
||||
|
||||
Widget _buildAssessmentWrapper(DuolingoViewModel viewModel) =>
|
||||
Widget _buildAssessmentWrapper(CoursePracticeViewModel viewModel) =>
|
||||
viewModel.hasAssessmentValidationMessage
|
||||
? _buildAssessmentValidator(viewModel)
|
||||
: Container();
|
||||
|
||||
Widget _buildAssessmentValidator(DuolingoViewModel viewModel) => Text(
|
||||
Widget _buildAssessmentValidator(CoursePracticeViewModel viewModel) => Text(
|
||||
viewModel.assessmentValidationMessage!,
|
||||
style: style12R700,
|
||||
);
|
||||
|
||||
Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding(
|
||||
Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) =>
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 50),
|
||||
child: _buildContinueButton(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildContinueButton(DuolingoViewModel viewModel) =>
|
||||
Widget _buildContinueButton(CoursePracticeViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
text: 'Submit',
|
||||
|
|
@ -3,65 +3,66 @@ import 'package:stacked/stacked.dart';
|
|||
|
||||
import '../../../common/app_colors.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/duolingo_assessment_question_card.dart';
|
||||
import '../duolingo_viewmodel.dart';
|
||||
import '../../../widgets/duolingo_practice_question_card.dart';
|
||||
import '../course_practice_viewmodel.dart';
|
||||
|
||||
class DuolingoWritingAssessment2Question
|
||||
extends ViewModelWidget<DuolingoViewModel> {
|
||||
final TextEditingController assessmentController;
|
||||
class DuolingoWritingPractice2Question
|
||||
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||
final TextEditingController practiceController;
|
||||
|
||||
const DuolingoWritingAssessment2Question(
|
||||
{super.key, required this.assessmentController});
|
||||
const DuolingoWritingPractice2Question(
|
||||
{super.key, required this.practiceController});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
||||
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||
_buildScaffoldWrapper(viewModel);
|
||||
|
||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
||||
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffold(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
||||
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
||||
|
||||
Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding(
|
||||
Widget _buildBodyColumnWrapper(CoursePracticeViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildBodyColumn(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: _buildBodyColumnChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
||||
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||
_buildAppBarWrapper(viewModel),
|
||||
_buildQuestionWrapper(viewModel),
|
||||
_buildContinueButtonWrapper(viewModel)
|
||||
];
|
||||
|
||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||
children: [
|
||||
verticalSpaceMedium,
|
||||
_buildAppBar(viewModel),
|
||||
],
|
||||
);
|
||||
|
||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
||||
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||
DuolingoPracticeAppBar(
|
||||
title: 'Writing Assessment',
|
||||
onClose: () => viewModel.goTo(0),
|
||||
);
|
||||
|
||||
Widget _buildQuestionWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildQuestionWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: _buildQuestionChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildQuestionChildren(DuolingoViewModel viewModel) => [
|
||||
List<Widget> _buildQuestionChildren(CoursePracticeViewModel viewModel) => [
|
||||
_buildTitle(),
|
||||
_buildSubtitle(),
|
||||
verticalSpaceMedium,
|
||||
|
|
@ -81,16 +82,17 @@ class DuolingoWritingAssessment2Question
|
|||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
||||
Widget _buildQuestion() => const DuolingoAssessmentQuestionCard(
|
||||
Widget _buildQuestion() => const DuolingoPracticeQuestionCard(
|
||||
subtitle:
|
||||
'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),
|
||||
child: _buildContinueButton(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildContinueButton(DuolingoViewModel viewModel) =>
|
||||
Widget _buildContinueButton(CoursePracticeViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
text: 'Submit',
|
||||
|
|
@ -1,48 +1,48 @@
|
|||
import 'package:flutter/material.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/ui_helpers.dart';
|
||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
||||
import '../../../widgets/duolingo_assessment_question_card.dart';
|
||||
import '../duolingo_viewmodel.dart';
|
||||
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||
import '../../../widgets/duolingo_practice_question_card.dart';
|
||||
import '../course_practice_viewmodel.dart';
|
||||
|
||||
class DuolingoWritingAssessment2Review
|
||||
extends ViewModelWidget<DuolingoViewModel> {
|
||||
final TextEditingController assessmentController;
|
||||
class DuolingoWritingPractice2Review
|
||||
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||
final TextEditingController practiceController;
|
||||
|
||||
const DuolingoWritingAssessment2Review(
|
||||
{super.key, required this.assessmentController});
|
||||
const DuolingoWritingPractice2Review(
|
||||
{super.key, required this.practiceController});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
||||
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||
_buildScaffoldWrapper(viewModel);
|
||||
|
||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
||||
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffold(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
||||
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||
SafeArea(child: _buildBodyColumn(viewModel));
|
||||
|
||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: _buildBodyColumnChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
||||
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||
_buildAppBarIndenter(viewModel),
|
||||
_buildExpandedBody(viewModel),
|
||||
];
|
||||
|
||||
Widget _buildAppBarIndenter(DuolingoViewModel viewModel) => Padding(
|
||||
Widget _buildAppBarIndenter(CoursePracticeViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildAppBarWrapper(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||
children: [
|
||||
verticalSpaceMedium,
|
||||
_buildAppBar(viewModel),
|
||||
|
|
@ -50,27 +50,29 @@ class DuolingoWritingAssessment2Review
|
|||
],
|
||||
);
|
||||
|
||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
||||
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||
DuolingoPracticeAppBar(
|
||||
title: 'Feedback',
|
||||
onClose: () => viewModel.goTo(0),
|
||||
);
|
||||
|
||||
Widget _buildExpandedBody(DuolingoViewModel viewModel) =>
|
||||
Widget _buildExpandedBody(CoursePracticeViewModel viewModel) =>
|
||||
Expanded(child: _buildBodyScroller(viewModel));
|
||||
|
||||
Widget _buildBodyScroller(DuolingoViewModel viewModel) =>
|
||||
Widget _buildBodyScroller(CoursePracticeViewModel viewModel) =>
|
||||
SingleChildScrollView(
|
||||
child: _buildQuestionSectionWrapper(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildQuestionSectionWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildQuestionSectionWrapper(CoursePracticeViewModel viewModel) =>
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: _buildQuestionQuestionSectionChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildQuestionQuestionSectionChildren(
|
||||
DuolingoViewModel viewModel) =>
|
||||
CoursePracticeViewModel viewModel) =>
|
||||
[
|
||||
verticalSpaceLarge,
|
||||
_buildTitle(),
|
||||
|
|
@ -93,28 +95,28 @@ class DuolingoWritingAssessment2Review
|
|||
child: _buildQuestion(),
|
||||
);
|
||||
|
||||
Widget _buildQuestion() => const DuolingoAssessmentQuestionCard(
|
||||
Widget _buildQuestion() => const DuolingoPracticeQuestionCard(
|
||||
subtitle:
|
||||
'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: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildAssessmentFormField(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildAssessmentFormField(DuolingoViewModel viewModel) =>
|
||||
Widget _buildAssessmentFormField(CoursePracticeViewModel viewModel) =>
|
||||
TextFormField(
|
||||
maxLines: 5,
|
||||
enabled: false,
|
||||
maxLength: 250,
|
||||
controller: assessmentController,
|
||||
controller: practiceController,
|
||||
decoration: inputDecoration(
|
||||
focus: true,
|
||||
hint: 'Start writing here...',
|
||||
filled: assessmentController.text.isNotEmpty),
|
||||
filled: practiceController.text.isNotEmpty),
|
||||
);
|
||||
|
||||
Widget _buildAssessmentReviewSection(DuolingoViewModel viewModel) =>
|
||||
DuolingoAssessmentReviewSection(onTap: () => viewModel.goTo(5));
|
||||
Widget _buildAssessmentReviewSection(CoursePracticeViewModel viewModel) =>
|
||||
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/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/duolingo_assessment_question_card.dart';
|
||||
import '../duolingo_viewmodel.dart';
|
||||
import '../../../widgets/duolingo_practice_question_card.dart';
|
||||
import '../course_practice_viewmodel.dart';
|
||||
|
||||
class DuolingoWritingAssessment3Question
|
||||
extends ViewModelWidget<DuolingoViewModel> {
|
||||
final TextEditingController assessmentController;
|
||||
class DuolingoWritingPractice3Question
|
||||
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||
final TextEditingController practiceController;
|
||||
|
||||
const DuolingoWritingAssessment3Question(
|
||||
{super.key, required this.assessmentController});
|
||||
const DuolingoWritingPractice3Question(
|
||||
{super.key, required this.practiceController});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
||||
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||
_buildScaffoldWrapper(viewModel);
|
||||
|
||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
||||
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffold(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
||||
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
||||
|
||||
Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding(
|
||||
Widget _buildBodyColumnWrapper(CoursePracticeViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildBodyColumn(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: _buildBodyColumnChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
||||
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||
_buildAppBarWrapper(viewModel),
|
||||
_buildQuestionWrapper(viewModel),
|
||||
_buildContinueButtonWrapper(viewModel)
|
||||
];
|
||||
|
||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||
children: [
|
||||
verticalSpaceMedium,
|
||||
_buildAppBar(viewModel),
|
||||
],
|
||||
);
|
||||
|
||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
||||
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||
DuolingoPracticeAppBar(
|
||||
title: 'Writing Assessment',
|
||||
onClose: () => viewModel.goTo(0),
|
||||
);
|
||||
|
||||
Widget _buildQuestionWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildQuestionWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: _buildQuestionChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildQuestionChildren(DuolingoViewModel viewModel) => [
|
||||
List<Widget> _buildQuestionChildren(CoursePracticeViewModel viewModel) => [
|
||||
_buildTitle(),
|
||||
verticalSpaceMedium,
|
||||
_buildQuestion(),
|
||||
verticalSpaceMedium,
|
||||
_buildAssessmentFormField(viewModel),
|
||||
if (viewModel.hasAssessmentValidationMessage &&
|
||||
viewModel.focusAssessment)
|
||||
viewModel.focusPractice)
|
||||
verticalSpaceTiny,
|
||||
if (viewModel.hasAssessmentValidationMessage &&
|
||||
viewModel.focusAssessment)
|
||||
viewModel.focusPractice)
|
||||
_buildAssessmentWrapper(viewModel),
|
||||
];
|
||||
|
||||
|
|
@ -82,38 +83,39 @@ class DuolingoWritingAssessment3Question
|
|||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
||||
Widget _buildQuestion() => const DuolingoAssessmentQuestionCard(
|
||||
Widget _buildQuestion() => const DuolingoPracticeQuestionCard(
|
||||
title: '1. Can you tell me about your favorite type of music?',
|
||||
);
|
||||
|
||||
Widget _buildAssessmentFormField(DuolingoViewModel viewModel) =>
|
||||
Widget _buildAssessmentFormField(CoursePracticeViewModel viewModel) =>
|
||||
TextFormField(
|
||||
maxLines: 5,
|
||||
maxLength: 250,
|
||||
controller: assessmentController,
|
||||
onTap: viewModel.setAssessmentFocus,
|
||||
controller: practiceController,
|
||||
onTap: viewModel.setPracticeFocus,
|
||||
decoration: inputDecoration(
|
||||
focus: true,
|
||||
hint: 'Start writing here...',
|
||||
filled: assessmentController.text.isNotEmpty),
|
||||
filled: practiceController.text.isNotEmpty),
|
||||
);
|
||||
|
||||
Widget _buildAssessmentWrapper(DuolingoViewModel viewModel) =>
|
||||
Widget _buildAssessmentWrapper(CoursePracticeViewModel viewModel) =>
|
||||
viewModel.hasAssessmentValidationMessage
|
||||
? _buildAssessmentValidator(viewModel)
|
||||
: Container();
|
||||
|
||||
Widget _buildAssessmentValidator(DuolingoViewModel viewModel) => Text(
|
||||
Widget _buildAssessmentValidator(CoursePracticeViewModel viewModel) => Text(
|
||||
viewModel.assessmentValidationMessage!,
|
||||
style: style12R700,
|
||||
);
|
||||
|
||||
Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding(
|
||||
Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) =>
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 50),
|
||||
child: _buildContinueButton(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildContinueButton(DuolingoViewModel viewModel) =>
|
||||
Widget _buildContinueButton(CoursePracticeViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
text: 'Submit',
|
||||
|
|
@ -1,48 +1,48 @@
|
|||
import 'package:flutter/material.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/ui_helpers.dart';
|
||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
||||
import '../../../widgets/duolingo_assessment_question_card.dart';
|
||||
import '../duolingo_viewmodel.dart';
|
||||
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||
import '../../../widgets/duolingo_practice_question_card.dart';
|
||||
import '../course_practice_viewmodel.dart';
|
||||
|
||||
class DuolingoWritingAssessment3Review
|
||||
extends ViewModelWidget<DuolingoViewModel> {
|
||||
final TextEditingController assessmentController;
|
||||
class DuolingoWritingPractice3Review
|
||||
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||
final TextEditingController practiceController;
|
||||
|
||||
const DuolingoWritingAssessment3Review(
|
||||
{super.key, required this.assessmentController});
|
||||
const DuolingoWritingPractice3Review(
|
||||
{super.key, required this.practiceController});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
||||
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||
_buildScaffoldWrapper(viewModel);
|
||||
|
||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
||||
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffold(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
||||
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||
SafeArea(child: _buildBodyColumn(viewModel));
|
||||
|
||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: _buildBodyColumnChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
||||
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||
_buildAppBarIndenter(viewModel),
|
||||
_buildExpandedBody(viewModel),
|
||||
];
|
||||
|
||||
Widget _buildAppBarIndenter(DuolingoViewModel viewModel) => Padding(
|
||||
Widget _buildAppBarIndenter(CoursePracticeViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildAppBarWrapper(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||
children: [
|
||||
verticalSpaceMedium,
|
||||
_buildAppBar(viewModel),
|
||||
|
|
@ -50,27 +50,29 @@ class DuolingoWritingAssessment3Review
|
|||
],
|
||||
);
|
||||
|
||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
||||
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||
DuolingoPracticeAppBar(
|
||||
title: 'Feedback',
|
||||
onClose: () => viewModel.goTo(0),
|
||||
);
|
||||
|
||||
Widget _buildExpandedBody(DuolingoViewModel viewModel) =>
|
||||
Widget _buildExpandedBody(CoursePracticeViewModel viewModel) =>
|
||||
Expanded(child: _buildBodyScroller(viewModel));
|
||||
|
||||
Widget _buildBodyScroller(DuolingoViewModel viewModel) =>
|
||||
Widget _buildBodyScroller(CoursePracticeViewModel viewModel) =>
|
||||
SingleChildScrollView(
|
||||
child: _buildQuestionSectionWrapper(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildQuestionSectionWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildQuestionSectionWrapper(CoursePracticeViewModel viewModel) =>
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: _buildQuestionQuestionSectionChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildQuestionQuestionSectionChildren(
|
||||
DuolingoViewModel viewModel) =>
|
||||
CoursePracticeViewModel viewModel) =>
|
||||
[
|
||||
verticalSpaceLarge,
|
||||
_buildTitle(),
|
||||
|
|
@ -93,27 +95,27 @@ class DuolingoWritingAssessment3Review
|
|||
child: _buildQuestion(),
|
||||
);
|
||||
|
||||
Widget _buildQuestion() => const DuolingoAssessmentQuestionCard(
|
||||
Widget _buildQuestion() => const DuolingoPracticeQuestionCard(
|
||||
title: 'Can you tell me about your favorite type of music?');
|
||||
|
||||
Widget _buildAssessmentFormFieldWrapper(DuolingoViewModel viewModel) =>
|
||||
Widget _buildAssessmentFormFieldWrapper(CoursePracticeViewModel viewModel) =>
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildAssessmentFormField(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildAssessmentFormField(DuolingoViewModel viewModel) =>
|
||||
Widget _buildAssessmentFormField(CoursePracticeViewModel viewModel) =>
|
||||
TextFormField(
|
||||
maxLines: 5,
|
||||
enabled: false,
|
||||
maxLength: 250,
|
||||
controller: assessmentController,
|
||||
controller: practiceController,
|
||||
decoration: inputDecoration(
|
||||
focus: true,
|
||||
hint: 'Start writing here...',
|
||||
filled: assessmentController.text.isNotEmpty),
|
||||
filled: practiceController.text.isNotEmpty),
|
||||
);
|
||||
|
||||
Widget _buildAssessmentReviewSection(DuolingoViewModel viewModel) =>
|
||||
DuolingoAssessmentReviewSection(onTap: () => viewModel.goTo(5));
|
||||
Widget _buildAssessmentReviewSection(CoursePracticeViewModel viewModel) =>
|
||||
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/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/duolingo_assessment_question_card.dart';
|
||||
import '../duolingo_viewmodel.dart';
|
||||
import '../../../widgets/duolingo_practice_question_card.dart';
|
||||
import '../course_practice_viewmodel.dart';
|
||||
|
||||
class DuolingoWritingAssessment4Question
|
||||
extends ViewModelWidget<DuolingoViewModel> {
|
||||
final TextEditingController assessmentController;
|
||||
class DuolingoWritingPractice4Question
|
||||
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||
final TextEditingController practiceController;
|
||||
|
||||
const DuolingoWritingAssessment4Question(
|
||||
{super.key, required this.assessmentController});
|
||||
const DuolingoWritingPractice4Question(
|
||||
{super.key, required this.practiceController});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
||||
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||
_buildScaffoldWrapper(viewModel);
|
||||
|
||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
||||
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffold(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
||||
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||
SafeArea(child: _buildBodyColumnWrapper(viewModel));
|
||||
|
||||
Widget _buildBodyColumnWrapper(DuolingoViewModel viewModel) => Padding(
|
||||
Widget _buildBodyColumnWrapper(CoursePracticeViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildBodyColumn(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: _buildBodyColumnChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
||||
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||
_buildAppBarWrapper(viewModel),
|
||||
_buildQuestionWrapper(viewModel),
|
||||
_buildContinueButtonWrapper(viewModel)
|
||||
];
|
||||
|
||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||
children: [
|
||||
verticalSpaceMedium,
|
||||
_buildAppBar(viewModel),
|
||||
],
|
||||
);
|
||||
|
||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
||||
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||
DuolingoPracticeAppBar(
|
||||
title: 'Writing Assessment',
|
||||
onClose: () => viewModel.goTo(0),
|
||||
);
|
||||
|
||||
Widget _buildQuestionWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildQuestionWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: _buildQuestionChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildQuestionChildren(DuolingoViewModel viewModel) => [
|
||||
List<Widget> _buildQuestionChildren(CoursePracticeViewModel viewModel) => [
|
||||
_buildTitle(),
|
||||
verticalSpaceMedium,
|
||||
_buildQuestion(),
|
||||
verticalSpaceMedium,
|
||||
_buildAssessmentFormField(viewModel),
|
||||
if (viewModel.hasAssessmentValidationMessage &&
|
||||
viewModel.focusAssessment)
|
||||
viewModel.focusPractice)
|
||||
verticalSpaceTiny,
|
||||
if (viewModel.hasAssessmentValidationMessage &&
|
||||
viewModel.focusAssessment)
|
||||
viewModel.focusPractice)
|
||||
_buildAssessmentWrapper(viewModel),
|
||||
];
|
||||
|
||||
|
|
@ -82,38 +83,39 @@ class DuolingoWritingAssessment4Question
|
|||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
||||
Widget _buildQuestion() => const DuolingoAssessmentQuestionCard(
|
||||
Widget _buildQuestion() => const DuolingoPracticeQuestionCard(
|
||||
title: '1. Can you tell me about your favorite type of music?',
|
||||
);
|
||||
|
||||
Widget _buildAssessmentFormField(DuolingoViewModel viewModel) =>
|
||||
Widget _buildAssessmentFormField(CoursePracticeViewModel viewModel) =>
|
||||
TextFormField(
|
||||
maxLines: 5,
|
||||
maxLength: 250,
|
||||
controller: assessmentController,
|
||||
onTap: viewModel.setAssessmentFocus,
|
||||
controller: practiceController,
|
||||
onTap: viewModel.setPracticeFocus,
|
||||
decoration: inputDecoration(
|
||||
focus: true,
|
||||
hint: 'Start writing here...',
|
||||
filled: assessmentController.text.isNotEmpty),
|
||||
filled: practiceController.text.isNotEmpty),
|
||||
);
|
||||
|
||||
Widget _buildAssessmentWrapper(DuolingoViewModel viewModel) =>
|
||||
Widget _buildAssessmentWrapper(CoursePracticeViewModel viewModel) =>
|
||||
viewModel.hasAssessmentValidationMessage
|
||||
? _buildAssessmentValidator(viewModel)
|
||||
: Container();
|
||||
|
||||
Widget _buildAssessmentValidator(DuolingoViewModel viewModel) => Text(
|
||||
Widget _buildAssessmentValidator(CoursePracticeViewModel viewModel) => Text(
|
||||
viewModel.assessmentValidationMessage!,
|
||||
style: style12R700,
|
||||
);
|
||||
|
||||
Widget _buildContinueButtonWrapper(DuolingoViewModel viewModel) => Padding(
|
||||
Widget _buildContinueButtonWrapper(CoursePracticeViewModel viewModel) =>
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 50),
|
||||
child: _buildContinueButton(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildContinueButton(DuolingoViewModel viewModel) =>
|
||||
Widget _buildContinueButton(CoursePracticeViewModel viewModel) =>
|
||||
CustomElevatedButton(
|
||||
height: 55,
|
||||
text: 'Submit',
|
||||
|
|
@ -1,48 +1,48 @@
|
|||
import 'package:flutter/material.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/ui_helpers.dart';
|
||||
import '../../../widgets/duolingo_assessment_app_bar.dart';
|
||||
import '../../../widgets/duolingo_assessment_question_card.dart';
|
||||
import '../duolingo_viewmodel.dart';
|
||||
import '../../../widgets/duolingo_practice_app_bar.dart';
|
||||
import '../../../widgets/duolingo_practice_question_card.dart';
|
||||
import '../course_practice_viewmodel.dart';
|
||||
|
||||
class DuolingoWritingAssessment4Review
|
||||
extends ViewModelWidget<DuolingoViewModel> {
|
||||
final TextEditingController assessmentController;
|
||||
class DuolingoWritingPractice4Review
|
||||
extends ViewModelWidget<CoursePracticeViewModel> {
|
||||
final TextEditingController practiceController;
|
||||
|
||||
const DuolingoWritingAssessment4Review(
|
||||
{super.key, required this.assessmentController});
|
||||
const DuolingoWritingPractice4Review(
|
||||
{super.key, required this.practiceController});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, DuolingoViewModel viewModel) =>
|
||||
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||
_buildScaffoldWrapper(viewModel);
|
||||
|
||||
Widget _buildScaffoldWrapper(DuolingoViewModel viewModel) => Scaffold(
|
||||
Widget _buildScaffoldWrapper(CoursePracticeViewModel viewModel) => Scaffold(
|
||||
backgroundColor: kcBackgroundColor,
|
||||
body: _buildScaffold(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildScaffold(DuolingoViewModel viewModel) =>
|
||||
Widget _buildScaffold(CoursePracticeViewModel viewModel) =>
|
||||
SafeArea(child: _buildBodyColumn(viewModel));
|
||||
|
||||
Widget _buildBodyColumn(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildBodyColumn(CoursePracticeViewModel viewModel) => Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: _buildBodyColumnChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildBodyColumnChildren(DuolingoViewModel viewModel) => [
|
||||
List<Widget> _buildBodyColumnChildren(CoursePracticeViewModel viewModel) => [
|
||||
_buildAppBarIndenter(viewModel),
|
||||
_buildExpandedBody(viewModel),
|
||||
];
|
||||
|
||||
Widget _buildAppBarIndenter(DuolingoViewModel viewModel) => Padding(
|
||||
Widget _buildAppBarIndenter(CoursePracticeViewModel viewModel) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildAppBarWrapper(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildAppBarWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildAppBarWrapper(CoursePracticeViewModel viewModel) => Column(
|
||||
children: [
|
||||
verticalSpaceMedium,
|
||||
_buildAppBar(viewModel),
|
||||
|
|
@ -50,27 +50,29 @@ class DuolingoWritingAssessment4Review
|
|||
],
|
||||
);
|
||||
|
||||
Widget _buildAppBar(DuolingoViewModel viewModel) => DuolingoAssessmentAppBar(
|
||||
Widget _buildAppBar(CoursePracticeViewModel viewModel) =>
|
||||
DuolingoPracticeAppBar(
|
||||
title: 'Feedback',
|
||||
onClose: () => viewModel.goTo(0),
|
||||
);
|
||||
|
||||
Widget _buildExpandedBody(DuolingoViewModel viewModel) =>
|
||||
Widget _buildExpandedBody(CoursePracticeViewModel viewModel) =>
|
||||
Expanded(child: _buildBodyScroller(viewModel));
|
||||
|
||||
Widget _buildBodyScroller(DuolingoViewModel viewModel) =>
|
||||
Widget _buildBodyScroller(CoursePracticeViewModel viewModel) =>
|
||||
SingleChildScrollView(
|
||||
child: _buildQuestionSectionWrapper(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildQuestionSectionWrapper(DuolingoViewModel viewModel) => Column(
|
||||
Widget _buildQuestionSectionWrapper(CoursePracticeViewModel viewModel) =>
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: _buildQuestionQuestionSectionChildren(viewModel),
|
||||
);
|
||||
|
||||
List<Widget> _buildQuestionQuestionSectionChildren(
|
||||
DuolingoViewModel viewModel) =>
|
||||
CoursePracticeViewModel viewModel) =>
|
||||
[
|
||||
verticalSpaceLarge,
|
||||
_buildTitle(),
|
||||
|
|
@ -93,27 +95,27 @@ class DuolingoWritingAssessment4Review
|
|||
child: _buildQuestion(),
|
||||
);
|
||||
|
||||
Widget _buildQuestion() => const DuolingoAssessmentQuestionCard(
|
||||
Widget _buildQuestion() => const DuolingoPracticeQuestionCard(
|
||||
title: 'Can you tell me about your favorite type of music?');
|
||||
|
||||
Widget _buildAssessmentFormFieldWrapper(DuolingoViewModel viewModel) =>
|
||||
Widget _buildAssessmentFormFieldWrapper(CoursePracticeViewModel viewModel) =>
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildAssessmentFormField(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildAssessmentFormField(DuolingoViewModel viewModel) =>
|
||||
Widget _buildAssessmentFormField(CoursePracticeViewModel viewModel) =>
|
||||
TextFormField(
|
||||
maxLines: 5,
|
||||
enabled: false,
|
||||
maxLength: 250,
|
||||
controller: assessmentController,
|
||||
controller: practiceController,
|
||||
decoration: inputDecoration(
|
||||
focus: true,
|
||||
hint: 'Start writing here...',
|
||||
filled: assessmentController.text.isNotEmpty),
|
||||
filled: practiceController.text.isNotEmpty),
|
||||
);
|
||||
|
||||
Widget _buildAssessmentReviewSection(DuolingoViewModel viewModel) =>
|
||||
DuolingoAssessmentReviewSection(onTap: () => viewModel.goTo(5));
|
||||
Widget _buildAssessmentReviewSection(CoursePracticeViewModel viewModel) =>
|
||||
DuolingoPracticeReviewSection(onTap: () => viewModel.goTo(5));
|
||||
}
|
||||
|
|
@ -2,37 +2,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:stacked/stacked.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/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/validators/form_validator.dart';
|
||||
|
|
@ -44,130 +14,7 @@ import 'duolingo_viewmodel.dart';
|
|||
class DuolingoView extends StackedView<DuolingoViewModel> with $DuolingoView {
|
||||
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
|
||||
DuolingoViewModel viewModelBuilder(BuildContext context) =>
|
||||
|
|
@ -198,40 +45,40 @@ class DuolingoView extends StackedView<DuolingoViewModel> with $DuolingoView {
|
|||
index: viewModel.currentPage, children: _buildScreens(viewModel));
|
||||
|
||||
List<Widget> _buildScreens(DuolingoViewModel viewModel) => [
|
||||
_buildDuolingoAssessmentsScreen(),
|
||||
_buildDuolingoIntroScreen(viewModel),
|
||||
_buildDuolingoQuestionScreen(viewModel),
|
||||
_buildDuolingoAnswerScreen(viewModel),
|
||||
_buildDuolingoReviewScreen(viewModel),
|
||||
_buildDuolingoRetakeScreen(viewModel),
|
||||
_buildDuolingoFinishScreen(viewModel),
|
||||
// _buildDuolingoAssessmentsScreen(),
|
||||
// _buildDuolingoIntroScreen(viewModel),
|
||||
// _buildDuolingoQuestionScreen(viewModel),
|
||||
// _buildDuolingoAnswerScreen(viewModel),
|
||||
// _buildDuolingoReviewScreen(viewModel),
|
||||
// _buildDuolingoRetakeScreen(viewModel),
|
||||
// _buildDuolingoFinishScreen(viewModel),
|
||||
];
|
||||
|
||||
Widget _buildDuolingoAssessmentsScreen() =>
|
||||
const DuolingoAssessmentsScreens();
|
||||
|
||||
Widget _buildDuolingoIntroScreen(DuolingoViewModel viewModel) =>
|
||||
DuolingoIntroScreen(
|
||||
type: viewModel.selectedAssessment['type'],
|
||||
title: viewModel.selectedAssessment['intro_title'],
|
||||
subtitle: viewModel.selectedAssessment['intro_subtitle']);
|
||||
|
||||
Widget _buildDuolingoQuestionScreen(DuolingoViewModel viewModel) =>
|
||||
_buildQuestionScreen(viewModel);
|
||||
|
||||
Widget _buildDuolingoAnswerScreen(DuolingoViewModel viewModel) =>
|
||||
_buildAnswerScreen(viewModel);
|
||||
|
||||
Widget _buildDuolingoReviewScreen(DuolingoViewModel viewModel) =>
|
||||
_buildReviewScreen(viewModel);
|
||||
|
||||
Widget _buildDuolingoRetakeScreen(DuolingoViewModel viewModel) =>
|
||||
DuolingoRetakeScreen(
|
||||
title: viewModel.selectedAssessment['outro_title'],
|
||||
subtitle: viewModel.selectedAssessment['outro_subtitle']);
|
||||
|
||||
Widget _buildDuolingoFinishScreen(DuolingoViewModel viewModel) =>
|
||||
DuolingoFinishScreen(
|
||||
title: viewModel.selectedAssessment['outro_title'],
|
||||
subtitle: viewModel.selectedAssessment['outro_subtitle']);
|
||||
// Widget _buildDuolingoAssessmentsScreen() =>
|
||||
// const DuolingoAssessmentsScreens();
|
||||
//
|
||||
// Widget _buildDuolingoIntroScreen(DuolingoViewModel viewModel) =>
|
||||
// DuolingoIntroScreen(
|
||||
// type: viewModel.selectedAssessment['type'],
|
||||
// title: viewModel.selectedAssessment['intro_title'],
|
||||
// subtitle: viewModel.selectedAssessment['intro_subtitle']);
|
||||
//
|
||||
// Widget _buildDuolingoQuestionScreen(DuolingoViewModel viewModel) =>
|
||||
// _buildQuestionScreen(viewModel);
|
||||
//
|
||||
// Widget _buildDuolingoAnswerScreen(DuolingoViewModel viewModel) =>
|
||||
// _buildAnswerScreen(viewModel);
|
||||
//
|
||||
// Widget _buildDuolingoReviewScreen(DuolingoViewModel viewModel) =>
|
||||
// _buildReviewScreen(viewModel);
|
||||
//
|
||||
// Widget _buildDuolingoRetakeScreen(DuolingoViewModel viewModel) =>
|
||||
// DuolingoRetakeScreen(
|
||||
// title: viewModel.selectedAssessment['outro_title'],
|
||||
// subtitle: viewModel.selectedAssessment['outro_subtitle']);
|
||||
//
|
||||
// Widget _buildDuolingoFinishScreen(DuolingoViewModel viewModel) =>
|
||||
// DuolingoFinishScreen(
|
||||
// title: viewModel.selectedAssessment['outro_title'],
|
||||
// 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_carousel_widget/flutter_carousel_widget.dart';
|
||||
import 'package:flutter_svg/svg.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/second_failure_screen.dart';
|
||||
import 'package:yimaru_app/ui/views/failure/screens/third_failure_screen.dart';
|
||||
|
||||
import '../../common/app_colors.dart';
|
||||
import '../../common/ui_helpers.dart';
|
||||
import '../../widgets/custom_circular_progress_indicator.dart';
|
||||
import 'failure_viewmodel.dart';
|
||||
|
||||
class FailureView extends StackedView<FailureViewModel> {
|
||||
|
|
|
|||
|
|
@ -1,43 +1,40 @@
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||
import 'package:yimaru_app/ui/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';
|
||||
|
||||
class FirstFailureScreen extends ViewModelWidget<FailureViewModel> {
|
||||
final String label;
|
||||
final GestureTapCallback onTap;
|
||||
|
||||
const FirstFailureScreen({super.key,required this.onTap,required this.label});
|
||||
const FirstFailureScreen(
|
||||
{super.key, required this.onTap, required this.label});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, FailureViewModel viewModel) =>
|
||||
Widget build(BuildContext context, FailureViewModel viewModel) =>
|
||||
_buildScaffoldWrapper();
|
||||
|
||||
Widget _buildScaffoldWrapper( ) => Scaffold(
|
||||
Widget _buildScaffoldWrapper() => Scaffold(
|
||||
backgroundColor: kcPrimaryColor,
|
||||
body: _buildScaffoldPadding(),
|
||||
);
|
||||
Widget _buildScaffoldPadding( ) => Padding(
|
||||
Widget _buildScaffoldPadding() => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildScaffold(),
|
||||
);
|
||||
|
||||
Widget _buildScaffold( ) => Column(
|
||||
Widget _buildScaffold() => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: _buildScaffoldChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildScaffoldChildren( ) =>
|
||||
List<Widget> _buildScaffoldChildren() =>
|
||||
[_buildUpperColumn(), _buildLowerColumnWrapper()];
|
||||
|
||||
Widget _buildUpperColumn() => Column(
|
||||
|
|
@ -59,17 +56,17 @@ class FirstFailureScreen extends ViewModelWidget<FailureViewModel> {
|
|||
height: 25,
|
||||
);
|
||||
|
||||
Widget _buildLowerColumnWrapper( ) => Expanded(
|
||||
Widget _buildLowerColumnWrapper() => Expanded(
|
||||
child: _buildLowerColumn(),
|
||||
);
|
||||
|
||||
Widget _buildLowerColumn( ) => Column(
|
||||
Widget _buildLowerColumn() => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: _buildLowerColumnChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildLowerColumnChildren( ) => [
|
||||
List<Widget> _buildLowerColumnChildren() => [
|
||||
_buildTitle(),
|
||||
verticalSpaceMedium,
|
||||
_buildImageWrapper(),
|
||||
|
|
@ -110,56 +107,51 @@ class FirstFailureScreen extends ViewModelWidget<FailureViewModel> {
|
|||
fit: BoxFit.cover,
|
||||
);
|
||||
|
||||
Widget _buildSafeWrapper( ) =>
|
||||
SafeArea(child: _buildContinueButtonWrapper());
|
||||
Widget _buildSafeWrapper() => SafeArea(child: _buildContinueButtonWrapper());
|
||||
|
||||
Widget _buildContinueButtonWrapper( ) => Align(
|
||||
Widget _buildContinueButtonWrapper() => Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: _buildLoadingTextContainer(),
|
||||
);
|
||||
|
||||
|
||||
|
||||
Widget _buildLoadingTextContainer() => Padding(
|
||||
padding: const EdgeInsets.only(bottom: 50),
|
||||
child: _buildLoadingTextWrapper(),
|
||||
);
|
||||
padding: const EdgeInsets.only(bottom: 50),
|
||||
child: _buildLoadingTextWrapper(),
|
||||
);
|
||||
|
||||
Widget _buildLoadingTextWrapper() => Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: _buildLoadingTextChildren(),
|
||||
);
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: _buildLoadingTextChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildLoadingTextChildren() => [
|
||||
_buildLoadingText(),
|
||||
horizontalSpaceSmall,
|
||||
_buildIndicatorWrapper(),
|
||||
horizontalSpaceSmall,
|
||||
_buildRetryButtonWrapper()
|
||||
];
|
||||
_buildLoadingText(),
|
||||
horizontalSpaceSmall,
|
||||
_buildIndicatorWrapper(),
|
||||
horizontalSpaceSmall,
|
||||
_buildRetryButtonWrapper()
|
||||
];
|
||||
|
||||
Widget _buildLoadingText() =>
|
||||
Text('$label...', style: style16W600);
|
||||
Widget _buildLoadingText() => Text('$label...', style: style16W600);
|
||||
|
||||
Widget _buildIndicatorWrapper() => SizedBox(
|
||||
width: 16,
|
||||
height: 16,
|
||||
child: _buildIndicator(),
|
||||
);
|
||||
width: 16,
|
||||
height: 16,
|
||||
child: _buildIndicator(),
|
||||
);
|
||||
|
||||
Widget _buildIndicator() =>
|
||||
const CustomCircularProgressIndicator(color: kcWhite);
|
||||
|
||||
|
||||
Widget _buildRetryButtonWrapper() => GestureDetector(
|
||||
onTap: onTap,
|
||||
child: _buildRetryButton(),
|
||||
);
|
||||
onTap: onTap,
|
||||
child: _buildRetryButton(),
|
||||
);
|
||||
|
||||
Widget _buildRetryButton() => Text(
|
||||
'Retry',
|
||||
style: style16W600.copyWith(
|
||||
fontStyle: FontStyle.italic, decoration: TextDecoration.underline),
|
||||
);
|
||||
'Retry',
|
||||
style: style16W600.copyWith(
|
||||
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_svg/svg.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||
import 'package:yimaru_app/ui/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';
|
||||
|
||||
class SecondFailureScreen extends ViewModelWidget<FailureViewModel> {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,10 @@
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||
import 'package:yimaru_app/ui/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';
|
||||
|
||||
class ThirdFailureScreen extends ViewModelWidget<FailureViewModel> {
|
||||
|
|
@ -21,145 +18,140 @@ class ThirdFailureScreen extends ViewModelWidget<FailureViewModel> {
|
|||
Widget build(BuildContext context, FailureViewModel viewModel) =>
|
||||
_buildScaffoldWrapper();
|
||||
|
||||
Widget _buildScaffoldWrapper( ) => Scaffold(
|
||||
backgroundColor:kcWhite,
|
||||
body: _buildScaffoldPadding(),
|
||||
);
|
||||
Widget _buildScaffoldPadding( ) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildScaffold(),
|
||||
);
|
||||
Widget _buildScaffoldWrapper() => Scaffold(
|
||||
backgroundColor: kcWhite,
|
||||
body: _buildScaffoldPadding(),
|
||||
);
|
||||
Widget _buildScaffoldPadding() => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildScaffold(),
|
||||
);
|
||||
|
||||
Widget _buildScaffold( ) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: _buildScaffoldChildren(),
|
||||
);
|
||||
Widget _buildScaffold() => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: _buildScaffoldChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildScaffoldChildren( ) =>
|
||||
List<Widget> _buildScaffoldChildren() =>
|
||||
[_buildUpperColumn(), _buildLowerColumnWrapper()];
|
||||
|
||||
Widget _buildUpperColumn() => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: _buildUpperColumnChildren(),
|
||||
);
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: _buildUpperColumnChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildUpperColumnChildren() =>
|
||||
[verticalSpaceLarge, _buildIconWrapper(), verticalSpaceLarge];
|
||||
|
||||
Widget _buildIconWrapper() => Align(
|
||||
alignment: Alignment.topLeft,
|
||||
child: _buildIcon(),
|
||||
);
|
||||
alignment: Alignment.topLeft,
|
||||
child: _buildIcon(),
|
||||
);
|
||||
|
||||
Widget _buildIcon() => SvgPicture.asset(
|
||||
'assets/icons/logo_purple.svg',
|
||||
height: 25,
|
||||
);
|
||||
'assets/icons/logo_purple.svg',
|
||||
height: 25,
|
||||
);
|
||||
|
||||
Widget _buildLowerColumnWrapper( ) => Expanded(
|
||||
child: _buildLowerColumn(),
|
||||
);
|
||||
Widget _buildLowerColumnWrapper() => Expanded(
|
||||
child: _buildLowerColumn(),
|
||||
);
|
||||
|
||||
Widget _buildLowerColumn( ) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: _buildLowerColumnChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildLowerColumnChildren( ) => [
|
||||
_buildTitle(),
|
||||
verticalSpaceMedium,
|
||||
_buildImageWrapper(),
|
||||
verticalSpaceMedium,
|
||||
_buildSafeWrapper()
|
||||
];
|
||||
Widget _buildLowerColumn() => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: _buildLowerColumnChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildLowerColumnChildren() => [
|
||||
_buildTitle(),
|
||||
verticalSpaceMedium,
|
||||
_buildImageWrapper(),
|
||||
verticalSpaceMedium,
|
||||
_buildSafeWrapper()
|
||||
];
|
||||
|
||||
Widget _buildTitle() => Text.rich(
|
||||
TextSpan(
|
||||
text: 'እንግሊዝኛ\n',
|
||||
style: style25P600,
|
||||
children: [
|
||||
TextSpan(
|
||||
text: 'በማንኛውም',
|
||||
style: style25P400,
|
||||
),
|
||||
TextSpan(
|
||||
text: ' ቦታ ',
|
||||
text: 'እንግሊዝኛ\n',
|
||||
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 _buildImageClipper() => ClipRRect(
|
||||
borderRadius: BorderRadius.circular(25),
|
||||
child: _buildImage(),
|
||||
);
|
||||
borderRadius: BorderRadius.circular(25),
|
||||
child: _buildImage(),
|
||||
);
|
||||
|
||||
Widget _buildImage() => Image.asset(
|
||||
'assets/images/landing_3.png',
|
||||
fit: BoxFit.cover,
|
||||
);
|
||||
|
||||
Widget _buildSafeWrapper( ) =>
|
||||
SafeArea(child: _buildContinueButtonWrapper());
|
||||
|
||||
Widget _buildContinueButtonWrapper( ) => Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: _buildLoadingTextContainer(),
|
||||
);
|
||||
'assets/images/landing_3.png',
|
||||
fit: BoxFit.cover,
|
||||
);
|
||||
|
||||
Widget _buildSafeWrapper() => SafeArea(child: _buildContinueButtonWrapper());
|
||||
|
||||
Widget _buildContinueButtonWrapper() => Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: _buildLoadingTextContainer(),
|
||||
);
|
||||
|
||||
Widget _buildLoadingTextContainer() => Padding(
|
||||
padding: const EdgeInsets.only(bottom: 50),
|
||||
child: _buildLoadingTextWrapper(),
|
||||
);
|
||||
padding: const EdgeInsets.only(bottom: 50),
|
||||
child: _buildLoadingTextWrapper(),
|
||||
);
|
||||
|
||||
Widget _buildLoadingTextWrapper() => Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: _buildLoadingTextChildren(),
|
||||
);
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: _buildLoadingTextChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildLoadingTextChildren() => [
|
||||
_buildLoadingText(),
|
||||
horizontalSpaceSmall,
|
||||
_buildIndicatorWrapper(),
|
||||
horizontalSpaceSmall,_buildRetryButtonWrapper()
|
||||
];
|
||||
_buildLoadingText(),
|
||||
horizontalSpaceSmall,
|
||||
_buildIndicatorWrapper(),
|
||||
horizontalSpaceSmall,
|
||||
_buildRetryButtonWrapper()
|
||||
];
|
||||
|
||||
Widget _buildLoadingText() =>
|
||||
Text('$label...', style: style16P600);
|
||||
Widget _buildLoadingText() => Text('$label...', style: style16P600);
|
||||
|
||||
Widget _buildIndicatorWrapper() => SizedBox(
|
||||
width: 16,
|
||||
height: 16,
|
||||
child: _buildIndicator(),
|
||||
);
|
||||
width: 16,
|
||||
height: 16,
|
||||
child: _buildIndicator(),
|
||||
);
|
||||
|
||||
Widget _buildIndicator() =>
|
||||
const CustomCircularProgressIndicator(color: kcPrimaryColor);
|
||||
|
||||
Widget _buildRetryButtonWrapper() => GestureDetector(
|
||||
onTap: onTap,
|
||||
child: _buildRetryButton(),
|
||||
);
|
||||
onTap: onTap,
|
||||
child: _buildRetryButton(),
|
||||
);
|
||||
|
||||
Widget _buildRetryButton() => Text(
|
||||
'Retry',
|
||||
style: style16P600.copyWith(
|
||||
fontStyle: FontStyle.italic, decoration: TextDecoration.underline),
|
||||
);
|
||||
'Retry',
|
||||
style: style16P600.copyWith(
|
||||
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/translations/locale_keys.g.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/profile/profile_view.dart';
|
||||
import 'package:yimaru_app/ui/widgets/page_loading_indicator.dart';
|
||||
|
||||
import '../../widgets/coming_soon.dart';
|
||||
import 'home_viewmodel.dart';
|
||||
|
||||
class HomeView extends StackedView<HomeViewModel> {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ class HomeViewModel extends ReactiveViewModel {
|
|||
// Dependency injection
|
||||
final _statusChecker = locator<StatusCheckerService>();
|
||||
|
||||
|
||||
final _bottomSheetService = locator<BottomSheetService>();
|
||||
|
||||
final _inAppUpdateService = locator<InAppUpdateService>();
|
||||
|
|
@ -26,7 +25,7 @@ class HomeViewModel extends ReactiveViewModel {
|
|||
|
||||
@override
|
||||
List<ListenableServiceMixin> get listenableServices =>
|
||||
[_authenticationService,_inAppNotificationService];
|
||||
[_authenticationService, _inAppNotificationService];
|
||||
|
||||
// Current user
|
||||
User? get _user => _authenticationService.user;
|
||||
|
|
@ -66,7 +65,6 @@ class HomeViewModel extends ReactiveViewModel {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// Unread notifications
|
||||
Future<void> getUnreadNotifications() async {
|
||||
if (await _statusChecker.checkConnection()) {
|
||||
|
|
|
|||
|
|
@ -53,5 +53,4 @@ class LandingView extends StackedView<LandingViewModel> {
|
|||
Widget _buildSecondLanding() => const SecondLandingScreen();
|
||||
|
||||
Widget _buildThirdLanding() => const ThirdLandingScreen();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class LearnModuleView extends StackedView<LearnModuleViewModel> {
|
|||
{required BuildContext context,
|
||||
required LearnModule module,
|
||||
required LearnModuleViewModel viewModel}) async {
|
||||
if (module.access?.isCompleted ?? false ) {
|
||||
if (module.access?.isCompleted ?? false) {
|
||||
await viewModel.navigateToLearnPractice(
|
||||
id: module.id ?? 0, module: module.name ?? '');
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:stacked/stacked.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_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_completion_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 '../../common/app_colors.dart';
|
||||
import '../../widgets/cancel_learn_practice_sheet.dart';
|
||||
import '../../widgets/cancel_practice_sheet.dart';
|
||||
import 'learn_practice_viewmodel.dart';
|
||||
|
||||
class LearnPracticeView extends StackedView<LearnPracticeViewModel> {
|
||||
|
|
@ -80,8 +80,7 @@ class LearnPracticeView extends StackedView<LearnPracticeViewModel> {
|
|||
},
|
||||
child: _buildScaffoldWrapper(viewModel));
|
||||
|
||||
Widget _buildSheet(LearnPracticeViewModel viewModel) =>
|
||||
CancelLearnPracticeSheet(
|
||||
Widget _buildSheet(LearnPracticeViewModel viewModel) => CancelPracticeSheet(
|
||||
onClose: viewModel.pop,
|
||||
onContinue: viewModel.pop,
|
||||
user: viewModel.user?.firstName ?? '',
|
||||
|
|
@ -101,7 +100,7 @@ class LearnPracticeView extends StackedView<LearnPracticeViewModel> {
|
|||
: _buildBody(viewModel);
|
||||
|
||||
Widget _buildPageLoadingIndicator(LearnPracticeViewModel viewModel) =>
|
||||
LearnLoadingScreen(
|
||||
PracticeLoadingScreen(
|
||||
isLoading: viewModel.busy(StateObjects.learnPractice),
|
||||
onTap: () async =>
|
||||
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:waveform_recorder/waveform_recorder.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_linear_progress_indicator.dart';
|
||||
import 'package:yimaru_app/ui/widgets/wave_wrapper.dart';
|
||||
|
|
@ -365,8 +365,7 @@ class InteractLearnPracticeScreen
|
|||
await _showSheet(context: context, viewModel: viewModel),
|
||||
);
|
||||
|
||||
Widget _buildSheet(LearnPracticeViewModel viewModel) =>
|
||||
CancelLearnPracticeSheet(
|
||||
Widget _buildSheet(LearnPracticeViewModel viewModel) => CancelPracticeSheet(
|
||||
onClose: viewModel.pop,
|
||||
onContinue: viewModel.pop,
|
||||
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/ui_helpers.dart';
|
||||
import '../../../widgets/cancel_learn_practice_sheet.dart';
|
||||
import '../../../widgets/cancel_practice_sheet.dart';
|
||||
import '../../../widgets/custom_elevated_button.dart';
|
||||
import '../../../widgets/small_app_bar.dart';
|
||||
|
||||
|
|
@ -104,8 +104,7 @@ class LearnPracticeAppreciationScreen
|
|||
await _showSheet(context: context, viewModel: viewModel),
|
||||
);
|
||||
|
||||
Widget _buildSheet(LearnPracticeViewModel viewModel) =>
|
||||
CancelLearnPracticeSheet(
|
||||
Widget _buildSheet(LearnPracticeViewModel viewModel) => CancelPracticeSheet(
|
||||
onClose: viewModel.pop,
|
||||
onContinue: viewModel.pop,
|
||||
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/translations/locale_keys.g.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/small_app_bar.dart';
|
||||
|
||||
|
|
@ -87,8 +87,7 @@ class LearnPracticeDescriptionScreen
|
|||
await _showSheet(context: context, viewModel: viewModel),
|
||||
);
|
||||
|
||||
Widget _buildSheet(LearnPracticeViewModel viewModel) =>
|
||||
CancelLearnPracticeSheet(
|
||||
Widget _buildSheet(LearnPracticeViewModel viewModel) => CancelPracticeSheet(
|
||||
onClose: viewModel.pop,
|
||||
onContinue: viewModel.pop,
|
||||
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/enmus.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/small_app_bar.dart';
|
||||
import '../../../widgets/speaking_partner_image.dart';
|
||||
|
|
@ -106,8 +106,7 @@ class LearnPracticeIntroScreen extends ViewModelWidget<LearnPracticeViewModel> {
|
|||
await _showSheet(context: context, viewModel: viewModel),
|
||||
);
|
||||
|
||||
Widget _buildSheet(LearnPracticeViewModel viewModel) =>
|
||||
CancelLearnPracticeSheet(
|
||||
Widget _buildSheet(LearnPracticeViewModel viewModel) => CancelPracticeSheet(
|
||||
onClose: viewModel.pop,
|
||||
onContinue: viewModel.pop,
|
||||
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/ui_helpers.dart';
|
||||
import '../../../widgets/cancel_learn_practice_sheet.dart';
|
||||
import '../../../widgets/cancel_practice_sheet.dart';
|
||||
import '../../../widgets/custom_elevated_button.dart';
|
||||
import '../../../widgets/page_loading_indicator.dart';
|
||||
import '../../../widgets/small_app_bar.dart';
|
||||
|
|
@ -120,8 +120,7 @@ class LearnPracticeResultScreen
|
|||
await _showSheet(context: context, viewModel: viewModel),
|
||||
);
|
||||
|
||||
Widget _buildSheet(LearnPracticeViewModel viewModel) =>
|
||||
CancelLearnPracticeSheet(
|
||||
Widget _buildSheet(LearnPracticeViewModel viewModel) => CancelPracticeSheet(
|
||||
onClose: viewModel.pop,
|
||||
onContinue: viewModel.pop,
|
||||
user: viewModel.user?.firstName ?? '',
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import 'package:yimaru_app/ui/widgets/custom_column_button.dart';
|
|||
import '../../../../models/learn_question.dart';
|
||||
import '../../../common/app_colors.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';
|
||||
|
||||
class StartLearnPracticeScreen extends ViewModelWidget<LearnPracticeViewModel> {
|
||||
|
|
@ -111,8 +111,7 @@ class StartLearnPracticeScreen extends ViewModelWidget<LearnPracticeViewModel> {
|
|||
title:
|
||||
'${LocaleKeys.practice_speaking.tr()} ($index/${viewModel.questions.length})');
|
||||
|
||||
Widget _buildSheet(LearnPracticeViewModel viewModel) =>
|
||||
CancelLearnPracticeSheet(
|
||||
Widget _buildSheet(LearnPracticeViewModel viewModel) => CancelPracticeSheet(
|
||||
onClose: viewModel.pop,
|
||||
onContinue: viewModel.pop,
|
||||
user: viewModel.user?.firstName ?? '',
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class LearnProgramViewModel extends ReactiveViewModel {
|
|||
|
||||
@override
|
||||
List<ListenableServiceMixin> get listenableServices =>
|
||||
[_learnService, _authenticationService,_inAppNotificationService];
|
||||
[_learnService, _authenticationService, _inAppNotificationService];
|
||||
|
||||
// Current user
|
||||
User? get _user => _authenticationService.user;
|
||||
|
|
|
|||
|
|
@ -60,8 +60,8 @@ class NotificationView extends StackedView<NotificationViewModel> {
|
|||
List<Widget> _buildColumnChildren(NotificationViewModel viewModel) => [
|
||||
verticalSpaceMedium,
|
||||
_buildAppBarWrapper(viewModel),
|
||||
verticalSpaceMedium,
|
||||
_buildNotificationsColumnWrapper(viewModel)
|
||||
verticalSpaceMedium,
|
||||
_buildNotificationsColumnWrapper(viewModel)
|
||||
];
|
||||
|
||||
Widget _buildAppBarWrapper(NotificationViewModel viewModel) => Padding(
|
||||
|
|
@ -75,7 +75,6 @@ class NotificationView extends StackedView<NotificationViewModel> {
|
|||
title: LocaleKeys.notifications.tr(),
|
||||
);
|
||||
|
||||
|
||||
Widget _buildNotificationsColumnWrapper(NotificationViewModel viewModel) =>
|
||||
Expanded(child: _buildNotificationsColumnScrollView(viewModel));
|
||||
|
||||
|
|
@ -87,26 +86,24 @@ class NotificationView extends StackedView<NotificationViewModel> {
|
|||
Widget _buildListViewBuilder(NotificationViewModel viewModel) =>
|
||||
viewModel.busy(StateObjects.notifications)
|
||||
? _buildProgressIndicator()
|
||||
: _buildListView(viewModel);
|
||||
: _buildListView(viewModel);
|
||||
|
||||
Widget _buildProgressIndicator() => const Center(
|
||||
child: CustomCircularProgressIndicator(color: kcPrimaryColor),
|
||||
);
|
||||
|
||||
Widget _buildListView(NotificationViewModel viewModel) => ListView.separated(
|
||||
shrinkWrap: true,
|
||||
itemCount: viewModel.notifications.length,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
separatorBuilder: (context, index) => verticalSpaceSmall,
|
||||
itemBuilder: (context, index) => _buildCard(
|
||||
notification: viewModel.notifications[index],
|
||||
),
|
||||
);
|
||||
shrinkWrap: true,
|
||||
itemCount: viewModel.notifications.length,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
separatorBuilder: (context, index) => verticalSpaceSmall,
|
||||
itemBuilder: (context, index) => _buildCard(
|
||||
notification: viewModel.notifications[index],
|
||||
),
|
||||
);
|
||||
|
||||
Widget _buildCard({
|
||||
required InAppNotification notification,
|
||||
}) =>
|
||||
NotificationCard(notification: notification);
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import '../../../app/app.locator.dart';
|
|||
import '../../../models/user.dart';
|
||||
import '../../../services/api_service.dart';
|
||||
import '../../../services/authentication_service.dart';
|
||||
import '../../../services/google_auth_service.dart';
|
||||
import '../../../services/in_app_notification_service.dart';
|
||||
import '../../../services/status_checker_service.dart';
|
||||
import '../../common/app_colors.dart';
|
||||
|
|
@ -32,7 +31,7 @@ class ProfileViewModel extends ReactiveViewModel {
|
|||
|
||||
@override
|
||||
List<ListenableServiceMixin> get listenableServices =>
|
||||
[_authenticationService,_inAppNotificationService];
|
||||
[_authenticationService, _inAppNotificationService];
|
||||
|
||||
// Current 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/ui_helpers.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';
|
||||
|
|
@ -13,29 +12,29 @@ import '../../../widgets/custom_circular_progress_indicator.dart';
|
|||
class FirstStartupScreen extends ViewModelWidget<StartupViewModel> {
|
||||
final String? label;
|
||||
|
||||
const FirstStartupScreen({super.key,this.label});
|
||||
const FirstStartupScreen({super.key, this.label});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, StartupViewModel viewModel) =>
|
||||
_buildScaffoldWrapper();
|
||||
|
||||
Widget _buildScaffoldWrapper( ) => Scaffold(
|
||||
Widget _buildScaffoldWrapper() => Scaffold(
|
||||
backgroundColor: kcPrimaryColor,
|
||||
body: _buildScaffoldPadding(),
|
||||
);
|
||||
Widget _buildScaffoldPadding( ) => Padding(
|
||||
Widget _buildScaffoldPadding() => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildScaffold(),
|
||||
);
|
||||
|
||||
Widget _buildScaffold( ) => Column(
|
||||
Widget _buildScaffold() => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: _buildScaffoldChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildScaffoldChildren( ) =>
|
||||
List<Widget> _buildScaffoldChildren() =>
|
||||
[_buildUpperColumn(), _buildLowerColumnWrapper()];
|
||||
|
||||
Widget _buildUpperColumn() => Column(
|
||||
|
|
@ -57,17 +56,17 @@ class FirstStartupScreen extends ViewModelWidget<StartupViewModel> {
|
|||
height: 25,
|
||||
);
|
||||
|
||||
Widget _buildLowerColumnWrapper( ) => Expanded(
|
||||
Widget _buildLowerColumnWrapper() => Expanded(
|
||||
child: _buildLowerColumn(),
|
||||
);
|
||||
|
||||
Widget _buildLowerColumn( ) => Column(
|
||||
Widget _buildLowerColumn() => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: _buildLowerColumnChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildLowerColumnChildren( ) => [
|
||||
List<Widget> _buildLowerColumnChildren() => [
|
||||
_buildTitle(),
|
||||
verticalSpaceMedium,
|
||||
_buildImageWrapper(),
|
||||
|
|
@ -108,44 +107,39 @@ class FirstStartupScreen extends ViewModelWidget<StartupViewModel> {
|
|||
fit: BoxFit.cover,
|
||||
);
|
||||
|
||||
Widget _buildSafeWrapper( ) =>
|
||||
SafeArea(child: _buildContinueButtonWrapper());
|
||||
Widget _buildSafeWrapper() => SafeArea(child: _buildContinueButtonWrapper());
|
||||
|
||||
Widget _buildContinueButtonWrapper( ) => Align(
|
||||
Widget _buildContinueButtonWrapper() => Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: _buildLoadingTextContainer(),
|
||||
);
|
||||
|
||||
|
||||
|
||||
Widget _buildLoadingTextContainer() => Padding(
|
||||
padding: const EdgeInsets.only(bottom: 50),
|
||||
child: _buildLoadingTextWrapper(),
|
||||
);
|
||||
padding: const EdgeInsets.only(bottom: 50),
|
||||
child: _buildLoadingTextWrapper(),
|
||||
);
|
||||
|
||||
Widget _buildLoadingTextWrapper() => Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: _buildLoadingTextChildren(),
|
||||
);
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: _buildLoadingTextChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildLoadingTextChildren() => [
|
||||
_buildLoadingText(),
|
||||
horizontalSpaceSmall,
|
||||
_buildIndicatorWrapper(),
|
||||
];
|
||||
_buildLoadingText(),
|
||||
horizontalSpaceSmall,
|
||||
_buildIndicatorWrapper(),
|
||||
];
|
||||
|
||||
Widget _buildLoadingText() =>
|
||||
Text('${label ?? LocaleKeys.loading.tr()} ...', style: style16W600);
|
||||
|
||||
Widget _buildIndicatorWrapper() => SizedBox(
|
||||
width: 16,
|
||||
height: 16,
|
||||
child: _buildIndicator(),
|
||||
);
|
||||
width: 16,
|
||||
height: 16,
|
||||
child: _buildIndicator(),
|
||||
);
|
||||
|
||||
Widget _buildIndicator() =>
|
||||
const CustomCircularProgressIndicator(color: kcWhite);
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import 'package:flutter_svg/svg.dart';
|
|||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||
|
||||
import '../../../common/translations/locale_keys.g.dart';
|
||||
import '../../../widgets/custom_circular_progress_indicator.dart';
|
||||
|
|
@ -13,141 +12,134 @@ import '../startup_viewmodel.dart';
|
|||
class SecondStartupScreen extends ViewModelWidget<StartupViewModel> {
|
||||
final String? label;
|
||||
|
||||
const SecondStartupScreen({super.key,this.label});
|
||||
const SecondStartupScreen({super.key, this.label});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, StartupViewModel viewModel) =>
|
||||
_buildScaffoldWrapper();
|
||||
|
||||
Widget _buildScaffoldWrapper( ) => Scaffold(
|
||||
backgroundColor: Colors.amber,
|
||||
body: _buildScaffoldPadding(),
|
||||
);
|
||||
Widget _buildScaffoldPadding( ) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildScaffold(),
|
||||
);
|
||||
Widget _buildScaffoldWrapper() => Scaffold(
|
||||
backgroundColor: Colors.amber,
|
||||
body: _buildScaffoldPadding(),
|
||||
);
|
||||
Widget _buildScaffoldPadding() => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildScaffold(),
|
||||
);
|
||||
|
||||
Widget _buildScaffold( ) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: _buildScaffoldChildren(),
|
||||
);
|
||||
Widget _buildScaffold() => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: _buildScaffoldChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildScaffoldChildren( ) =>
|
||||
List<Widget> _buildScaffoldChildren() =>
|
||||
[_buildUpperColumn(), _buildLowerColumnWrapper()];
|
||||
|
||||
Widget _buildUpperColumn() => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: _buildUpperColumnChildren(),
|
||||
);
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: _buildUpperColumnChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildUpperColumnChildren() =>
|
||||
[verticalSpaceLarge, _buildIconWrapper(), verticalSpaceLarge];
|
||||
|
||||
Widget _buildIconWrapper() => Align(
|
||||
alignment: Alignment.topLeft,
|
||||
child: _buildIcon(),
|
||||
);
|
||||
alignment: Alignment.topLeft,
|
||||
child: _buildIcon(),
|
||||
);
|
||||
|
||||
Widget _buildIcon() => SvgPicture.asset(
|
||||
'assets/icons/logo_purple.svg',
|
||||
height: 25,
|
||||
);
|
||||
'assets/icons/logo_purple.svg',
|
||||
height: 25,
|
||||
);
|
||||
|
||||
Widget _buildLowerColumnWrapper( ) => Expanded(
|
||||
child: _buildLowerColumn(),
|
||||
);
|
||||
Widget _buildLowerColumnWrapper() => Expanded(
|
||||
child: _buildLowerColumn(),
|
||||
);
|
||||
|
||||
Widget _buildLowerColumn( ) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: _buildLowerColumnChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildLowerColumnChildren( ) => [
|
||||
_buildTitle(),
|
||||
verticalSpaceMedium,
|
||||
_buildImageWrapper(),
|
||||
verticalSpaceMedium,
|
||||
_buildSafeWrapper()
|
||||
];
|
||||
Widget _buildLowerColumn() => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: _buildLowerColumnChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildLowerColumnChildren() => [
|
||||
_buildTitle(),
|
||||
verticalSpaceMedium,
|
||||
_buildImageWrapper(),
|
||||
verticalSpaceMedium,
|
||||
_buildSafeWrapper()
|
||||
];
|
||||
|
||||
Widget _buildTitle() => Text.rich(
|
||||
TextSpan(
|
||||
text: 'እንግሊዝኛ\n',
|
||||
style: style25P600,
|
||||
children: [
|
||||
TextSpan(
|
||||
text: 'በማንኛውም',
|
||||
style: style25P400,
|
||||
),
|
||||
TextSpan(
|
||||
text: ' እድሜ ',
|
||||
text: 'እንግሊዝኛ\n',
|
||||
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 _buildImageClipper() => ClipRRect(
|
||||
borderRadius: BorderRadius.circular(25),
|
||||
child: _buildImage(),
|
||||
);
|
||||
borderRadius: BorderRadius.circular(25),
|
||||
child: _buildImage(),
|
||||
);
|
||||
|
||||
Widget _buildImage() => Image.asset(
|
||||
'assets/images/landing_2.png',
|
||||
fit: BoxFit.cover,
|
||||
);
|
||||
|
||||
Widget _buildSafeWrapper( ) =>
|
||||
SafeArea(child: _buildContinueButtonWrapper());
|
||||
|
||||
Widget _buildContinueButtonWrapper( ) => Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: _buildLoadingTextContainer(),
|
||||
);
|
||||
'assets/images/landing_2.png',
|
||||
fit: BoxFit.cover,
|
||||
);
|
||||
|
||||
Widget _buildSafeWrapper() => SafeArea(child: _buildContinueButtonWrapper());
|
||||
|
||||
Widget _buildContinueButtonWrapper() => Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: _buildLoadingTextContainer(),
|
||||
);
|
||||
|
||||
Widget _buildLoadingTextContainer() => Padding(
|
||||
padding: const EdgeInsets.only(bottom: 50),
|
||||
child: _buildLoadingTextWrapper(),
|
||||
);
|
||||
padding: const EdgeInsets.only(bottom: 50),
|
||||
child: _buildLoadingTextWrapper(),
|
||||
);
|
||||
|
||||
Widget _buildLoadingTextWrapper() => Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: _buildLoadingTextChildren(),
|
||||
);
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: _buildLoadingTextChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildLoadingTextChildren() => [
|
||||
_buildLoadingText(),
|
||||
horizontalSpaceSmall,
|
||||
_buildIndicatorWrapper(),
|
||||
];
|
||||
_buildLoadingText(),
|
||||
horizontalSpaceSmall,
|
||||
_buildIndicatorWrapper(),
|
||||
];
|
||||
|
||||
Widget _buildLoadingText() =>
|
||||
Text('${label ?? LocaleKeys.loading.tr()} ...', style: style16P600);
|
||||
|
||||
Widget _buildIndicatorWrapper() => SizedBox(
|
||||
width: 16,
|
||||
height: 16,
|
||||
child: _buildIndicator(),
|
||||
);
|
||||
width: 16,
|
||||
height: 16,
|
||||
child: _buildIndicator(),
|
||||
);
|
||||
|
||||
Widget _buildIndicator() =>
|
||||
const CustomCircularProgressIndicator(color: kcPrimaryColor);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import 'package:flutter_svg/svg.dart';
|
|||
import 'package:stacked/stacked.dart';
|
||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||
import 'package:yimaru_app/ui/widgets/custom_elevated_button.dart';
|
||||
|
||||
import '../../../common/translations/locale_keys.g.dart';
|
||||
import '../../../widgets/custom_circular_progress_indicator.dart';
|
||||
|
|
@ -13,141 +12,134 @@ import '../startup_viewmodel.dart';
|
|||
class ThirdStartupScreen extends ViewModelWidget<StartupViewModel> {
|
||||
final String? label;
|
||||
|
||||
const ThirdStartupScreen({super.key,this.label});
|
||||
const ThirdStartupScreen({super.key, this.label});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, StartupViewModel viewModel) =>
|
||||
_buildScaffoldWrapper();
|
||||
|
||||
Widget _buildScaffoldWrapper( ) => Scaffold(
|
||||
backgroundColor:kcWhite,
|
||||
body: _buildScaffoldPadding(),
|
||||
);
|
||||
Widget _buildScaffoldPadding( ) => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildScaffold(),
|
||||
);
|
||||
Widget _buildScaffoldWrapper() => Scaffold(
|
||||
backgroundColor: kcWhite,
|
||||
body: _buildScaffoldPadding(),
|
||||
);
|
||||
Widget _buildScaffoldPadding() => Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: _buildScaffold(),
|
||||
);
|
||||
|
||||
Widget _buildScaffold( ) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: _buildScaffoldChildren(),
|
||||
);
|
||||
Widget _buildScaffold() => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: _buildScaffoldChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildScaffoldChildren( ) =>
|
||||
List<Widget> _buildScaffoldChildren() =>
|
||||
[_buildUpperColumn(), _buildLowerColumnWrapper()];
|
||||
|
||||
Widget _buildUpperColumn() => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: _buildUpperColumnChildren(),
|
||||
);
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: _buildUpperColumnChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildUpperColumnChildren() =>
|
||||
[verticalSpaceLarge, _buildIconWrapper(), verticalSpaceLarge];
|
||||
|
||||
Widget _buildIconWrapper() => Align(
|
||||
alignment: Alignment.topLeft,
|
||||
child: _buildIcon(),
|
||||
);
|
||||
alignment: Alignment.topLeft,
|
||||
child: _buildIcon(),
|
||||
);
|
||||
|
||||
Widget _buildIcon() => SvgPicture.asset(
|
||||
'assets/icons/logo_purple.svg',
|
||||
height: 25,
|
||||
);
|
||||
'assets/icons/logo_purple.svg',
|
||||
height: 25,
|
||||
);
|
||||
|
||||
Widget _buildLowerColumnWrapper( ) => Expanded(
|
||||
child: _buildLowerColumn(),
|
||||
);
|
||||
Widget _buildLowerColumnWrapper() => Expanded(
|
||||
child: _buildLowerColumn(),
|
||||
);
|
||||
|
||||
Widget _buildLowerColumn( ) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: _buildLowerColumnChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildLowerColumnChildren( ) => [
|
||||
_buildTitle(),
|
||||
verticalSpaceMedium,
|
||||
_buildImageWrapper(),
|
||||
verticalSpaceMedium,
|
||||
_buildSafeWrapper()
|
||||
];
|
||||
Widget _buildLowerColumn() => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: _buildLowerColumnChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildLowerColumnChildren() => [
|
||||
_buildTitle(),
|
||||
verticalSpaceMedium,
|
||||
_buildImageWrapper(),
|
||||
verticalSpaceMedium,
|
||||
_buildSafeWrapper()
|
||||
];
|
||||
|
||||
Widget _buildTitle() => Text.rich(
|
||||
TextSpan(
|
||||
text: 'እንግሊዝኛ\n',
|
||||
style: style25P600,
|
||||
children: [
|
||||
TextSpan(
|
||||
text: 'በማንኛውም',
|
||||
style: style25P400,
|
||||
),
|
||||
TextSpan(
|
||||
text: ' ቦታ ',
|
||||
text: 'እንግሊዝኛ\n',
|
||||
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 _buildImageClipper() => ClipRRect(
|
||||
borderRadius: BorderRadius.circular(25),
|
||||
child: _buildImage(),
|
||||
);
|
||||
borderRadius: BorderRadius.circular(25),
|
||||
child: _buildImage(),
|
||||
);
|
||||
|
||||
Widget _buildImage() => Image.asset(
|
||||
'assets/images/landing_3.png',
|
||||
fit: BoxFit.cover,
|
||||
);
|
||||
|
||||
Widget _buildSafeWrapper( ) =>
|
||||
SafeArea(child: _buildContinueButtonWrapper());
|
||||
|
||||
Widget _buildContinueButtonWrapper( ) => Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: _buildLoadingTextContainer(),
|
||||
);
|
||||
'assets/images/landing_3.png',
|
||||
fit: BoxFit.cover,
|
||||
);
|
||||
|
||||
Widget _buildSafeWrapper() => SafeArea(child: _buildContinueButtonWrapper());
|
||||
|
||||
Widget _buildContinueButtonWrapper() => Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: _buildLoadingTextContainer(),
|
||||
);
|
||||
|
||||
Widget _buildLoadingTextContainer() => Padding(
|
||||
padding: const EdgeInsets.only(bottom: 50),
|
||||
child: _buildLoadingTextWrapper(),
|
||||
);
|
||||
padding: const EdgeInsets.only(bottom: 50),
|
||||
child: _buildLoadingTextWrapper(),
|
||||
);
|
||||
|
||||
Widget _buildLoadingTextWrapper() => Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: _buildLoadingTextChildren(),
|
||||
);
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: _buildLoadingTextChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildLoadingTextChildren() => [
|
||||
_buildLoadingText(),
|
||||
horizontalSpaceSmall,
|
||||
_buildIndicatorWrapper(),
|
||||
];
|
||||
_buildLoadingText(),
|
||||
horizontalSpaceSmall,
|
||||
_buildIndicatorWrapper(),
|
||||
];
|
||||
|
||||
Widget _buildLoadingText() =>
|
||||
Text('${label ?? LocaleKeys.loading.tr()} ...', style: style16P600);
|
||||
|
||||
Widget _buildIndicatorWrapper() => SizedBox(
|
||||
width: 16,
|
||||
height: 16,
|
||||
child: _buildIndicator(),
|
||||
);
|
||||
width: 16,
|
||||
height: 16,
|
||||
child: _buildIndicator(),
|
||||
);
|
||||
|
||||
Widget _buildIndicator() =>
|
||||
const CustomCircularProgressIndicator(color: kcPrimaryColor);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,12 @@
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter_carousel_widget/flutter_carousel_widget.dart';
|
||||
import 'package:flutter_svg/svg.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/second_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/enmus.dart';
|
||||
import '../../common/translations/locale_keys.g.dart';
|
||||
import 'startup_viewmodel.dart';
|
||||
|
||||
class StartupView extends StackedView<StartupViewModel> {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import '../../../app/app.router.dart';
|
|||
import '../../../models/user.dart';
|
||||
import '../../../services/api_service.dart';
|
||||
import '../../../services/image_downloader_service.dart';
|
||||
import '../../../services/in_app_notification_service.dart';
|
||||
import '../../../services/localization_service.dart';
|
||||
import '../../../services/status_checker_service.dart';
|
||||
import '../../common/enmus.dart';
|
||||
|
|
@ -29,7 +28,6 @@ class StartupViewModel extends ReactiveViewModel {
|
|||
|
||||
final _imageDownloaderService = locator<ImageDownloaderService>();
|
||||
|
||||
|
||||
@override
|
||||
List<ListenableServiceMixin> get listenableServices =>
|
||||
[_onboardingService, _authenticationService];
|
||||
|
|
@ -144,6 +142,4 @@ class StartupViewModel extends ReactiveViewModel {
|
|||
await replaceWithFailure();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@ import '../common/ui_helpers.dart';
|
|||
import 'custom_bottom_sheet.dart';
|
||||
import 'custom_elevated_button.dart';
|
||||
|
||||
class CancelLearnPracticeSheet extends StatelessWidget {
|
||||
class CancelPracticeSheet extends StatelessWidget {
|
||||
final String user;
|
||||
final GestureTapCallback? onClose;
|
||||
final GestureTapCallback? onCancel;
|
||||
final GestureTapCallback? onContinue;
|
||||
|
||||
const CancelLearnPracticeSheet(
|
||||
const CancelPracticeSheet(
|
||||
{super.key,
|
||||
this.onClose,
|
||||
this.onCancel,
|
||||
|
|
@ -1,40 +1,76 @@
|
|||
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/views/course_practice/course_practice_viewmodel.dart';
|
||||
|
||||
import '../common/helper_functions.dart';
|
||||
import '../common/ui_helpers.dart';
|
||||
|
||||
class CountdownTimer extends StatelessWidget {
|
||||
const CountdownTimer({super.key});
|
||||
class CountdownTimer extends ViewModelWidget<CoursePracticeViewModel> {
|
||||
final String? time;
|
||||
const CountdownTimer({super.key, this.time});
|
||||
|
||||
Future<void> _stopRecording(CoursePracticeViewModel viewModel)async{
|
||||
await viewModel.stopRecording();
|
||||
viewModel.setNextButton();
|
||||
}
|
||||
@override
|
||||
Widget build(BuildContext context) => _buildContainer();
|
||||
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||
_buildContainer(viewModel);
|
||||
|
||||
Widget _buildContainer() => Container(
|
||||
width: 100,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(25),
|
||||
color: kcPrimaryColor.withValues(alpha: 0.1),
|
||||
Widget _buildContainer(CoursePracticeViewModel viewModel) => Container(
|
||||
width: 100,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(25),
|
||||
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(
|
||||
Icons.timer_outlined,
|
||||
color: kcPrimaryColor,
|
||||
);
|
||||
Icons.timer_outlined,
|
||||
color: kcPrimaryColor,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -191,14 +191,14 @@ class CourseModuleTileLarge extends ViewModelWidget<CourseModuleViewModel> {
|
|||
shrinkWrap: true,
|
||||
itemCount: lessons.length,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) => _buildCourseModuleCard(
|
||||
itemBuilder: (context, index) => _buildCourseLessonTile(
|
||||
lesson: lessons[index],
|
||||
onVideoTap: () async =>
|
||||
await viewModel.navigateToCourseLessonDetail(lessons[index]),
|
||||
onPracticeTap: () {}),
|
||||
onPracticeTap: ()async=>await viewModel.navigateToCoursePractice(lessons[index].id ?? 0)),
|
||||
);
|
||||
|
||||
Widget _buildCourseModuleCard({
|
||||
Widget _buildCourseLessonTile({
|
||||
required CourseLesson lesson,
|
||||
required GestureTapCallback onVideoTap,
|
||||
required GestureTapCallback onPracticeTap,
|
||||
|
|
|
|||
|
|
@ -1,68 +1,89 @@
|
|||
import 'package:audioplayers/audioplayers.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/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 subtitle;
|
||||
|
||||
const CustomResponseCard(
|
||||
{super.key, required this.title, required this.subtitle});
|
||||
{super.key,
|
||||
required this.voice,
|
||||
required this.title,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => _buildContainer();
|
||||
Widget build(BuildContext context, CoursePracticeViewModel viewModel) =>
|
||||
_buildContainer(viewModel);
|
||||
|
||||
Widget _buildContainer() => Container(
|
||||
decoration: BoxDecoration(
|
||||
color: kcWhite,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
|
||||
child: _buildRow(),
|
||||
);
|
||||
Widget _buildContainer(CoursePracticeViewModel viewModel) => Container(
|
||||
decoration: BoxDecoration(
|
||||
color: kcWhite,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
|
||||
child: _buildRow(viewModel),
|
||||
);
|
||||
|
||||
Widget _buildRow() => Row(
|
||||
children: [_buildPlayButton(), _buildColumnWrapper()],
|
||||
);
|
||||
Widget _buildRow(CoursePracticeViewModel viewModel) => Row(
|
||||
children: [_buildPlayButton(viewModel), _buildColumnWrapper()],
|
||||
);
|
||||
|
||||
Widget _buildPlayButton() => ElevatedButton(
|
||||
onPressed: () {},
|
||||
style: const ButtonStyle(
|
||||
shape: WidgetStatePropertyAll(CircleBorder()),
|
||||
padding: WidgetStatePropertyAll(EdgeInsets.all(5)),
|
||||
shadowColor: WidgetStatePropertyAll(kcPrimaryColor),
|
||||
backgroundColor: WidgetStatePropertyAll(kcPrimaryColor),
|
||||
),
|
||||
child: _buildPlayIcon(),
|
||||
);
|
||||
Widget _buildPlayButton(CoursePracticeViewModel viewModel) => ElevatedButton(
|
||||
style: const ButtonStyle(
|
||||
shape: WidgetStatePropertyAll(CircleBorder()),
|
||||
padding: WidgetStatePropertyAll(EdgeInsets.all(5)),
|
||||
shadowColor: WidgetStatePropertyAll(kcPrimaryColor),
|
||||
backgroundColor: WidgetStatePropertyAll(kcPrimaryColor),
|
||||
),
|
||||
onPressed: () async =>
|
||||
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(
|
||||
Icons.play_arrow_rounded,
|
||||
size: 25,
|
||||
color: kcWhite,
|
||||
);
|
||||
Icons.play_arrow_rounded,
|
||||
size: 25,
|
||||
color: kcWhite,
|
||||
);
|
||||
|
||||
Widget _buildColumnWrapper() => Expanded(child: _buildColumn());
|
||||
Widget _buildPauseIcon() => const Icon(
|
||||
Icons.pause,
|
||||
size: 25,
|
||||
color: kcWhite,
|
||||
);
|
||||
|
||||
Widget _buildColumn() => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: _buildColumnChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildColumnChildren() => [_buildTitle(), _buildSubtitle()];
|
||||
Widget _buildColumnWrapper() => Expanded(child: _buildTitle());
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
title,
|
||||
maxLines: 1,
|
||||
softWrap: false,
|
||||
style: style12RP600,
|
||||
);
|
||||
|
||||
Widget _buildSubtitle() => Text(
|
||||
subtitle,
|
||||
maxLines: 1,
|
||||
softWrap: false,
|
||||
style: style12RP600,
|
||||
);
|
||||
title,
|
||||
maxLines: 1,
|
||||
softWrap: false,
|
||||
style: style12RP600,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:yimaru_app/ui/common/app_colors.dart';
|
||||
|
||||
class DuolingoAssessmentAppBar extends StatelessWidget {
|
||||
class DuolingoPracticeAppBar extends StatelessWidget {
|
||||
final String? title;
|
||||
final GestureTapCallback? onClose;
|
||||
|
||||
const DuolingoAssessmentAppBar({super.key, this.onClose, this.title});
|
||||
const DuolingoPracticeAppBar({super.key, this.onClose, this.title});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => _buildAppBar();
|
||||
|
|
@ -4,11 +4,11 @@ import 'package:yimaru_app/ui/common/app_colors.dart';
|
|||
import '../common/ui_helpers.dart';
|
||||
import 'custom_elevated_button.dart';
|
||||
|
||||
class DuolingoAssessmentCard extends StatelessWidget {
|
||||
class DuolingoPracticeCard extends StatelessWidget {
|
||||
final String title;
|
||||
final GestureTapCallback? onTap;
|
||||
|
||||
const DuolingoAssessmentCard({super.key, this.onTap, required this.title});
|
||||
const DuolingoPracticeCard({super.key, this.onTap, required this.title});
|
||||
|
||||
@override
|
||||
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/ui_helpers.dart';
|
||||
|
||||
class DuolingoAssessmentQuestionCard extends StatelessWidget {
|
||||
class DuolingoPracticeQuestionCard extends StatelessWidget {
|
||||
final String? title;
|
||||
final String? subtitle;
|
||||
|
||||
const DuolingoAssessmentQuestionCard({super.key, this.title, this.subtitle});
|
||||
const DuolingoPracticeQuestionCard({super.key, this.title, this.subtitle});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => _buildContainer();
|
||||
|
|
@ -4,9 +4,9 @@ import '../common/app_colors.dart';
|
|||
import '../common/ui_helpers.dart';
|
||||
import 'custom_elevated_button.dart';
|
||||
|
||||
class DuolingoAssessmentReviewSection extends StatelessWidget {
|
||||
class DuolingoPracticeReviewSection extends StatelessWidget {
|
||||
final GestureTapCallback? onTap;
|
||||
const DuolingoAssessmentReviewSection({super.key, this.onTap});
|
||||
const DuolingoPracticeReviewSection({super.key, this.onTap});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => _buildReviewContainer();
|
||||
|
|
@ -43,7 +43,7 @@ class LearnPracticeAnswerCard extends ViewModelWidget<LearnPracticeViewModel> {
|
|||
shadowColor: WidgetStatePropertyAll(kcPrimaryColor),
|
||||
backgroundColor: WidgetStatePropertyAll(kcPrimaryColor),
|
||||
),
|
||||
onPressed: () async => viewModel.busyObject == answer['busy_object'] &&
|
||||
onPressed: () async =>
|
||||
viewModel.player.state == PlayerState.playing
|
||||
? await viewModel.pauseAudio()
|
||||
: await viewModel.playResult(answer: answer, voice: voice),
|
||||
|
|
@ -51,7 +51,7 @@ class LearnPracticeAnswerCard extends ViewModelWidget<LearnPracticeViewModel> {
|
|||
);
|
||||
|
||||
Widget _buildButtonState(LearnPracticeViewModel viewModel) =>
|
||||
viewModel.busyObject == answer['busy_object'] &&
|
||||
|
||||
viewModel.playing == voice
|
||||
? viewModel.busy(answer['busy_object'])
|
||||
? _buildProgressIndicatorWrapper()
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ import 'package:yimaru_app/ui/widgets/custom_linear_progress_indicator.dart';
|
|||
|
||||
import '../common/app_colors.dart';
|
||||
|
||||
class ListenableAssessmentCard extends StatelessWidget {
|
||||
const ListenableAssessmentCard({super.key});
|
||||
class ListenablePracticeCard extends StatelessWidget {
|
||||
const ListenablePracticeCard({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => _buildRow();
|
||||
|
|
@ -11,13 +11,11 @@ class NotificationCard extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) => _buildContainer();
|
||||
|
||||
|
||||
|
||||
Widget _buildContainer() => Container(
|
||||
height: 100,
|
||||
width: double.maxFinite,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 15),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15,vertical: 15),
|
||||
height: 100,
|
||||
width: double.maxFinite,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 15),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
color: (notification.isRead ?? false)
|
||||
|
|
@ -33,15 +31,12 @@ class NotificationCard extends StatelessWidget {
|
|||
);
|
||||
|
||||
Widget _buildRow() => Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: _buildRowChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildRowChildren()=> [
|
||||
_buildIcon(),
|
||||
horizontalSpaceSmall,
|
||||
_buildColumnWrapper()
|
||||
];
|
||||
List<Widget> _buildRowChildren() =>
|
||||
[_buildIcon(), horizontalSpaceSmall, _buildColumnWrapper()];
|
||||
|
||||
Widget _buildIcon() => const Icon(
|
||||
Icons.notifications_none,
|
||||
|
|
@ -49,15 +44,14 @@ class NotificationCard extends StatelessWidget {
|
|||
color: kcMediumGrey,
|
||||
);
|
||||
|
||||
Widget _buildColumnWrapper()=> Expanded(child: _buildColumn());
|
||||
Widget _buildColumnWrapper() => Expanded(child: _buildColumn());
|
||||
|
||||
Widget _buildColumn() => Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: _buildColumnChildren(),
|
||||
);
|
||||
|
||||
List<Widget> _buildColumnChildren() =>
|
||||
[ _buildTitle(), _buildSubtitle()];
|
||||
List<Widget> _buildColumnChildren() => [_buildTitle(), _buildSubtitle()];
|
||||
|
||||
Widget _buildTitle() => Text(
|
||||
notification.payload?.headline ?? '',
|
||||
|
|
|
|||
|
|
@ -1,34 +1,33 @@
|
|||
import 'package:badges/badges.dart' as badges;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:badges/badges.dart';
|
||||
import 'package:yimaru_app/ui/common/ui_helpers.dart';
|
||||
import '../common/app_colors.dart';
|
||||
|
||||
class NotificationIcon extends StatelessWidget {
|
||||
final String count;
|
||||
final GestureTapCallback? onTap;
|
||||
const NotificationIcon({super.key,this.onTap,required this.count});
|
||||
const NotificationIcon({super.key, this.onTap, required this.count});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => _buildNotificationIconWrapper();
|
||||
|
||||
|
||||
Widget _buildNotificationIconWrapper() => Align(
|
||||
alignment: Alignment.bottomRight,
|
||||
child: _buildNotificationButton());
|
||||
alignment: Alignment.bottomRight, child: _buildNotificationButton());
|
||||
|
||||
Widget _buildNotificationButton() =>
|
||||
GestureDetector(
|
||||
Widget _buildNotificationButton() => GestureDetector(
|
||||
onTap: onTap,
|
||||
child: _buildNotificationBadge(),
|
||||
);
|
||||
|
||||
Widget _buildNotificationBadge()=> badges.Badge(
|
||||
badgeContent: Text(count,style: style12W600,),
|
||||
|
||||
child: _buildNotificationIcon(),
|
||||
);
|
||||
Widget _buildNotificationBadge() => badges.Badge(
|
||||
badgeContent: Text(
|
||||
count,
|
||||
style: style12W600,
|
||||
),
|
||||
child: _buildNotificationIcon(),
|
||||
);
|
||||
Widget _buildNotificationIcon() => const Icon(
|
||||
Icons.notifications_none,
|
||||
color: kcDarkGrey,
|
||||
);
|
||||
Icons.notifications_none,
|
||||
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/small_app_bar.dart';
|
||||
|
||||
import '../../../common/app_colors.dart';
|
||||
import '../../../common/translations/locale_keys.g.dart';
|
||||
import '../../../common/ui_helpers.dart';
|
||||
import '../common/app_colors.dart';
|
||||
import '../common/translations/locale_keys.g.dart';
|
||||
import '../common/ui_helpers.dart';
|
||||
|
||||
class LearnLoadingScreen extends StatelessWidget {
|
||||
class PracticeLoadingScreen extends StatelessWidget {
|
||||
final bool isEmpty;
|
||||
final bool isLoading;
|
||||
final GestureTapCallback? onPop;
|
||||
final GestureTapCallback? onTap;
|
||||
|
||||
const LearnLoadingScreen(
|
||||
const PracticeLoadingScreen(
|
||||
{super.key,
|
||||
this.onTap,
|
||||
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