Compare commits

...

3 Commits

Author SHA1 Message Date
b8f9493bed Merge branch 'release/0.1.12'
- fix(learn_practice): Add remaining pages.
- fix: Apply UAT comments.
2026-05-05 08:11:11 +03:00
e55eaf64a6 - fix(learn_practice): Add remaining pages.
- fix: Apply UAT comments.
2026-05-05 08:08:48 +03:00
9c2808297a Merge tag '0.1.11' into develop
- fix(player): Change the video player into chewie for better performance
2026-05-01 15:37:20 +03:00
13 changed files with 224 additions and 41 deletions

View File

@ -279,6 +279,7 @@ class AssessmentViewModel extends BaseViewModel {
Future<void> _getAssessments() async {
if (await _statusChecker.checkConnection()) {
_assessments = await _apiService.getAssessments();
_assessments.reversed;
}
}

View File

@ -4,6 +4,7 @@ import 'package:yimaru_app/ui/common/enmus.dart';
import 'package:yimaru_app/ui/views/learn_practice/screens/finish_learn_practice_screen.dart';
import 'package:yimaru_app/ui/views/learn_practice/screens/learn_loading_screen.dart';
import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_completion_screen.dart';
import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_description_screen.dart';
import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_result_screen.dart';
import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_questions_screen.dart';
import 'package:yimaru_app/ui/views/learn_practice/screens/learn_practice_intro_screen.dart';
@ -102,17 +103,21 @@ class LearnPracticeView extends StackedView<LearnPracticeViewModel> {
index: viewModel.currentPage, children: _buildScreens(viewModel));
List<Widget> _buildScreens(LearnPracticeViewModel viewModel) => [
_buildLearnPracticeIntroScreen(viewModel),
_buildLearnPracticeQuestionsScreen(viewModel),
_buildLearnPracticeIntroScreen(),
_buildLearnPracticeElementsScreen(),
_buildLearnPracticeQuestionsScreen(),
_buildFinishLearnPracticeScreen(),
_buildLearnPracticeResultScreen(),
_buildLearnPracticeCompletionScreen()
];
Widget _buildLearnPracticeIntroScreen(LearnPracticeViewModel viewModel) =>
Widget _buildLearnPracticeIntroScreen() =>
const LearnPracticeIntroScreen();
Widget _buildLearnPracticeQuestionsScreen(LearnPracticeViewModel viewModel) =>
Widget _buildLearnPracticeElementsScreen() =>
const LearnPracticeDescriptionScreen();
Widget _buildLearnPracticeQuestionsScreen() =>
const LearnPracticeQuestionsScreen();
Widget _buildFinishLearnPracticeScreen() => const FinishLearnPracticeScreen();

View File

@ -240,6 +240,13 @@ class LearnPracticeViewModel extends ReactiveViewModel {
}
}
// Reset
Future<void> reset() async {
goTo(0);
_answers.clear();
questionSetController.jumpToPage(0);
}
// Navigation
void pop() => _navigationService.back();

View File

@ -12,6 +12,8 @@ class FinishLearnPracticeScreen
extends ViewModelWidget<LearnPracticeViewModel> {
const FinishLearnPracticeScreen({super.key});
Future<void> _reset(LearnPracticeViewModel viewModel)async =>await viewModel.reset();
@override
Widget build(BuildContext context, LearnPracticeViewModel viewModel) =>
_buildScaffoldWrapper(viewModel);
@ -109,7 +111,7 @@ class FinishLearnPracticeScreen
borderRadius: 12,
foregroundColor: kcWhite,
text: 'Continue Practice',
onTap: () => viewModel.goTo(5),
onTap: () => viewModel.goTo(4),
backgroundColor: kcPrimaryColor,
);
@ -120,7 +122,7 @@ class FinishLearnPracticeScreen
text: 'Practice Again',
backgroundColor: kcWhite,
borderColor: kcPrimaryColor,
onTap: () => viewModel.goTo(1),
foregroundColor: kcPrimaryColor,
onTap: ()async => await _reset(viewModel) ,
);
}

View File

@ -0,0 +1,175 @@
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/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/custom_elevated_button.dart';
import '../../../widgets/small_app_bar.dart';
import '../../../widgets/speaking_partner_image.dart';
class LearnPracticeDescriptionScreen
extends ViewModelWidget<LearnPracticeViewModel> {
const LearnPracticeDescriptionScreen({super.key});
Future<void> _cancel(LearnPracticeViewModel viewModel) async {
await viewModel.stopRecording();
viewModel.pop();
viewModel.pop();
}
Future<void> _showSheet(
{required BuildContext context,
required LearnPracticeViewModel viewModel}) async =>
await showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: kcTransparent,
builder: (cxt) => _buildSheet(viewModel),
);
@override
Widget build(BuildContext context, LearnPracticeViewModel viewModel) =>
_buildScaffoldWrapper(context: context, viewModel: viewModel);
Widget _buildScaffoldWrapper(
{required BuildContext context,
required LearnPracticeViewModel viewModel}) =>
Scaffold(
backgroundColor: kcBackgroundColor,
body: _buildScaffold(context: context, viewModel: viewModel),
);
Widget _buildScaffold(
{required BuildContext context,
required LearnPracticeViewModel viewModel}) =>
SafeArea(
child: _buildColumnWrapper(context: context, viewModel: viewModel));
Widget _buildColumnWrapper(
{required BuildContext context,
required LearnPracticeViewModel viewModel}) =>
Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: _buildColumn(context: context, viewModel: viewModel),
);
Widget _buildColumn(
{required BuildContext context,
required LearnPracticeViewModel viewModel}) =>
Column(
children: [
verticalSpaceMedium,
_buildAppBar(context: context, viewModel: viewModel),
verticalSpaceMedium,
_buildBodyColumnWrapper(viewModel),
],
);
Widget _buildAppBar(
{required BuildContext context,
required LearnPracticeViewModel viewModel}) =>
SmallAppBar(
showBackButton: true,
onPop: () async =>
await _showSheet(context: context, viewModel: viewModel),
);
Widget _buildSheet(LearnPracticeViewModel viewModel) =>
CancelLearnPracticeSheet(
onClose: viewModel.pop,
onContinue: viewModel.pop,
user: viewModel.user?.firstName ?? '',
onCancel: () async => await _cancel(viewModel),
);
Widget _buildBodyColumnWrapper(LearnPracticeViewModel viewModel) => Expanded(
child: _buildBodyColumn(viewModel),
);
Widget _buildBodyColumn(LearnPracticeViewModel viewModel) => Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: _buildBodyColumnChildren(viewModel),
);
List<Widget> _buildBodyColumnChildren(LearnPracticeViewModel viewModel) => [
_buildPracticeColumnWrapper(viewModel),
_buildContinueButtonWrapper(viewModel)
];
Widget _buildPracticeColumnWrapper(LearnPracticeViewModel viewModel) =>
Expanded(child: _buildPracticeColumnScrollView(viewModel));
Widget _buildPracticeColumnScrollView(LearnPracticeViewModel viewModel) =>
SingleChildScrollView(
child: _buildPracticeColumn(viewModel),
);
Widget _buildPracticeColumn(LearnPracticeViewModel viewModel) => Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: _buildPracticeColumnChildren(viewModel),
);
List<Widget> _buildPracticeColumnChildren(LearnPracticeViewModel viewModel) =>
[
_buildTitle(viewModel),
verticalSpaceMedium,
_buildSubtitle(viewModel),
verticalSpaceMedium,
_buildImageContainer(viewModel),
];
Widget _buildTitle(LearnPracticeViewModel viewModel) => Text.rich(
TextSpan(text: 'Speaking Practice: ', style: style14DG600, children: [
TextSpan(
style: style14MG400,
text: viewModel.practices.first.title ?? '',
)
]),
);
Widget _buildSubtitle(LearnPracticeViewModel viewModel) => Text(
viewModel.practices.first.storyDescription ?? '',
style: style14DG400,
textAlign: TextAlign.center,
);
Widget _buildImageContainer(LearnPracticeViewModel viewModel) => Container(
height: 200,
width: double.maxFinite,
margin: const EdgeInsets.symmetric(horizontal: 10),
child: _buildImageWrapper(viewModel),
);
Widget _buildImageWrapper(LearnPracticeViewModel viewModel) => ClipRRect(
borderRadius: BorderRadius.circular(5),
child: _buildImage(viewModel),
);
Widget _buildImage(LearnPracticeViewModel viewModel) => CachedNetworkImage(
fit: BoxFit.cover,
width: double.maxFinite,
imageUrl: getReadableUrl(viewModel.practices.first.storyImage ?? '') ?? '',
);
Widget _buildContinueButtonWrapper(LearnPracticeViewModel viewModel) =>
Padding(
padding: const EdgeInsets.only(bottom: 50),
child: _buildContinueButton(viewModel),
);
Widget _buildContinueButton(LearnPracticeViewModel viewModel) =>
CustomElevatedButton(
height: 55,
borderRadius: 12,
text: 'Start Practice',
foregroundColor: kcWhite,
onTap: () => viewModel.goTo(2),
backgroundColor: kcPrimaryColor,
);
}

View File

@ -71,7 +71,6 @@ class LearnPracticeIntroScreen extends ViewModelWidget<LearnPracticeViewModel> {
required LearnPracticeViewModel viewModel}) =>
SmallAppBar(
showBackButton: true,
title: 'Practice Speaking',
onPop: () async =>
await _showSheet(context: context, viewModel: viewModel),
);

View File

@ -14,10 +14,7 @@ class LearnPracticeResultScreen
extends ViewModelWidget<LearnPracticeViewModel> {
const LearnPracticeResultScreen({super.key});
void _navigate(LearnPracticeViewModel viewModel) {
viewModel.questionSetController.jumpToPage(0);
viewModel.goTo(0);
}
void _navigate(LearnPracticeViewModel viewModel) async=>await viewModel.reset();
Future<void> _cancel(LearnPracticeViewModel viewModel) async {
await viewModel.stopRecording();
@ -147,7 +144,7 @@ class LearnPracticeResultScreen
text: 'Continue',
borderRadius: 12,
foregroundColor: kcWhite,
onTap: () => viewModel.goTo(4),
onTap: () => viewModel.goTo(5),
backgroundColor: kcPrimaryColor,
);

View File

@ -22,9 +22,9 @@ class StartLearnPracticeScreen extends ViewModelWidget<LearnPracticeViewModel> {
viewModel.pop();
}
void _start(LearnPracticeViewModel viewModel) {
viewModel.playVoicePrompt(question);
}
Future<void> _start(LearnPracticeViewModel viewModel)async =>
await viewModel.playVoicePrompt(question);
Future<void> _showSheet(
{required BuildContext context,
@ -115,7 +115,7 @@ class StartLearnPracticeScreen extends ViewModelWidget<LearnPracticeViewModel> {
Widget _buildStartButtonContainer(LearnPracticeViewModel viewModel) =>
GestureDetector(
onTap: () => _start(viewModel),
onTap: () async=>await _start(viewModel),
child: _buildStartButton(),
);
@ -142,16 +142,16 @@ class StartLearnPracticeScreen extends ViewModelWidget<LearnPracticeViewModel> {
startAngle: 0.0,
center: Alignment.center,
colors: [
kcPrimaryColor.withOpacity(0.3),
kcIndigo.withOpacity(0.2),
kcIndigo.withOpacity(0.3),
kcIndigo.withOpacity(0.4),
kcIndigo.withOpacity(0.5),
kcPrimaryColor.withOpacity(0.5),
kcPrimaryColor.withOpacity(0.4),
kcPrimaryColor.withOpacity(0.3),
kcPrimaryColor.withOpacity(0.2),
kcPrimaryColor.withOpacity(0.5),
kcPrimaryColor.withValues(alpha:0.3),
kcIndigo.withValues(alpha:0.2),
kcIndigo.withValues(alpha:0.3),
kcIndigo.withValues(alpha:0.4),
kcIndigo.withValues(alpha:0.5),
kcPrimaryColor.withValues(alpha:0.5),
kcPrimaryColor.withValues(alpha:0.4),
kcPrimaryColor.withValues(alpha:0.3),
kcPrimaryColor.withValues(alpha: 0.2),
kcPrimaryColor.withValues(alpha:0.5),
],
// quarterly spread
),

View File

@ -92,7 +92,7 @@ class CourseProgressSection extends ViewModelWidget<ProgressViewModel> {
[_buildLearningStatus(), verticalSpaceSmall, _buildActionButton()];
Widget _buildProgressIndicator() => const CustomLinearProgressIndicator(
progress: 0.5,
progress: 0,
activeColor: kcPrimaryColor,
backgroundColor: kcVeryLightGrey,
);
@ -111,10 +111,10 @@ class CourseProgressSection extends ViewModelWidget<ProgressViewModel> {
];
Widget _buildWatchedVideos() =>
const CustomColumn(title: '15/25', subtitle: 'Videos');
const CustomColumn(title: '0/0', subtitle: 'Videos');
Widget _buildCompletedPractices() =>
const CustomColumn(title: '8/12', subtitle: 'Practices');
const CustomColumn(title: '0/0', subtitle: 'Practices');
Widget _buildActionButton() => const CustomElevatedButton(
height: 15,

View File

@ -158,7 +158,7 @@ class LearnCourseTile extends ViewModelWidget<LearnCourseViewModel> {
height: 15,
borderRadius: 12,
onTap: onPracticeTap,
text: 'View Practices',
text: 'Take Practices',
backgroundColor: kcWhite,
borderColor: kcPrimaryColor,
foregroundColor: kcPrimaryColor,

View File

@ -6,6 +6,7 @@ import 'package:yimaru_app/ui/widgets/mini_thumbnail.dart';
import '../common/app_colors.dart';
import '../common/helper_functions.dart';
import '../common/ui_helpers.dart';
import 'custom_elevated_button.dart';
import 'custom_linear_progress_indicator.dart';

View File

@ -55,13 +55,9 @@ class LearningProgressCard extends StatelessWidget {
color: kcPrimaryColor,
);
Widget _buildTitle() => const Text(
Widget _buildTitle() => Text(
'Learn English',
style: TextStyle(
fontSize: 16,
color: kcDarkGrey,
fontWeight: FontWeight.w600,
),
style: style16DG600,
);
Widget _buildSubtitle() => const Text(
@ -71,7 +67,7 @@ class LearningProgressCard extends StatelessWidget {
);
Widget _buildProgressIndicator() => const CustomLinearProgressIndicator(
progress: 0.5,
progress: 0,
activeColor: kcPrimaryColor,
backgroundColor: kcVeryLightGrey,
);
@ -89,11 +85,11 @@ class LearningProgressCard extends StatelessWidget {
];
Widget _buildWatchedVideos() => const Expanded(
child: CustomColumn(title: '120', subtitle: 'Videos Watched'));
child: CustomColumn(title: '0', subtitle: 'Videos Watched'));
Widget _buildCompletedPractices() => const Expanded(
child: CustomColumn(title: '85', subtitle: 'Practices Completed'));
child: CustomColumn(title: '0', subtitle: 'Practices Completed'));
Widget _buildTakenQuizzes() => const Expanded(
child: CustomColumn(title: '45', subtitle: 'Quizzes Taken'));
child: CustomColumn(title: '0', subtitle: 'Quizzes Taken'));
Widget _buildActionButton() => const CustomElevatedButton(
height: 15,

View File

@ -1,5 +1,5 @@
name: yimaru_app
version: 0.1.11+13
version: 0.1.12+14
publish_to: 'none'
description: A new Flutter project.