Merge branch 'auto-refresh-levelList' into 'master'

refactor: auto refresh after submit feedback and app configuration

See merge request profile-image/kedaireka/polinema-adapative-learning/mobile-adaptive-learning!4
This commit is contained in:
Naresh Pratista 2024-11-06 06:31:11 +00:00
commit a1c271408a
18 changed files with 125 additions and 143 deletions

View File

@ -3,7 +3,7 @@
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<application
android:label="english_learning"
android:label="SEALS"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity

Binary file not shown.

Before

Width:  |  Height:  |  Size: 544 B

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

@ -1 +1 @@
const String baseUrl = 'https://c62a-139-255-101-170.ngrok-free.app/';
const String baseUrl = 'https://7558-114-6-25-184.ngrok-free.app/';

View File

@ -51,9 +51,6 @@ class HistoryScreen extends StatelessWidget {
}
Widget _buildContent(BuildContext context, HistoryProvider historyProvider) {
print("isLoading: ${historyProvider.isLoading}");
print("error: ${historyProvider.error}");
print("historyLength: ${historyProvider.learningHistory.length}");
if (historyProvider.isLoading) {
return const Center(child: CircularProgressIndicator());
}

View File

@ -12,12 +12,16 @@ class ExerciseScreen extends StatefulWidget {
final String? levelId;
final String studentLearningId;
final bool isReview;
final String topicId; // Tambahkan ini
final String topicTitle; // Tambahkan ini
const ExerciseScreen({
super.key,
required this.levelId,
required this.studentLearningId,
this.isReview = false,
required this.topicId,
required this.topicTitle,
});
@override
@ -142,6 +146,8 @@ class _ExerciseScreenState extends State<ExerciseScreen> {
ExerciseNavigator(
onScrollToTop: _scrollToTop,
isReview: widget.isReview,
topicId: widget.topicId,
topicTitle: widget.topicTitle,
),
const SizedBox(height: 32),
],

View File

@ -11,11 +11,15 @@ import 'package:provider/provider.dart';
class ExerciseNavigator extends StatelessWidget {
final VoidCallback? onScrollToTop;
final bool isReview;
final String topicId;
final String topicTitle;
const ExerciseNavigator({
super.key,
required this.onScrollToTop,
this.isReview = false,
required this.topicId,
required this.topicTitle,
});
@override
@ -43,6 +47,8 @@ class ExerciseNavigator extends StatelessWidget {
score: int.tryParse(result['SCORE'].toString()) ?? 0,
isCompleted: result['IS_PASS'] == 1,
stdLearningId: result['STUDENT_LEARNING_ID']?.toString(),
topicId: topicId,
topicTitle: topicTitle,
),
),
);

View File

@ -2,15 +2,20 @@ import 'package:english_learning/core/widgets/global_button.dart';
import 'package:english_learning/core/utils/styles/theme.dart';
import 'package:english_learning/features/learning/modules/exercises/providers/exercise_provider.dart';
import 'package:english_learning/features/learning/modules/feedback/widgets/feedback_dialog.dart';
import 'package:english_learning/features/learning/modules/level/screens/level_list_screen.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class FeedbackScreen extends StatefulWidget {
final String stdLearningId;
final String topicId;
final String topicTitle;
const FeedbackScreen({
super.key,
required this.stdLearningId,
required this.topicId,
required this.topicTitle,
});
@override
@ -75,7 +80,16 @@ class _FeedbackScreenState extends State<FeedbackScreen> {
return FeedbackDialog(
onSubmit: () {
Navigator.of(dialogContext).pop(); // Close the dialog
Navigator.of(context).pop(); // Return to the previous screen
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => LevelListScreen(
topicId: widget.topicId,
topicTitle: widget.topicTitle,
),
),
(route) => false, // This removes all previous routes
);
},
);
},
@ -189,16 +203,15 @@ class _FeedbackScreenState extends State<FeedbackScreen> {
backgroundColor: Colors.transparent,
borderColor: AppColors.blueColor,
onPressed: () {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => LevelListScreen(
// topicId: widget.topicId!,
// topicTitle: widget.topicTitle!,
// ),
// ),
// );
Navigator.of(context).pop(true);
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => LevelListScreen(
topicId: widget.topicId,
topicTitle: widget.topicTitle,
),
),
);
},
)
],

View File

@ -46,31 +46,6 @@ class LevelProvider with ChangeNotifier {
orElse: () => throw Exception('Pretest not found'));
}
// int getLevelScore(String levelId) {
// final level = _levels.firstWhere((level) => level.idLevel == levelId,
// orElse: () => const Level(
// idLevel: '',
// idTopic: '',
// idSection: '',
// nameSection: '',
// nameTopic: '',
// nameLevel: '',
// content: '',
// isPretest: 0,
// timeLevel: '',
// ));
// return level.score ?? 0;
// }
// bool isLevelAllowed(String levelName) {
// return _unlockedLevels.contains(levelName);
// }
// bool isLevelCompleted(String levelId) {
// return _lastCompletedLevel != null &&
// _lastCompletedLevel!['ID_LEVEL'] == levelId;
// }
bool isPretestFinished(String levelId) {
return _levels.any(
(level) => level.idLevel == levelId && level.idStudentLearning != null);
@ -98,38 +73,4 @@ class LevelProvider with ChangeNotifier {
return _levels.any(
(level) => level.idLevel == levelId && level.idStudentLearning != null);
}
// bool isLevelAllowed(String levelId) {
// if (_lastCompletedLevel == null) {
// // Jika tidak ada level yang selesai, hanya pretest yang diizinkan
// return levelId == _levels.first.idLevel;
// }
// String lastCompletedLevelId = _lastCompletedLevel!['ID_LEVEL'];
// int lastCompletedIndex =
// _levels.indexWhere((level) => level.idLevel == lastCompletedLevelId);
// int currentIndex = _levels.indexWhere((level) => level.idLevel == levelId);
// // Level diizinkan jika indeksnya kurang dari atau sama dengan indeks terakhir yang selesai + 1
// return currentIndex <= lastCompletedIndex + 1;
// }
// bool isLevelAllowed(int levelIndex) {
// if (levelIndex == 0) return true; // Pretest is always allowed
// if (_lastCompletedLevel == null)
// return levelIndex ==
// 1; // If no level completed, only first level is allowed
// String lastCompletedLevelId = _lastCompletedLevel!['ID_LEVEL'];
// int lastCompletedIndex =
// _levels.indexWhere((level) => level.idLevel == lastCompletedLevelId);
// return levelIndex <= lastCompletedIndex + 2;
// }
// bool isLevelFinished(int levelIndex) {
// return _levels[levelIndex].idStudentLearning != null;
// }
// int getLevelScore(int levelIndex) {
// return _levels[levelIndex].score ?? 0;
// }
}

View File

@ -78,43 +78,46 @@ class _LevelListScreenState extends State<LevelListScreen> {
.where((level) => level.isPretest == 0)
.toList();
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
PretestCard(
pretest: pretest,
score: levelProvider.getPretestScore(pretest.idLevel),
isCompleted:
levelProvider.isPretestFinished(pretest.idLevel),
isAllowed: levelProvider.isLevelAllowed(pretest.idLevel),
),
const SizedBox(height: 12),
Expanded(
child: GridView.builder(
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 1.0,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
),
itemCount: otherLevels.length,
itemBuilder: (context, index) {
final level = otherLevels[index];
return LevelCard(
level: level,
isAllowed:
levelProvider.isLevelAllowed(level.idLevel),
score: levelProvider.getLevelScore(level.idLevel),
isCompleted:
levelProvider.isLevelCompleted(level.idLevel),
);
},
return RefreshIndicator(
onRefresh: _fetchLevels,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
PretestCard(
pretest: pretest,
score: levelProvider.getPretestScore(pretest.idLevel),
isCompleted:
levelProvider.isPretestFinished(pretest.idLevel),
isAllowed: levelProvider.isLevelAllowed(pretest.idLevel),
),
),
],
const SizedBox(height: 12),
Expanded(
child: GridView.builder(
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 1.0,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
),
itemCount: otherLevels.length,
itemBuilder: (context, index) {
final level = otherLevels[index];
return LevelCard(
level: level,
isAllowed:
levelProvider.isLevelAllowed(level.idLevel),
score: levelProvider.getLevelScore(level.idLevel),
isCompleted:
levelProvider.isLevelCompleted(level.idLevel),
);
},
),
),
],
),
),
);
}

View File

@ -141,6 +141,8 @@ class LevelCard extends StatelessWidget {
MaterialPageRoute(
builder: (context) => MaterialScreen(
levelId: level.idLevel,
topicId: level.idTopic,
topicTitle: level.nameTopic,
),
),
);

View File

@ -37,6 +37,8 @@ class PretestCard extends StatelessWidget {
levelId: pretest.idLevel,
isReview: true,
studentLearningId: pretest.idStudentLearning,
topicId: pretest.idTopic,
topicTitle: pretest.nameTopic,
),
),
);
@ -48,6 +50,8 @@ class PretestCard extends StatelessWidget {
builder: (context) => MaterialScreen(
levelId: pretest.idLevel,
isReview: false,
topicId: pretest.idTopic,
topicTitle: pretest.nameTopic,
),
),
);
@ -141,31 +145,6 @@ class PretestCard extends StatelessWidget {
width: double.infinity,
height: 36,
color: isFinished ? Colors.green : AppColors.yellowButtonColor,
// onPressed: () {
// if (!isCompleted) {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => MaterialScreen(
// levelId: pretest.idLevel,
// ),
// ),
// );
// }
// // Jika isCompleted true, tidak melakukan apa-apa
// },
// onPressed: isAllowed
// ? () {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => MaterialScreen(
// levelId: pretest.idLevel,
// ),
// ),
// );
// }
// : () {},
onPressed: navigateToMaterial,
),
],

View File

@ -16,12 +16,16 @@ class MaterialScreen extends StatefulWidget {
final String levelId;
final bool isReview;
final String? studentLearningId;
final String topicId;
final String topicTitle;
const MaterialScreen({
super.key,
required this.levelId,
this.isReview = false,
this.studentLearningId,
required this.topicId,
required this.topicTitle,
});
@override
@ -100,6 +104,8 @@ class _MaterialScreenState extends State<MaterialScreen>
levelId: widget.levelId,
studentLearningId: studentLearningId,
isReview: widget.isReview,
topicId: widget.topicId,
topicTitle: widget.topicTitle,
),
),
);

View File

@ -10,6 +10,8 @@ class ResultScreen extends StatelessWidget {
final int score;
final bool isCompleted;
final String? stdLearningId;
final String topicId;
final String topicTitle;
const ResultScreen({
super.key,
@ -18,6 +20,8 @@ class ResultScreen extends StatelessWidget {
required this.score,
required this.isCompleted,
this.stdLearningId,
required this.topicId,
required this.topicTitle,
});
@override
@ -37,18 +41,24 @@ class ResultScreen extends StatelessWidget {
currentLevel: currentLevel,
score: score,
stdLearningId: stdLearningId ?? '',
topicId: topicId, // Tambahkan ini
topicTitle: topicTitle, // Tambahkan ini
)
else if (nextLevel != currentLevel)
JumpResultWidget(
nextLevel: nextLevel,
score: score,
stdLearningId: stdLearningId ?? '',
topicId: topicId, // Tambahkan ini
topicTitle: topicTitle, // Tambahkan ini
)
else
DownResultWidget(
nextLevel: nextLevel,
score: score,
stdLearningId: stdLearningId ?? '',
topicId: topicId, // Tambahkan ini
topicTitle: topicTitle, // Tambahkan ini
),
],
),

View File

@ -8,12 +8,16 @@ class CompleteResultWidget extends StatelessWidget {
final String? currentLevel;
final int? score;
final String stdLearningId;
final String topicId;
final String topicTitle;
const CompleteResultWidget({
super.key,
required this.currentLevel,
required this.score,
required this.stdLearningId,
required this.topicId,
required this.topicTitle,
});
@override
@ -57,6 +61,8 @@ class CompleteResultWidget extends StatelessWidget {
MaterialPageRoute(
builder: (context) => FeedbackScreen(
stdLearningId: stdLearningId,
topicId: topicId, // Tambahkan ini
topicTitle: topicTitle, // Tambahkan ini
),
),
);

View File

@ -8,12 +8,16 @@ class DownResultWidget extends StatelessWidget {
final String? nextLevel;
final int? score;
final String stdLearningId;
final String topicId;
final String topicTitle;
const DownResultWidget({
super.key,
required this.nextLevel,
required this.score,
required this.stdLearningId,
required this.topicId,
required this.topicTitle,
});
@override
@ -67,6 +71,8 @@ class DownResultWidget extends StatelessWidget {
MaterialPageRoute(
builder: (context) => FeedbackScreen(
stdLearningId: stdLearningId,
topicId: topicId, // Tambahkan ini
topicTitle: topicTitle, // Tambahkan ini
),
),
);

View File

@ -8,12 +8,17 @@ class JumpResultWidget extends StatelessWidget {
final String? nextLevel;
final int? score;
final String stdLearningId;
final String topicId;
final String topicTitle;
const JumpResultWidget(
{super.key,
required this.nextLevel,
required this.score,
required this.stdLearningId});
const JumpResultWidget({
super.key,
required this.nextLevel,
required this.score,
required this.stdLearningId,
required this.topicId,
required this.topicTitle,
});
@override
Widget build(BuildContext context) {
@ -66,6 +71,8 @@ class JumpResultWidget extends StatelessWidget {
MaterialPageRoute(
builder: (context) => FeedbackScreen(
stdLearningId: stdLearningId,
topicId: topicId, // Tambahkan ini
topicTitle: topicTitle, // Tambahkan ini
),
),
);

View File

@ -137,7 +137,7 @@ class _TopicsListScreenState extends State<TopicsListScreen> {
child: Consumer<TopicProvider>(
builder: (context, topicProvider, _) {
if (topicProvider.topics.isEmpty) {
return Center(child: CircularProgressIndicator());
return const Center(child: CircularProgressIndicator());
} else if (topicProvider.error != null) {
return const Center(child: Text('No topics available'));
} else {
@ -150,7 +150,7 @@ class _TopicsListScreenState extends State<TopicsListScreen> {
description: topic.description,
isCompleted: topic.isCompleted,
onTap: () {
Navigator.push(
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => LevelListScreen(