feat: done implement admin result page

This commit is contained in:
akhdanre 2025-05-19 00:44:42 +07:00
parent b8c7d62c8c
commit 871ec13c31
10 changed files with 468 additions and 281 deletions

View File

@ -1,5 +1,7 @@
import 'package:get/get_navigation/src/routes/get_route.dart'; import 'package:get/get_navigation/src/routes/get_route.dart';
import 'package:quiz_app/app/middleware/auth_middleware.dart'; import 'package:quiz_app/app/middleware/auth_middleware.dart';
import 'package:quiz_app/feature/admin_result_page/bindings/admin_result_binding.dart';
import 'package:quiz_app/feature/admin_result_page/view/admin_result_page.dart';
import 'package:quiz_app/feature/history/binding/detail_history_binding.dart'; import 'package:quiz_app/feature/history/binding/detail_history_binding.dart';
import 'package:quiz_app/feature/history/binding/history_binding.dart'; import 'package:quiz_app/feature/history/binding/history_binding.dart';
import 'package:quiz_app/feature/history/view/detail_history_view.dart'; import 'package:quiz_app/feature/history/view/detail_history_view.dart';
@ -142,9 +144,10 @@ class AppPages {
page: () => UpdateProfilePage(), page: () => UpdateProfilePage(),
binding: UpdateProfileBinding(), binding: UpdateProfileBinding(),
), ),
// GetPage( GetPage(
// name: AppRoutes.monitorResultMPLPage, name: AppRoutes.monitorResultMPLPage,
// page: () => AdminResultPage(), page: () => AdminResultPage(),
// ) binding: AdminResultBinding(),
)
]; ];
} }

View File

@ -23,6 +23,8 @@ class APIEndpoint {
static const String session = "/session"; static const String session = "/session";
static const String sessionHistory = "/history/session";
static const String userData = "/user"; static const String userData = "/user";
static const String userUpdate = "/user/update"; static const String userUpdate = "/user/update";
} }

View File

@ -0,0 +1,87 @@
class Participant {
final String id;
final String name;
final int score;
Participant({
required this.id,
required this.name,
required this.score,
});
factory Participant.fromJson(Map<String, dynamic> json) {
return Participant(
id: json['id'],
name: json['name'],
score: json['score'],
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'score': score,
};
}
}
class SessionHistory {
final String id;
final String sessionCode;
final String quizId;
final String hostId;
final DateTime createdAt;
final DateTime? startedAt;
final DateTime? endedAt;
final bool isActive;
final int participantLimit;
final List<Participant> participants;
final int currentQuestionIndex;
SessionHistory({
required this.id,
required this.sessionCode,
required this.quizId,
required this.hostId,
required this.createdAt,
this.startedAt,
this.endedAt,
required this.isActive,
required this.participantLimit,
required this.participants,
required this.currentQuestionIndex,
});
factory SessionHistory.fromJson(Map<String, dynamic> json) {
return SessionHistory(
id: json['id'],
sessionCode: json['session_code'],
quizId: json['quiz_id'],
hostId: json['host_id'],
createdAt: DateTime.parse(json['created_at']),
startedAt: json['started_at'] != null ? DateTime.parse(json['started_at']) : null,
endedAt: json['ended_at'] != null ? DateTime.parse(json['ended_at']) : null,
isActive: json['is_active'],
participantLimit: json['participan_limit'], // Typo di JSON, harusnya "participant_limit"
participants: (json['participants'] as List).map((p) => Participant.fromJson(p)).toList(),
currentQuestionIndex: json['current_question_index'],
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'session_code': sessionCode,
'quiz_id': quizId,
'host_id': hostId,
'created_at': createdAt.toIso8601String(),
'started_at': startedAt?.toIso8601String(),
'ended_at': endedAt?.toIso8601String(),
'is_active': isActive,
'participan_limit': participantLimit, // Tetap gunakan sesuai field JSON yang ada
'participants': participants.map((p) => p.toJson()).toList(),
'current_question_index': currentQuestionIndex,
};
}
}

View File

@ -5,6 +5,7 @@ import 'package:quiz_app/core/utils/logger.dart';
import 'package:quiz_app/data/models/base/base_model.dart'; import 'package:quiz_app/data/models/base/base_model.dart';
import 'package:quiz_app/data/models/history/detail_quiz_history.dart'; import 'package:quiz_app/data/models/history/detail_quiz_history.dart';
import 'package:quiz_app/data/models/history/quiz_history.dart'; import 'package:quiz_app/data/models/history/quiz_history.dart';
import 'package:quiz_app/data/models/history/session_history.dart';
import 'package:quiz_app/data/providers/dio_client.dart'; import 'package:quiz_app/data/providers/dio_client.dart';
class HistoryService extends GetxService { class HistoryService extends GetxService {
@ -45,4 +46,20 @@ class HistoryService extends GetxService {
return null; return null;
} }
} }
Future<BaseResponseModel<SessionHistory>?> getSessionHistory(String sessionId) async {
try {
final result = await _dio.get("${APIEndpoint.sessionHistory}/$sessionId");
final parsedResponse = BaseResponseModel<SessionHistory>.fromJson(
result.data,
(data) => SessionHistory.fromJson(data),
);
return parsedResponse;
} catch (e, stacktrace) {
logC.e(e, stackTrace: stacktrace);
return null;
}
}
} }

View File

@ -0,0 +1,11 @@
import 'package:get/get.dart';
import 'package:quiz_app/data/services/history_service.dart';
import 'package:quiz_app/feature/admin_result_page/controller/admin_result_controller.dart';
class AdminResultBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => HistoryService());
Get.lazyPut(() => AdminResultController(Get.find<HistoryService>()));
}
}

View File

@ -0,0 +1,32 @@
import 'package:get/get.dart';
import 'package:quiz_app/data/models/history/session_history.dart';
import 'package:quiz_app/data/services/history_service.dart';
class AdminResultController extends GetxController {
final HistoryService _historyService;
AdminResultController(this._historyService);
SessionHistory? sessionHistory;
RxBool isLoading = false.obs;
@override
void onInit() {
loadData();
super.onInit();
}
void loadData() async {
isLoading.value = true;
final sessionId = Get.arguments as String;
final result = await _historyService.getSessionHistory(sessionId);
if (result != null) {
sessionHistory = result.data!;
print(sessionHistory!.toJson());
}
isLoading.value = false;
}
}

View File

@ -1,263 +1,246 @@
// import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
// import 'package:lucide_icons/lucide_icons.dart'; import 'package:get/get_state_manager/src/rx_flutter/rx_obx_widget.dart';
// import 'dart:math' as math; import 'package:get/get_state_manager/src/simple/get_view.dart';
import 'package:quiz_app/app/const/colors/app_colors.dart';
import 'package:quiz_app/app/const/text/text_style.dart';
import 'package:quiz_app/data/models/history/session_history.dart';
import 'package:quiz_app/feature/admin_result_page/controller/admin_result_controller.dart';
// import 'package:quiz_app/app/const/colors/app_colors.dart'; class AdminResultPage extends GetView<AdminResultController> {
// import 'package:quiz_app/app/const/text/text_style.dart'; const AdminResultPage({Key? key}) : super(key: key);
// import 'package:quiz_app/feature/admin_result_page/view/detail_participant_result_page.dart';
// class AdminResultPage extends StatelessWidget { @override
// const AdminResultPage({Key? key}) : super(key: key); Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.background,
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Obx(() {
if (controller.isLoading.value) {
return const Center(child: CircularProgressIndicator());
}
// @override if (controller.sessionHistory == null) {
// Widget build(BuildContext context) { return const Center(child: Text("Data tidak ditemukan."));
// return Scaffold( }
// backgroundColor: AppColors.background,
// body: SafeArea(
// child: Padding(
// padding: const EdgeInsets.all(16.0),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// _buildSectionHeader("Hasil Akhir Kuis"),
// _buildSummaryCard(),
// const SizedBox(height: 20),
// _buildSectionHeader('Peringkat Peserta'),
// const SizedBox(height: 14),
// Expanded(
// child: ListView.separated(
// itemCount: dummyParticipants.length,
// separatorBuilder: (context, index) => const SizedBox(height: 10),
// itemBuilder: (context, index) {
// final participant = dummyParticipants[index];
// return _buildParticipantResultCard(
// context,
// participant,
// position: index + 1,
// );
// },
// ),
// ),
// ],
// ),
// ),
// ),
// );
// }
// Widget _buildSectionHeader(String title) { final participants = controller.sessionHistory!.participants;
// return Container(
// padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(8),
// ),
// child: Text(title, style: AppTextStyles.title),
// );
// }
// Widget _buildSummaryCard() { return Column(
// // Hitung nilai rata-rata crossAxisAlignment: CrossAxisAlignment.start,
// final avgScore = dummyParticipants.map((p) => p.scorePercent).reduce((a, b) => a + b) / dummyParticipants.length; children: [
_buildSectionHeader("Hasil Akhir Kuis"),
const SizedBox(height: 20),
_buildSummaryCard(participants),
const SizedBox(height: 20),
_buildSectionHeader('Peringkat Peserta'),
const SizedBox(height: 14),
Expanded(
child: ListView.separated(
itemCount: participants.length,
separatorBuilder: (context, index) => const SizedBox(height: 10),
itemBuilder: (context, index) {
final participant = participants[index];
return _buildParticipantResultCard(
participant,
position: index + 1,
);
},
),
),
],
);
}),
),
),
);
}
// // Hitung jumlah peserta yang lulus (>= 60%) Widget _buildSectionHeader(String title) {
// final passCount = dummyParticipants.where((p) => p.scorePercent >= 60).length; return Container(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
),
child: Text(title, style: AppTextStyles.title),
);
}
// return Card( Widget _buildSummaryCard(List<Participant> participants) {
// elevation: 2, final avgScore = participants.isNotEmpty ? participants.map((p) => p.score).reduce((a, b) => a + b) / participants.length : 0.0;
// color: Colors.white,
// shadowColor: AppColors.shadowPrimary.withValues(alpha:0.2),
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(16),
// side: BorderSide(color: AppColors.accentBlue.withValues(alpha:0.2)),
// ),
// child: Padding(
// padding: const EdgeInsets.all(20),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Row(
// children: [
// Icon(
// LucideIcons.clipboardCheck,
// color: AppColors.primaryBlue,
// size: 20,
// ),
// const SizedBox(width: 8),
// Text(
// "RINGKASAN KUIS",
// style: TextStyle(
// fontSize: 13,
// fontWeight: FontWeight.bold,
// color: AppColors.primaryBlue,
// letterSpacing: 0.8,
// ),
// ),
// ],
// ),
// Divider(color: AppColors.borderLight, height: 20),
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceAround,
// children: [
// _buildSummaryItem(
// icon: LucideIcons.users,
// value: "${dummyParticipants.length}",
// label: "Total Peserta",
// ),
// _buildSummaryItem(
// icon: LucideIcons.percent,
// value: "${avgScore.toStringAsFixed(1)}%",
// label: "Rata-Rata Nilai",
// valueColor: _getScoreColor(avgScore),
// ),
// _buildSummaryItem(
// icon: LucideIcons.award,
// value: "$passCount/${dummyParticipants.length}",
// label: "Peserta Lulus",
// valueColor: AppColors.scoreGood,
// ),
// ],
// ),
// ],
// ),
// ),
// );
// }
// Widget _buildSummaryItem({ final passCount = participants.where((p) => p.score >= 60).length;
// required IconData icon,
// required String value,
// required String label,
// Color? valueColor,
// }) {
// return Column(
// children: [
// Icon(icon, color: AppColors.softGrayText, size: 22),
// const SizedBox(height: 8),
// Text(
// value,
// style: TextStyle(
// fontSize: 18,
// fontWeight: FontWeight.bold,
// color: valueColor ?? AppColors.darkText,
// ),
// ),
// const SizedBox(height: 4),
// Text(
// label,
// style: AppTextStyles.caption,
// ),
// ],
// );
// }
// Widget _buildParticipantResultCard(BuildContext context, ParticipantResult participant, {required int position}) { return Card(
// return InkWell( elevation: 2,
// onTap: () { color: Colors.white,
// // Navigasi ke halaman detail saat kartu ditekan shadowColor: AppColors.shadowPrimary.withOpacity(0.2),
// Navigator.push( shape: RoundedRectangleBorder(
// context, borderRadius: BorderRadius.circular(16),
// MaterialPageRoute( side: BorderSide(color: AppColors.accentBlue.withOpacity(0.2)),
// builder: (context) => ParticipantDetailPage(participant: participant), ),
// ), child: Padding(
// ); padding: const EdgeInsets.all(20),
// }, child: Column(
// child: Card( crossAxisAlignment: CrossAxisAlignment.start,
// elevation: 2, children: [
// color: Colors.white, Row(
// shadowColor: AppColors.shadowPrimary.withValues(alpha:0.2), children: [
// shape: RoundedRectangleBorder( Icon(Icons.assignment_turned_in, color: AppColors.primaryBlue, size: 20),
// borderRadius: BorderRadius.circular(16), const SizedBox(width: 8),
// side: BorderSide(color: AppColors.accentBlue.withValues(alpha:0.2)), Text(
// ), "RINGKASAN KUIS",
// child: Padding( style: TextStyle(
// padding: const EdgeInsets.all(20), fontSize: 13,
// child: Row( fontWeight: FontWeight.bold,
// children: [ color: AppColors.primaryBlue,
// // Position indicator letterSpacing: 0.8,
// Container( ),
// width: 36, ),
// height: 36, ],
// decoration: BoxDecoration( ),
// shape: BoxShape.circle, const Divider(height: 20),
// color: _getPositionColor(position), Row(
// ), mainAxisAlignment: MainAxisAlignment.spaceAround,
// child: Center( children: [
// child: Text( _buildSummaryItem(
// position.toString(), icon: Icons.group,
// style: const TextStyle( value: "${participants.length}",
// fontSize: 16, label: "Total Peserta",
// fontWeight: FontWeight.bold, ),
// color: Colors.white, _buildSummaryItem(
// ), icon: Icons.percent,
// ), value: "${avgScore.toStringAsFixed(1)}%",
// ), label: "Rata-Rata Nilai",
// ), valueColor: _getScoreColor(avgScore),
// const SizedBox(width: 16), ),
// // Participant info _buildSummaryItem(
// Expanded( icon: Icons.emoji_events,
// child: Column( value: "$passCount/${participants.length}",
// crossAxisAlignment: CrossAxisAlignment.start, label: "Peserta Lulus",
// children: [ valueColor: AppColors.scoreGood,
// Text( ),
// participant.name, ],
// style: AppTextStyles.subtitle, ),
// ), ],
// const SizedBox(height: 4), ),
// Text( ),
// "Benar: ${participant.correctAnswers}/${participant.totalQuestions}", );
// style: AppTextStyles.caption, }
// ),
// ],
// ),
// ),
// // Score
// Container(
// width: 60,
// height: 60,
// decoration: BoxDecoration(
// shape: BoxShape.circle,
// color: _getScoreColor(participant.scorePercent).withValues(alpha:0.1),
// border: Border.all(
// color: _getScoreColor(participant.scorePercent),
// width: 2,
// ),
// ),
// child: Center(
// child: Text(
// "${participant.scorePercent.toInt()}%",
// style: TextStyle(
// fontSize: 16,
// fontWeight: FontWeight.bold,
// color: _getScoreColor(participant.scorePercent),
// ),
// ),
// ),
// ),
// const SizedBox(width: 12),
// // Arrow indicator
// Icon(
// LucideIcons.chevronRight,
// color: AppColors.softGrayText,
// size: 20,
// ),
// ],
// ),
// ),
// ),
// );
// }
// Color _getScoreColor(double score) { Widget _buildSummaryItem({
// if (score >= 80) return AppColors.scoreExcellent; required IconData icon,
// if (score >= 70) return AppColors.scoreGood; required String value,
// if (score >= 60) return AppColors.scoreAverage; required String label,
// return AppColors.scorePoor; Color? valueColor,
// } }) {
return Column(
children: [
Icon(icon, color: AppColors.softGrayText, size: 22),
const SizedBox(height: 8),
Text(
value,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: valueColor ?? AppColors.darkText,
),
),
const SizedBox(height: 4),
Text(
label,
style: AppTextStyles.caption,
),
],
);
}
// Color _getPositionColor(int position) { Widget _buildParticipantResultCard(Participant participant, {required int position}) {
// if (position == 1) return const Color(0xFFFFD700); // Gold final scorePercent = participant.score.toDouble();
// if (position == 2) return const Color(0xFFC0C0C0); // Silver
// if (position == 3) return const Color(0xFFCD7F32); // Bronze return Card(
// return AppColors.softGrayText; elevation: 2,
// } color: Colors.white,
// } shadowColor: AppColors.shadowPrimary.withOpacity(0.2),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
side: BorderSide(color: AppColors.accentBlue.withOpacity(0.2)),
),
child: Padding(
padding: const EdgeInsets.all(20),
child: Row(
children: [
Container(
width: 36,
height: 36,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: _getPositionColor(position),
),
child: Center(
child: Text(
position.toString(),
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(participant.name, style: AppTextStyles.subtitle),
const SizedBox(height: 4),
Text("Skor: ${participant.score}", style: AppTextStyles.caption),
],
),
),
Container(
width: 60,
height: 60,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: _getScoreColor(scorePercent).withOpacity(0.1),
border: Border.all(
color: _getScoreColor(scorePercent),
width: 2,
),
),
child: Center(
child: Text(
"${participant.score}%",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: _getScoreColor(scorePercent),
),
),
),
),
const SizedBox(width: 12),
const Icon(Icons.chevron_right, color: AppColors.softGrayText, size: 20),
],
),
),
);
}
Color _getScoreColor(double score) {
if (score >= 80) return AppColors.scoreExcellent;
if (score >= 70) return AppColors.scoreGood;
if (score >= 60) return AppColors.scoreAverage;
return AppColors.scorePoor;
}
Color _getPositionColor(int position) {
if (position == 1) return const Color(0xFFFFD700); // Gold
if (position == 2) return const Color(0xFFC0C0C0); // Silver
if (position == 3) return const Color(0xFFCD7F32); // Bronze
return AppColors.softGrayText;
}
}

View File

@ -1,4 +1,5 @@
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:quiz_app/app/routes/app_pages.dart';
import 'package:quiz_app/core/utils/logger.dart'; import 'package:quiz_app/core/utils/logger.dart';
import 'package:quiz_app/data/models/user/user_model.dart'; import 'package:quiz_app/data/models/user/user_model.dart';
import 'package:quiz_app/data/services/socket_service.dart'; import 'package:quiz_app/data/services/socket_service.dart';
@ -100,6 +101,10 @@ class MonitorQuizController extends GetxController {
// Notify observers // Notify observers
participan.refresh(); participan.refresh();
}); });
_socketService.quizDone.listen((_) {
Get.offAllNamed(AppRoutes.monitorResultMPLPage, arguments: sessionId);
});
} }
} }

View File

@ -26,26 +26,68 @@ class RoomMakerController extends GetxController {
this._quizService, this._quizService,
); );
// final roomName = ''.obs;
final selectedQuiz = Rxn<QuizListingModel>(); final selectedQuiz = Rxn<QuizListingModel>();
RxBool isOnwQuiz = true.obs; RxBool isOnwQuiz = true.obs;
final TextEditingController nameTC = TextEditingController(); final TextEditingController nameTC = TextEditingController();
final TextEditingController maxPlayerTC = TextEditingController(); final TextEditingController maxPlayerTC = TextEditingController();
final ScrollController scrollController = ScrollController();
final availableQuizzes = <QuizListingModel>[].obs; final availableQuizzes = <QuizListingModel>[].obs;
int currentPage = 1;
bool isLoading = false;
bool hasMoreData = true;
@override @override
void onInit() { void onInit() {
loadQuiz(); loadQuiz(reset: true);
scrollController.addListener(_scrollListener);
super.onInit(); super.onInit();
} }
loadQuiz() async { Future<void> loadQuiz({bool reset = false}) async {
BaseResponseModel<List<QuizListingModel>>? response = await _quizService.userQuiz(_userController.userData!.id, 1); if (isLoading) return;
isLoading = true;
if (reset) {
currentPage = 1;
hasMoreData = true;
}
BaseResponseModel<List<QuizListingModel>>? response;
if (isOnwQuiz.value) {
response = await _quizService.userQuiz(_userController.userData!.id, currentPage);
} else {
response = await _quizService.recomendationQuiz(page: currentPage, amount: 5);
}
if (response != null) { if (response != null) {
availableQuizzes.assignAll(response.data!); if (reset) {
availableQuizzes.assignAll(response.data!);
} else {
availableQuizzes.addAll(response.data!);
}
if (response.data == null || response.data!.isEmpty) {
hasMoreData = false;
} else {
currentPage++;
}
} else {
hasMoreData = false;
}
isLoading = false;
}
void _scrollListener() {
if (scrollController.position.pixels >= scrollController.position.maxScrollExtent - 100) {
if (hasMoreData && !isLoading) {
loadQuiz();
}
} }
} }
@ -67,7 +109,10 @@ class RoomMakerController extends GetxController {
if (response != null) { if (response != null) {
_socketService.initSocketConnection(); _socketService.initSocketConnection();
_socketService.joinRoom(sessionCode: response.data!.sessionCode, userId: _userController.userData!.id); _socketService.joinRoom(
sessionCode: response.data!.sessionCode,
userId: _userController.userData!.id,
);
_socketService.roomMessages.listen((data) { _socketService.roomMessages.listen((data) {
if (data["type"] == "join") { if (data["type"] == "join") {
@ -94,17 +139,7 @@ class RoomMakerController extends GetxController {
void onQuizSourceChange(bool base) async { void onQuizSourceChange(bool base) async {
isOnwQuiz.value = base; isOnwQuiz.value = base;
if (base) { await loadQuiz(reset: true);
BaseResponseModel<List<QuizListingModel>>? response = await _quizService.userQuiz(_userController.userData!.id, 1);
if (response != null) {
availableQuizzes.assignAll(response.data!);
}
return;
}
BaseResponseModel<List<QuizListingModel>>? response = await _quizService.recomendationQuiz(page: 1, amount: 4);
if (response != null) {
availableQuizzes.assignAll(response.data!);
}
} }
void onQuizChoosen(String quizId) { void onQuizChoosen(String quizId) {

View File

@ -44,13 +44,25 @@ class RoomMakerView extends GetView<RoomMakerController> {
Expanded( Expanded(
child: Container( child: Container(
child: Obx(() => ListView.builder( child: Obx(() => ListView.builder(
itemCount: controller.availableQuizzes.length, controller: controller.scrollController,
itemBuilder: (context, index) { itemCount: controller.availableQuizzes.length + (controller.isLoading ? 1 : 0),
return QuizContainerComponent( itemBuilder: (context, index) {
data: controller.availableQuizzes[index], if (index < controller.availableQuizzes.length) {
onTap: controller.onQuizChoosen, return QuizContainerComponent(
); data: controller.availableQuizzes[index],
})), onTap: controller.onQuizChoosen,
);
} else {
// Loading Indicator di Bawah
return Padding(
padding: const EdgeInsets.all(16.0),
child: Center(
child: CircularProgressIndicator(),
),
);
}
},
)),
), ),
), ),
SizedBox( SizedBox(