feat: listing quiz done
This commit is contained in:
parent
c026a53d6f
commit
668c7eac27
|
@ -6,6 +6,8 @@ import 'package:quiz_app/feature/home/view/home_page.dart';
|
||||||
import 'package:quiz_app/feature/detail_quiz/binding/detail_quiz_binding.dart';
|
import 'package:quiz_app/feature/detail_quiz/binding/detail_quiz_binding.dart';
|
||||||
import 'package:quiz_app/feature/library/binding/library_binding.dart';
|
import 'package:quiz_app/feature/library/binding/library_binding.dart';
|
||||||
import 'package:quiz_app/feature/detail_quiz/view/detail_quix_view.dart';
|
import 'package:quiz_app/feature/detail_quiz/view/detail_quix_view.dart';
|
||||||
|
import 'package:quiz_app/feature/listing_quiz/binding/listing_quiz_binding.dart';
|
||||||
|
import 'package:quiz_app/feature/listing_quiz/view/listing_quiz_view.dart';
|
||||||
import 'package:quiz_app/feature/login/bindings/login_binding.dart';
|
import 'package:quiz_app/feature/login/bindings/login_binding.dart';
|
||||||
import 'package:quiz_app/feature/login/view/login_page.dart';
|
import 'package:quiz_app/feature/login/view/login_page.dart';
|
||||||
import 'package:quiz_app/feature/navigation/bindings/navigation_binding.dart';
|
import 'package:quiz_app/feature/navigation/bindings/navigation_binding.dart';
|
||||||
|
@ -85,6 +87,11 @@ class AppPages {
|
||||||
name: AppRoutes.resultQuizPage,
|
name: AppRoutes.resultQuizPage,
|
||||||
page: () => QuizResultView(),
|
page: () => QuizResultView(),
|
||||||
binding: QuizResultBinding(),
|
binding: QuizResultBinding(),
|
||||||
|
),
|
||||||
|
GetPage(
|
||||||
|
name: AppRoutes.listingQuizPage,
|
||||||
|
page: () => ListingsQuizView(),
|
||||||
|
binding: ListingQuizBinding(),
|
||||||
)
|
)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ abstract class AppRoutes {
|
||||||
|
|
||||||
static const quizCreatePage = "/quiz/creation";
|
static const quizCreatePage = "/quiz/creation";
|
||||||
static const quizPreviewPage = "/quiz/preview";
|
static const quizPreviewPage = "/quiz/preview";
|
||||||
|
static const listingQuizPage = "/quiz/listing";
|
||||||
static const detailQuizPage = "/quiz/detail";
|
static const detailQuizPage = "/quiz/detail";
|
||||||
|
|
||||||
static const playQuizPage = "/quiz/play";
|
static const playQuizPage = "/quiz/play";
|
||||||
|
|
|
@ -6,10 +6,13 @@ class RecomendationComponent extends StatelessWidget {
|
||||||
final String title;
|
final String title;
|
||||||
final List<QuizListingModel> datas;
|
final List<QuizListingModel> datas;
|
||||||
final Function(String) itemOnTap;
|
final Function(String) itemOnTap;
|
||||||
|
final Function() allOnTap;
|
||||||
|
|
||||||
const RecomendationComponent({
|
const RecomendationComponent({
|
||||||
required this.title,
|
required this.title,
|
||||||
required this.datas,
|
required this.datas,
|
||||||
required this.itemOnTap,
|
required this.itemOnTap,
|
||||||
|
required this.allOnTap,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -60,9 +63,12 @@ class RecomendationComponent extends StatelessWidget {
|
||||||
title,
|
title,
|
||||||
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
Text(
|
GestureDetector(
|
||||||
"Lihat semua",
|
onTap: allOnTap,
|
||||||
style: TextStyle(fontSize: 14, color: Colors.blue.shade700),
|
child: Text(
|
||||||
|
"Lihat semua",
|
||||||
|
style: TextStyle(fontSize: 14, color: Colors.blue.shade700),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -51,9 +51,9 @@ class QuizService extends GetxService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<BaseResponseModel<List<QuizListingModel>>?> recomendationQuiz() async {
|
Future<BaseResponseModel<List<QuizListingModel>>?> recomendationQuiz({int page = 1, int amount = 3}) async {
|
||||||
try {
|
try {
|
||||||
final response = await _dio.get(APIEndpoint.quizRecomendation);
|
final response = await _dio.get("${APIEndpoint.quizRecomendation}?page=$page&limit=$amount");
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final parsedResponse = BaseResponseModel<List<QuizListingModel>>.fromJson(
|
final parsedResponse = BaseResponseModel<List<QuizListingModel>>.fromJson(
|
||||||
|
|
|
@ -7,7 +7,7 @@ import 'package:quiz_app/data/services/quiz_service.dart';
|
||||||
|
|
||||||
class HomeController extends GetxController {
|
class HomeController extends GetxController {
|
||||||
final UserController _userController = Get.find<UserController>();
|
final UserController _userController = Get.find<UserController>();
|
||||||
QuizService _quizService;
|
final QuizService _quizService;
|
||||||
HomeController(this._quizService);
|
HomeController(this._quizService);
|
||||||
|
|
||||||
Rx<String> get userName => _userController.userName;
|
Rx<String> get userName => _userController.userName;
|
||||||
|
@ -31,4 +31,6 @@ class HomeController extends GetxController {
|
||||||
}
|
}
|
||||||
|
|
||||||
void onRecommendationTap(String quizId) => Get.toNamed(AppRoutes.detailQuizPage, arguments: quizId);
|
void onRecommendationTap(String quizId) => Get.toNamed(AppRoutes.detailQuizPage, arguments: quizId);
|
||||||
|
|
||||||
|
void goToListingsQuizPage() => Get.toNamed(AppRoutes.listingQuizPage);
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,7 @@ class HomeView extends GetView<HomeController> {
|
||||||
title: "Quiz Rekomendasi",
|
title: "Quiz Rekomendasi",
|
||||||
datas: controller.data.toList(),
|
datas: controller.data.toList(),
|
||||||
itemOnTap: controller.onRecommendationTap,
|
itemOnTap: controller.onRecommendationTap,
|
||||||
|
allOnTap: controller.goToListingsQuizPage,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:quiz_app/data/services/quiz_service.dart';
|
||||||
|
import 'package:quiz_app/feature/listing_quiz/controller/listing_quiz_controller.dart';
|
||||||
|
|
||||||
|
class ListingQuizBinding extends Bindings {
|
||||||
|
@override
|
||||||
|
void dependencies() {
|
||||||
|
if (!Get.isRegistered<QuizService>()) {
|
||||||
|
Get.lazyPut<QuizService>(() => QuizService());
|
||||||
|
}
|
||||||
|
Get.lazyPut<ListingQuizController>(() => ListingQuizController(Get.find<QuizService>()));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:quiz_app/app/routes/app_pages.dart';
|
||||||
|
import 'package:quiz_app/data/models/base/base_model.dart';
|
||||||
|
import 'package:quiz_app/data/models/quiz/quiz_listing_model.dart';
|
||||||
|
import 'package:quiz_app/data/services/quiz_service.dart';
|
||||||
|
|
||||||
|
class ListingQuizController extends GetxController {
|
||||||
|
final QuizService _quizService;
|
||||||
|
|
||||||
|
ListingQuizController(this._quizService);
|
||||||
|
|
||||||
|
RxBool isLoading = false.obs;
|
||||||
|
RxBool isLoadingMore = false.obs;
|
||||||
|
RxList<QuizListingModel> quizzes = <QuizListingModel>[].obs;
|
||||||
|
|
||||||
|
final ScrollController scrollController = ScrollController();
|
||||||
|
|
||||||
|
final int amountQuiz = 8;
|
||||||
|
int currentPage = 1;
|
||||||
|
bool hasMore = true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
super.onInit();
|
||||||
|
_getRecomendationQuiz();
|
||||||
|
scrollController.addListener(_onScroll);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onScroll() {
|
||||||
|
if (scrollController.position.pixels >= scrollController.position.maxScrollExtent - 200) {
|
||||||
|
if (!isLoadingMore.value && hasMore) {
|
||||||
|
loadMoreQuiz();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _getRecomendationQuiz() async {
|
||||||
|
isLoading.value = true;
|
||||||
|
currentPage = 1;
|
||||||
|
BaseResponseModel? response = await _quizService.recomendationQuiz(amount: amountQuiz);
|
||||||
|
if (response != null && response.data != null) {
|
||||||
|
final data = response.data as List<QuizListingModel>;
|
||||||
|
quizzes.assignAll(data);
|
||||||
|
hasMore = data.length == amountQuiz;
|
||||||
|
}
|
||||||
|
isLoading.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> loadMoreQuiz() async {
|
||||||
|
isLoadingMore.value = true;
|
||||||
|
currentPage++;
|
||||||
|
BaseResponseModel? response = await _quizService.recomendationQuiz(page: currentPage, amount: amountQuiz);
|
||||||
|
if (response != null && response.data != null) {
|
||||||
|
final data = response.data as List<QuizListingModel>;
|
||||||
|
quizzes.addAll(data);
|
||||||
|
hasMore = data.length == amountQuiz;
|
||||||
|
}
|
||||||
|
isLoadingMore.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void goToDetailQuiz(String quizId) => Get.toNamed(AppRoutes.detailQuizPage, arguments: quizId);
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:quiz_app/app/const/colors/app_colors.dart';
|
||||||
|
import 'package:quiz_app/component/quiz_container_component.dart';
|
||||||
|
import 'package:quiz_app/feature/listing_quiz/controller/listing_quiz_controller.dart';
|
||||||
|
|
||||||
|
class ListingsQuizView extends GetView<ListingQuizController> {
|
||||||
|
const ListingsQuizView({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: AppColors.background,
|
||||||
|
appBar: AppBar(
|
||||||
|
centerTitle: true,
|
||||||
|
title: const Text(
|
||||||
|
'Daftar Kuis',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
body: Padding(
|
||||||
|
padding: const EdgeInsets.all(20.0),
|
||||||
|
child: Obx(() {
|
||||||
|
if (controller.isLoading.value) {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (controller.quizzes.isEmpty) {
|
||||||
|
return const Center(child: Text('Tidak ada kuis tersedia.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ListView.builder(
|
||||||
|
controller: controller.scrollController,
|
||||||
|
itemCount: controller.quizzes.length + 1, // +1 untuk indikator loading
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
if (index == controller.quizzes.length) {
|
||||||
|
return Obx(() => controller.isLoadingMore.value
|
||||||
|
? const Padding(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
child: Center(child: CircularProgressIndicator()),
|
||||||
|
)
|
||||||
|
: const SizedBox());
|
||||||
|
}
|
||||||
|
|
||||||
|
final quiz = controller.quizzes[index];
|
||||||
|
return QuizContainerComponent(
|
||||||
|
data: quiz,
|
||||||
|
onTap: controller.goToDetailQuiz,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,6 +47,8 @@ class SearchQuizController extends GetxController {
|
||||||
|
|
||||||
void goToDetailPage(String quizId) => Get.toNamed(AppRoutes.detailQuizPage, arguments: quizId);
|
void goToDetailPage(String quizId) => Get.toNamed(AppRoutes.detailQuizPage, arguments: quizId);
|
||||||
|
|
||||||
|
void goToListingsQuizPage() => Get.toNamed(AppRoutes.listingQuizPage);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onClose() {
|
void onClose() {
|
||||||
searchController.dispose();
|
searchController.dispose();
|
||||||
|
|
|
@ -32,6 +32,7 @@ class SearchView extends GetView<SearchQuizController> {
|
||||||
title: "Quiz Rekomendasi",
|
title: "Quiz Rekomendasi",
|
||||||
datas: controller.recommendationQData.toList(),
|
datas: controller.recommendationQData.toList(),
|
||||||
itemOnTap: controller.goToDetailPage,
|
itemOnTap: controller.goToDetailPage,
|
||||||
|
allOnTap: controller.goToListingsQuizPage,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 30),
|
const SizedBox(height: 30),
|
||||||
|
@ -40,6 +41,7 @@ class SearchView extends GetView<SearchQuizController> {
|
||||||
title: "Quiz Populer",
|
title: "Quiz Populer",
|
||||||
datas: controller.recommendationQData.toList(),
|
datas: controller.recommendationQData.toList(),
|
||||||
itemOnTap: controller.goToDetailPage,
|
itemOnTap: controller.goToDetailPage,
|
||||||
|
allOnTap: controller.goToListingsQuizPage,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
Loading…
Reference in New Issue