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/library/binding/library_binding.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/view/login_page.dart';
|
||||
import 'package:quiz_app/feature/navigation/bindings/navigation_binding.dart';
|
||||
|
@ -85,6 +87,11 @@ class AppPages {
|
|||
name: AppRoutes.resultQuizPage,
|
||||
page: () => QuizResultView(),
|
||||
binding: QuizResultBinding(),
|
||||
),
|
||||
GetPage(
|
||||
name: AppRoutes.listingQuizPage,
|
||||
page: () => ListingsQuizView(),
|
||||
binding: ListingQuizBinding(),
|
||||
)
|
||||
];
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ abstract class AppRoutes {
|
|||
|
||||
static const quizCreatePage = "/quiz/creation";
|
||||
static const quizPreviewPage = "/quiz/preview";
|
||||
|
||||
static const listingQuizPage = "/quiz/listing";
|
||||
static const detailQuizPage = "/quiz/detail";
|
||||
|
||||
static const playQuizPage = "/quiz/play";
|
||||
|
|
|
@ -6,10 +6,13 @@ class RecomendationComponent extends StatelessWidget {
|
|||
final String title;
|
||||
final List<QuizListingModel> datas;
|
||||
final Function(String) itemOnTap;
|
||||
final Function() allOnTap;
|
||||
|
||||
const RecomendationComponent({
|
||||
required this.title,
|
||||
required this.datas,
|
||||
required this.itemOnTap,
|
||||
required this.allOnTap,
|
||||
super.key,
|
||||
});
|
||||
|
||||
|
@ -60,9 +63,12 @@ class RecomendationComponent extends StatelessWidget {
|
|||
title,
|
||||
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
Text(
|
||||
"Lihat semua",
|
||||
style: TextStyle(fontSize: 14, color: Colors.blue.shade700),
|
||||
GestureDetector(
|
||||
onTap: allOnTap,
|
||||
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 {
|
||||
final response = await _dio.get(APIEndpoint.quizRecomendation);
|
||||
final response = await _dio.get("${APIEndpoint.quizRecomendation}?page=$page&limit=$amount");
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final parsedResponse = BaseResponseModel<List<QuizListingModel>>.fromJson(
|
||||
|
|
|
@ -7,7 +7,7 @@ import 'package:quiz_app/data/services/quiz_service.dart';
|
|||
|
||||
class HomeController extends GetxController {
|
||||
final UserController _userController = Get.find<UserController>();
|
||||
QuizService _quizService;
|
||||
final QuizService _quizService;
|
||||
HomeController(this._quizService);
|
||||
|
||||
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 goToListingsQuizPage() => Get.toNamed(AppRoutes.listingQuizPage);
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ class HomeView extends GetView<HomeController> {
|
|||
title: "Quiz Rekomendasi",
|
||||
datas: controller.data.toList(),
|
||||
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 goToListingsQuizPage() => Get.toNamed(AppRoutes.listingQuizPage);
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
searchController.dispose();
|
||||
|
|
|
@ -32,6 +32,7 @@ class SearchView extends GetView<SearchQuizController> {
|
|||
title: "Quiz Rekomendasi",
|
||||
datas: controller.recommendationQData.toList(),
|
||||
itemOnTap: controller.goToDetailPage,
|
||||
allOnTap: controller.goToListingsQuizPage,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
|
@ -40,6 +41,7 @@ class SearchView extends GetView<SearchQuizController> {
|
|||
title: "Quiz Populer",
|
||||
datas: controller.recommendationQData.toList(),
|
||||
itemOnTap: controller.goToDetailPage,
|
||||
allOnTap: controller.goToListingsQuizPage,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
Loading…
Reference in New Issue