From 289e16655c54f946fc55c304fb79ef75903bef60 Mon Sep 17 00:00:00 2001 From: Naresh Pratista <2141720057@student.polinema.ac.id> Date: Thu, 28 Nov 2024 14:05:56 +0700 Subject: [PATCH] feat: HistoryCard navigation to direct users to ReviewScreen, and refactor various code to fix the navigation --- lib/core/services/constants.dart | 2 +- .../repositories/history_repository.dart | 6 +- .../history/models/history_model.dart | 11 +- .../history/provider/history_provider.dart | 16 +- .../history/screens/history_screen.dart | 8 +- .../widgets/exercise_history_card.dart | 172 ++++++++++-------- .../models/review_exercise_model.dart | 8 +- .../providers/exercise_provider.dart | 8 + .../exercises/screens/exercise_screen.dart | 61 +++---- .../exercises/screens/review_screen.dart | 122 +++++++++++++ .../exercises/widgets/exercise_navigator.dart | 6 +- .../modules/level/widgets/pretest_card.dart | 9 +- .../material/screens/material_screen.dart | 20 +- 13 files changed, 297 insertions(+), 152 deletions(-) create mode 100644 lib/features/learning/modules/exercises/screens/review_screen.dart diff --git a/lib/core/services/constants.dart b/lib/core/services/constants.dart index d4dd96d..4887ad8 100644 --- a/lib/core/services/constants.dart +++ b/lib/core/services/constants.dart @@ -1 +1 @@ -const String baseUrl = 'http://54.173.167.62/'; +const String baseUrl = 'https://1252-114-6-25-184.ngrok-free.app/'; diff --git a/lib/core/services/repositories/history_repository.dart b/lib/core/services/repositories/history_repository.dart index 775a4fc..b83f4b6 100644 --- a/lib/core/services/repositories/history_repository.dart +++ b/lib/core/services/repositories/history_repository.dart @@ -7,7 +7,7 @@ class HistoryRepository { HistoryRepository(this._dioClient); - Future> getLearningHistory( + Future> getLearningHistory( String sectionId, String token, ) async { @@ -23,9 +23,7 @@ class HistoryRepository { if (historyData.isEmpty) { return []; // Mengembalikan list kosong jika tidak ada data } - return historyData - .map((json) => LearningHistory.fromJson(json)) - .toList(); + return historyData.map((json) => HistoryModel.fromJson(json)).toList(); } else { throw Exception( 'Failed to load learning history: ${response.statusMessage}'); diff --git a/lib/features/history/models/history_model.dart b/lib/features/history/models/history_model.dart index 0d749da..e59090a 100644 --- a/lib/features/history/models/history_model.dart +++ b/lib/features/history/models/history_model.dart @@ -1,6 +1,7 @@ import 'package:intl/intl.dart'; -class LearningHistory { +class HistoryModel { + final String idStudentLearning; final int? score; final String currentLevel; final String? nextLevel; @@ -9,7 +10,8 @@ class LearningHistory { final String sectionName; final bool isPass; - LearningHistory({ + HistoryModel({ + required this.idStudentLearning, required this.score, required this.currentLevel, required this.nextLevel, @@ -19,8 +21,9 @@ class LearningHistory { required this.isPass, }); - factory LearningHistory.fromJson(Map json) { - return LearningHistory( + factory HistoryModel.fromJson(Map json) { + return HistoryModel( + idStudentLearning: json['ID_STUDENT_LEARNING'], score: json['SCORE'] ?? 0, currentLevel: json['CURRENT_LEVEL'] ?? 'Unknown', nextLevel: json['NEXT_LEVEL'], diff --git a/lib/features/history/provider/history_provider.dart b/lib/features/history/provider/history_provider.dart index 4a104ee..4ef3c30 100644 --- a/lib/features/history/provider/history_provider.dart +++ b/lib/features/history/provider/history_provider.dart @@ -7,7 +7,7 @@ import 'package:english_learning/features/history/models/history_model.dart'; class HistoryProvider with ChangeNotifier { final HistoryRepository _repository; final SectionProvider _sectionProvider; - List _learningHistory = []; + List _historyModel = []; int _selectedPageIndex = 0; bool _isLoading = false; String? _error; @@ -18,7 +18,7 @@ class HistoryProvider with ChangeNotifier { this._sectionProvider, ); - List get learningHistory => _learningHistory; + List get historyModel => _historyModel; int get selectedPageIndex => _selectedPageIndex; bool get isLoading => _isLoading; String? get error => _error; @@ -46,13 +46,13 @@ class HistoryProvider with ChangeNotifier { String firstSectionId = _sectionProvider.sections.first.id; // Fetch history for the first section - _learningHistory = + _historyModel = await _repository.getLearningHistory(firstSectionId, token); _error = null; _isInitialized = true; // Mark as initialized } catch (e) { _error = 'Error loading data: ${e.toString()}'; - _learningHistory = []; + _historyModel = []; } finally { _isLoading = false; notifyListeners(); @@ -78,11 +78,11 @@ class HistoryProvider with ChangeNotifier { try { final history = await _repository.getLearningHistory(sectionId, token); - _learningHistory = history; + _historyModel = history; _error = null; } catch (e) { _error = 'Error fetching learning history: ${e.toString()}'; - _learningHistory = []; + _historyModel = []; } finally { _isLoading = false; notifyListeners(); @@ -151,14 +151,14 @@ class HistoryProvider with ChangeNotifier { await _sectionProvider.fetchSections(token); if (_sectionProvider.sections.isNotEmpty) { String firstSectionId = _sectionProvider.sections.first.id; - _learningHistory = + _historyModel = await _repository.getLearningHistory(firstSectionId, token); } else { _error = 'No sections available'; } } catch (e) { _error = 'Error loading data: ${e.toString()}'; - _learningHistory = []; // Clear the list in case of error + _historyModel = []; // Clear the list in case of error } finally { _isLoading = false; notifyListeners(); diff --git a/lib/features/history/screens/history_screen.dart b/lib/features/history/screens/history_screen.dart index c2f229f..230d6d5 100644 --- a/lib/features/history/screens/history_screen.dart +++ b/lib/features/history/screens/history_screen.dart @@ -144,7 +144,7 @@ class _HistoryScreenState extends State { } // Tampilkan empty state jika tidak ada history - if (historyProvider.learningHistory.isEmpty) { + if (historyProvider.historyModel.isEmpty) { return _buildEmptyState(context); } @@ -154,8 +154,8 @@ class _HistoryScreenState extends State { child: AnimatedSwitcher( duration: const Duration(milliseconds: 300), child: ListView.builder( - key: ValueKey(historyProvider.learningHistory.length), - itemCount: historyProvider.learningHistory.length, + key: ValueKey(historyProvider.historyModel.length), + itemCount: historyProvider.historyModel.length, itemBuilder: (context, index) { return AnimatedOpacity( opacity: 1.0, @@ -163,7 +163,7 @@ class _HistoryScreenState extends State { child: Column( children: [ ExerciseHistoryCard( - exercise: historyProvider.learningHistory[index], + exercise: historyProvider.historyModel[index], ), const SizedBox(height: 8.0), ], diff --git a/lib/features/history/widgets/exercise_history_card.dart b/lib/features/history/widgets/exercise_history_card.dart index e704e8c..2c3f966 100644 --- a/lib/features/history/widgets/exercise_history_card.dart +++ b/lib/features/history/widgets/exercise_history_card.dart @@ -1,104 +1,124 @@ import 'package:english_learning/features/history/models/history_model.dart'; import 'package:english_learning/features/history/provider/history_provider.dart'; import 'package:english_learning/core/utils/styles/theme.dart'; +import 'package:english_learning/features/learning/modules/exercises/screens/review_screen.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -class ExerciseHistoryCard extends StatelessWidget { - final LearningHistory exercise; +class ExerciseHistoryCard extends StatefulWidget { + final HistoryModel exercise; const ExerciseHistoryCard({ super.key, required this.exercise, }); + @override + State createState() => _ExerciseHistoryCardState(); +} + +class _ExerciseHistoryCardState extends State { @override Widget build(BuildContext context) { final historyProvider = Provider.of(context, listen: false); final color = historyProvider.getColorForLevels( - exercise.currentLevel, - exercise.nextLevel, - exercise.isPass, + widget.exercise.currentLevel, + widget.exercise.nextLevel, + widget.exercise.isPass, ); - return Card( - color: AppColors.whiteColor, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12.0), - ), - elevation: 0, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 18.0), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - exercise.topicName, - overflow: TextOverflow.ellipsis, - style: AppTextStyles.tetriaryTextStyle.copyWith( - fontWeight: FontWeight.w500, + return GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ReviewScreen( + idStudentLearning: widget.exercise.idStudentLearning, + topicTitle: widget.exercise.topicName, + topicId: null, + ), + ), + ); + }, + child: Card( + color: AppColors.whiteColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12.0), + ), + elevation: 0, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 18.0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + widget.exercise.topicName, + overflow: TextOverflow.ellipsis, + style: AppTextStyles.tetriaryTextStyle.copyWith( + fontWeight: FontWeight.w500, + ), ), - ), - const SizedBox(height: 8), - RichText( - text: TextSpan( - style: AppTextStyles.greyTextStyle.copyWith( - fontSize: 14, + const SizedBox(height: 8), + RichText( + text: TextSpan( + style: AppTextStyles.greyTextStyle.copyWith( + fontSize: 14, + fontWeight: FontWeight.w600, + ), + children: [ + TextSpan( + text: '${widget.exercise.currentLevel} → ', + style: AppTextStyles.blackTextStyle.copyWith(), + ), + TextSpan( + text: widget.exercise.isPass + ? 'Topic Finished' + : '${widget.exercise.nextLevel}', + style: AppTextStyles.blackTextStyle.copyWith( + color: color, + ), + ), + ], + ), + ), + const SizedBox(height: 8), + Text( + 'Submission: ${widget.exercise.formattedDate}', + style: AppTextStyles.disableTextStyle.copyWith( + fontSize: 12, fontWeight: FontWeight.w600, ), - children: [ - TextSpan( - text: '${exercise.currentLevel} → ', - style: AppTextStyles.blackTextStyle.copyWith(), - ), - TextSpan( - text: exercise.isPass - ? 'Topic Finished' - : '${exercise.nextLevel}', - style: AppTextStyles.blackTextStyle.copyWith( - color: color, - ), - ), - ], ), - ), - const SizedBox(height: 8), - Text( - 'Submission: ${exercise.formattedDate}', - style: AppTextStyles.disableTextStyle.copyWith( - fontSize: 12, - fontWeight: FontWeight.w600, - ), - ), - ], - ), - ), - const SizedBox(width: 10), - Container( - padding: const EdgeInsets.symmetric( - horizontal: 16.0, - vertical: 26.0, - ), - decoration: BoxDecoration( - border: Border.all( - color: color, - ), - borderRadius: BorderRadius.circular(8.0), - ), - child: Text( - '${exercise.score}/100', - style: TextStyle( - color: color, - fontWeight: FontWeight.bold, + ], ), ), - ), - ], + const SizedBox(width: 10), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 26.0, + ), + decoration: BoxDecoration( + border: Border.all( + color: color, + ), + borderRadius: BorderRadius.circular(8.0), + ), + child: Text( + '${widget.exercise.score}/100', + style: TextStyle( + color: color, + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ), ), ), ); diff --git a/lib/features/learning/modules/exercises/models/review_exercise_model.dart b/lib/features/learning/modules/exercises/models/review_exercise_model.dart index fd78585..7c0384f 100644 --- a/lib/features/learning/modules/exercises/models/review_exercise_model.dart +++ b/lib/features/learning/modules/exercises/models/review_exercise_model.dart @@ -36,7 +36,7 @@ class ReviewExerciseModel { } class ReviewExerciseDetail { - final String idStudentExercise; + final String? idStudentExercise; final String idAdminExercise; final String title; final String question; @@ -46,14 +46,14 @@ class ReviewExerciseDetail { final String? video; final String? audio; - final String answerStudent; - final int isCorrect; + String answerStudent; + int isCorrect; final double resultScoreStudent; final List? multipleChoices; final List? matchingPairs; ReviewExerciseDetail({ - required this.idStudentExercise, + this.idStudentExercise, required this.idAdminExercise, required this.title, required this.question, diff --git a/lib/features/learning/modules/exercises/providers/exercise_provider.dart b/lib/features/learning/modules/exercises/providers/exercise_provider.dart index 691f2fb..668a29f 100644 --- a/lib/features/learning/modules/exercises/providers/exercise_provider.dart +++ b/lib/features/learning/modules/exercises/providers/exercise_provider.dart @@ -374,6 +374,14 @@ class ExerciseProvider extends ChangeNotifier { // Reset current index _currentExerciseIndex = 0; + + // Ensure all exercises are displayed with empty answers for unanswered questions + for (var exercise in _reviewExercises) { + if (exercise.answerStudent.isEmpty) { + exercise.answerStudent = ''; // Set empty answer + exercise.isCorrect = 0; // Mark as incorrect + } + } } catch (e) { print('Error fetching review exercises: $e'); rethrow; diff --git a/lib/features/learning/modules/exercises/screens/exercise_screen.dart b/lib/features/learning/modules/exercises/screens/exercise_screen.dart index a40916c..5ea1c2b 100644 --- a/lib/features/learning/modules/exercises/screens/exercise_screen.dart +++ b/lib/features/learning/modules/exercises/screens/exercise_screen.dart @@ -12,15 +12,15 @@ class ExerciseScreen extends StatefulWidget { final String? levelId; final String studentLearningId; final bool isReview; - final String topicId; // Tambahkan ini - final String topicTitle; // Tambahkan ini + final String topicId; + final String topicTitle; const ExerciseScreen({ super.key, required this.levelId, required this.studentLearningId, - this.isReview = false, required this.topicId, + required this.isReview, required this.topicTitle, }); @@ -36,12 +36,7 @@ class _ExerciseScreenState extends State { _scrollController = ScrollController(); WidgetsBinding.instance.addPostFrameCallback((_) { final provider = context.read(); - - if (widget.isReview) { - provider.fetchReviewExercises(widget.studentLearningId); - } else { - provider.fetchExercises(widget.levelId!); - } + provider.fetchExercises(widget.levelId!); provider.setStudentLearningId(widget.studentLearningId); }); } @@ -70,9 +65,7 @@ class _ExerciseScreenState extends State { } final currentExercise = provider.currentExercise; - final hasExercises = widget.isReview - ? provider.reviewExercises.isNotEmpty - : provider.exercises.isNotEmpty; + final hasExercises = provider.exercises.isNotEmpty; if (!hasExercises || currentExercise == null) { return const Scaffold( @@ -85,15 +78,6 @@ class _ExerciseScreenState extends State { appBar: AppBar( elevation: 0, automaticallyImplyLeading: false, - leading: widget.isReview - ? IconButton( - icon: const Icon( - BootstrapIcons.arrow_left, - color: AppColors.whiteColor, - ), - onPressed: () => Navigator.of(context).pop(), - ) - : null, // Show back button only in review mode iconTheme: const IconThemeData(color: AppColors.whiteColor), centerTitle: true, title: Text( @@ -108,27 +92,24 @@ class _ExerciseScreenState extends State { gradient: AppColors.gradientTheme, ), ), - actions: !widget.isReview - ? [ - IconButton( - icon: const Icon( - BootstrapIcons.info_circle, - color: AppColors.whiteColor, - size: 22, - ), - onPressed: () => _showInstructions(context), - ), - ] - : null, + actions: [ + IconButton( + icon: const Icon( + BootstrapIcons.info_circle, + color: AppColors.whiteColor, + size: 22, + ), + onPressed: () => _showInstructions(context), + ), + ], ), body: SafeArea( child: Column( children: [ - if (!widget.isReview) - const Padding( - padding: EdgeInsets.all(16.0), - child: ExerciseProgress(), - ), + const Padding( + padding: EdgeInsets.all(16.0), + child: ExerciseProgress(), + ), Expanded( child: SingleChildScrollView( controller: _scrollController, @@ -140,12 +121,12 @@ class _ExerciseScreenState extends State { ExerciseContent( key: ValueKey(provider.currentExerciseIndex), exercise: currentExercise, - isReview: widget.isReview, + isReview: false, ), const SizedBox(height: 24), ExerciseNavigator( onScrollToTop: _scrollToTop, - isReview: widget.isReview, + isReview: false, topicId: widget.topicId, topicTitle: widget.topicTitle, ), diff --git a/lib/features/learning/modules/exercises/screens/review_screen.dart b/lib/features/learning/modules/exercises/screens/review_screen.dart new file mode 100644 index 0000000..c8bbee9 --- /dev/null +++ b/lib/features/learning/modules/exercises/screens/review_screen.dart @@ -0,0 +1,122 @@ +import 'package:bootstrap_icons/bootstrap_icons.dart'; +import 'package:english_learning/features/learning/modules/exercises/providers/exercise_provider.dart'; +import 'package:english_learning/features/learning/modules/exercises/widgets/content/exercise_content.dart'; +import 'package:english_learning/features/learning/modules/exercises/widgets/exercise_navigator.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:english_learning/core/utils/styles/theme.dart'; + +class ReviewScreen extends StatefulWidget { + final String idStudentLearning; + final String? topicId; + final String topicTitle; + + const ReviewScreen({ + super.key, + required this.idStudentLearning, + this.topicId, + required this.topicTitle, + }); + + @override + State createState() => _ReviewScreenState(); +} + +class _ReviewScreenState extends State { + late ScrollController _scrollController; + + @override + void initState() { + super.initState(); + _scrollController = ScrollController(); + WidgetsBinding.instance.addPostFrameCallback((_) { + final provider = context.read(); + provider.fetchReviewExercises(widget.idStudentLearning); + }); + } + + @override + void dispose() { + _scrollController.dispose(); + super.dispose(); + } + + void _scrollToTop() { + _scrollController.animateTo( + 0, + duration: const Duration(milliseconds: 300), + curve: Curves.easeOut, + ); + } + + @override + Widget build(BuildContext context) { + return Consumer(builder: (context, provider, child) { + if (provider.isLoading) { + return const Scaffold( + body: Center(child: CircularProgressIndicator()), + ); + } + + final currentExercise = provider.currentExercise; + final hasExercises = provider.reviewExercises.isNotEmpty; + + if (!hasExercises || currentExercise == null) { + return const Scaffold( + body: Center(child: Text('No review exercises available')), + ); + } + + return Scaffold( + backgroundColor: AppColors.bgSoftColor, + appBar: AppBar( + elevation: 0, + centerTitle: true, + title: Text( + '${provider.nameTopic} - ${provider.nameLevel}', + style: AppTextStyles.whiteTextStyle.copyWith( + fontSize: 14, + fontWeight: FontWeight.w900, + ), + ), + flexibleSpace: Container( + decoration: BoxDecoration( + gradient: AppColors.gradientTheme, + ), + ), + leading: IconButton( + icon: const Icon( + BootstrapIcons.arrow_left, + color: AppColors.whiteColor, + ), + onPressed: () => Navigator.of(context).pop(), + ), + ), + body: SafeArea( + child: SingleChildScrollView( + controller: _scrollController, + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Column( + children: [ + const SizedBox(height: 16), + ExerciseContent( + key: ValueKey(provider.currentExerciseIndex), + exercise: currentExercise, + isReview: true, + ), + const SizedBox(height: 24), + ExerciseNavigator( + onScrollToTop: _scrollToTop, + isReview: true, + topicId: widget.topicId ?? '', + topicTitle: widget.topicTitle, + ), + const SizedBox(height: 32), + ], + ), + ), + ), + ); + }); + } +} diff --git a/lib/features/learning/modules/exercises/widgets/exercise_navigator.dart b/lib/features/learning/modules/exercises/widgets/exercise_navigator.dart index 9e99267..a21f678 100644 --- a/lib/features/learning/modules/exercises/widgets/exercise_navigator.dart +++ b/lib/features/learning/modules/exercises/widgets/exercise_navigator.dart @@ -12,14 +12,14 @@ import 'package:provider/provider.dart'; class ExerciseNavigator extends StatelessWidget { final VoidCallback? onScrollToTop; final bool isReview; - final String topicId; + final String? topicId; final String topicTitle; const ExerciseNavigator({ super.key, required this.onScrollToTop, this.isReview = false, - required this.topicId, + this.topicId, required this.topicTitle, }); @@ -48,7 +48,7 @@ class ExerciseNavigator extends StatelessWidget { score: int.tryParse(result['SCORE'].toString()) ?? 0, isCompleted: result['IS_PASS'] == 1, stdLearningId: result['STUDENT_LEARNING_ID']?.toString(), - topicId: topicId, + topicId: topicId!, topicTitle: topicTitle, ), ), diff --git a/lib/features/learning/modules/level/widgets/pretest_card.dart b/lib/features/learning/modules/level/widgets/pretest_card.dart index 00c7475..a8c6faf 100644 --- a/lib/features/learning/modules/level/widgets/pretest_card.dart +++ b/lib/features/learning/modules/level/widgets/pretest_card.dart @@ -1,6 +1,7 @@ import 'package:bootstrap_icons/bootstrap_icons.dart'; import 'package:english_learning/core/utils/styles/theme.dart'; import 'package:english_learning/core/widgets/custom_button.dart'; +import 'package:english_learning/features/learning/modules/exercises/screens/review_screen.dart'; import 'package:english_learning/features/learning/modules/level/models/level_model.dart'; import 'package:english_learning/features/learning/modules/level/providers/level_provider.dart'; import 'package:english_learning/features/learning/modules/material/screens/material_screen.dart'; @@ -33,11 +34,9 @@ class PretestCard extends StatelessWidget { Navigator.push( context, MaterialPageRoute( - builder: (context) => MaterialScreen( - levelId: pretest.idLevel, - isReview: true, - studentLearningId: pretest.idStudentLearning, - topicId: pretest.idTopic, + builder: (context) => ReviewScreen( + idStudentLearning: pretest.idStudentLearning!, + topicId: null, topicTitle: pretest.nameTopic, ), ), diff --git a/lib/features/learning/modules/material/screens/material_screen.dart b/lib/features/learning/modules/material/screens/material_screen.dart index 882dc67..34504ec 100644 --- a/lib/features/learning/modules/material/screens/material_screen.dart +++ b/lib/features/learning/modules/material/screens/material_screen.dart @@ -4,6 +4,7 @@ import 'package:english_learning/core/widgets/custom_snackbar.dart'; import 'package:english_learning/core/widgets/global_button.dart'; import 'package:english_learning/features/auth/provider/user_provider.dart'; import 'package:english_learning/features/learning/modules/exercises/screens/exercise_screen.dart'; +import 'package:english_learning/features/learning/modules/exercises/screens/review_screen.dart'; import 'package:english_learning/features/learning/modules/level/providers/level_provider.dart'; import 'package:english_learning/features/learning/modules/material/widgets/audio_player_widget.dart'; import 'package:english_learning/features/learning/modules/material/widgets/image_widget.dart'; @@ -67,8 +68,8 @@ class _MaterialScreenState extends State Future _createStudentLearning() async { if (widget.isReview && widget.studentLearningId != null) { - // Jika mode review dan studentLearningId tersedia, langsung navigasi ke ExerciseScreen - _navigateToExercise(widget.studentLearningId!); + // Jika mode review dan studentLearningId tersedia, langsung navigasi ke ReviewScreen + _navigateToReview(widget.studentLearningId!); return; } setState(() { @@ -102,7 +103,20 @@ class _MaterialScreenState extends State builder: (context) => ExerciseScreen( levelId: widget.levelId, studentLearningId: studentLearningId, - isReview: widget.isReview, + isReview: false, + topicId: widget.topicId, + topicTitle: widget.topicTitle, + ), + ), + ); + } + + void _navigateToReview(String studentLearningId) { + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (context) => ReviewScreen( + idStudentLearning: studentLearningId, topicId: widget.topicId, topicTitle: widget.topicTitle, ),