feat: done working on subject quiz listing
This commit is contained in:
parent
cd38b79bef
commit
93ab86e833
|
@ -0,0 +1 @@
|
||||||
|
enum ListingType { recomendation, populer, subject }
|
|
@ -74,9 +74,17 @@ class QuizService extends GetxService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<BaseResponseModel<List<QuizListingModel>>?> searchQuiz(String keyword) async {
|
Future<BaseResponseModel<List<QuizListingModel>>?> searchQuiz(String keyword, int page, {int limit = 10, String? subjectId}) async {
|
||||||
try {
|
try {
|
||||||
final response = await _dio.get("${APIEndpoint.quizSearch}?keyword=$keyword&page=1&limit=10");
|
final queryParams = {
|
||||||
|
"keyword": keyword,
|
||||||
|
"page": page.toString(),
|
||||||
|
"limit": limit.toString(),
|
||||||
|
if (subjectId != null && subjectId.isNotEmpty) "subject_id": subjectId,
|
||||||
|
};
|
||||||
|
|
||||||
|
final uri = Uri.parse(APIEndpoint.quizSearch).replace(queryParameters: queryParams);
|
||||||
|
final response = await _dio.getUri(uri);
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final parsedResponse = BaseResponseModel<List<QuizListingModel>>.fromJson(
|
final parsedResponse = BaseResponseModel<List<QuizListingModel>>.fromJson(
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:quiz_app/app/const/enums/listing_type.dart';
|
||||||
import 'package:quiz_app/app/routes/app_pages.dart';
|
import 'package:quiz_app/app/routes/app_pages.dart';
|
||||||
import 'package:quiz_app/data/controllers/user_controller.dart';
|
import 'package:quiz_app/data/controllers/user_controller.dart';
|
||||||
import 'package:quiz_app/data/models/base/base_model.dart';
|
import 'package:quiz_app/data/models/base/base_model.dart';
|
||||||
|
@ -56,5 +57,8 @@ 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);
|
void goToListingsQuizPage(ListingType page, {String? subjectId, String? subjecName}) => Get.toNamed(
|
||||||
|
AppRoutes.listingQuizPage,
|
||||||
|
arguments: {"page": page, "id": subjectId, "subject_name": subjecName},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,12 @@ import 'package:quiz_app/data/models/subject/subject_model.dart';
|
||||||
|
|
||||||
class SearchComponent extends StatelessWidget {
|
class SearchComponent extends StatelessWidget {
|
||||||
final Function() onSearchTap;
|
final Function() onSearchTap;
|
||||||
|
final Function(String, String) onSubjectTap;
|
||||||
final List<SubjectModel> subject;
|
final List<SubjectModel> subject;
|
||||||
|
|
||||||
const SearchComponent({
|
const SearchComponent({
|
||||||
super.key,
|
super.key,
|
||||||
|
required this.onSubjectTap,
|
||||||
required this.onSearchTap,
|
required this.onSearchTap,
|
||||||
required this.subject,
|
required this.subject,
|
||||||
});
|
});
|
||||||
|
@ -66,9 +68,12 @@ class SearchComponent extends StatelessWidget {
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
itemCount: subject.length,
|
itemCount: subject.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return Padding(
|
return GestureDetector(
|
||||||
padding: EdgeInsets.only(right: index != subject.length - 1 ? 8.0 : 0),
|
onTap: () => onSubjectTap(subject[index].id, subject[index].alias),
|
||||||
child: _buildCategoryComponent(subject[index].alias),
|
child: Padding(
|
||||||
|
padding: EdgeInsets.only(right: index != subject.length - 1 ? 8.0 : 0),
|
||||||
|
child: _buildCategoryComponent(subject[index].alias),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:quiz_app/app/const/colors/app_colors.dart';
|
import 'package:quiz_app/app/const/colors/app_colors.dart';
|
||||||
|
import 'package:quiz_app/app/const/enums/listing_type.dart';
|
||||||
import 'package:quiz_app/feature/home/controller/home_controller.dart';
|
import 'package:quiz_app/feature/home/controller/home_controller.dart';
|
||||||
import 'package:quiz_app/feature/home/view/component/button_option.dart';
|
import 'package:quiz_app/feature/home/view/component/button_option.dart';
|
||||||
import 'package:quiz_app/component/widget/recomendation_component.dart';
|
import 'package:quiz_app/component/widget/recomendation_component.dart';
|
||||||
|
@ -44,6 +45,7 @@ class HomeView extends GetView<HomeController> {
|
||||||
Obx(() => SearchComponent(
|
Obx(() => SearchComponent(
|
||||||
onSearchTap: controller.goToSearch,
|
onSearchTap: controller.goToSearch,
|
||||||
subject: controller.subjects.toList(),
|
subject: controller.subjects.toList(),
|
||||||
|
onSubjectTap: (p0, p1) => controller.goToListingsQuizPage(ListingType.subject, subjectId: p0, subjecName: p1),
|
||||||
)),
|
)),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
Obx(
|
Obx(
|
||||||
|
@ -51,7 +53,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,
|
allOnTap: () => controller.goToListingsQuizPage(ListingType.recomendation),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:quiz_app/app/const/enums/listing_type.dart';
|
||||||
import 'package:quiz_app/app/routes/app_pages.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/base/base_model.dart';
|
||||||
import 'package:quiz_app/data/models/quiz/quiz_listing_model.dart';
|
import 'package:quiz_app/data/models/quiz/quiz_listing_model.dart';
|
||||||
|
@ -20,13 +21,37 @@ class ListingQuizController extends GetxController {
|
||||||
int currentPage = 1;
|
int currentPage = 1;
|
||||||
bool hasMore = true;
|
bool hasMore = true;
|
||||||
|
|
||||||
|
RxString appBarTitle = "Quiz Recommendation".obs;
|
||||||
|
bool isSearchMode = false;
|
||||||
|
String? currentSubjectId;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
_getRecomendationQuiz();
|
loadData();
|
||||||
scrollController.addListener(_onScroll);
|
scrollController.addListener(_onScroll);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void loadData() {
|
||||||
|
final Map<String, dynamic> data = Get.arguments as Map<String, dynamic>;
|
||||||
|
final pageType = data['page'] as ListingType;
|
||||||
|
|
||||||
|
switch (pageType) {
|
||||||
|
case ListingType.populer:
|
||||||
|
appBarTitle.value = "Quiz Populer";
|
||||||
|
_loadRecommendation(resetPage: true);
|
||||||
|
break;
|
||||||
|
case ListingType.recomendation:
|
||||||
|
appBarTitle.value = "Quiz Recommendation";
|
||||||
|
_loadRecommendation(resetPage: true);
|
||||||
|
break;
|
||||||
|
case ListingType.subject:
|
||||||
|
appBarTitle.value = "Quiz ${data["subject_name"]}";
|
||||||
|
_loadBySubject(subjectId: data["id"], resetPage: true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void _onScroll() {
|
void _onScroll() {
|
||||||
if (scrollController.position.pixels >= scrollController.position.maxScrollExtent - 200) {
|
if (scrollController.position.pixels >= scrollController.position.maxScrollExtent - 200) {
|
||||||
if (!isLoadingMore.value && hasMore) {
|
if (!isLoadingMore.value && hasMore) {
|
||||||
|
@ -35,28 +60,60 @@ class ListingQuizController extends GetxController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _getRecomendationQuiz() async {
|
Future<void> _loadRecommendation({bool resetPage = false}) async {
|
||||||
|
isSearchMode = false;
|
||||||
|
currentSubjectId = null;
|
||||||
|
if (resetPage) currentPage = 1;
|
||||||
|
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
currentPage = 1;
|
|
||||||
BaseResponseModel? response = await _quizService.recomendationQuiz(amount: amountQuiz);
|
final response = await _quizService.recomendationQuiz(page: currentPage, amount: amountQuiz);
|
||||||
if (response != null && response.data != null) {
|
_handleResponse(response, resetPage: resetPage);
|
||||||
final data = response.data as List<QuizListingModel>;
|
|
||||||
quizzes.assignAll(data);
|
isLoading.value = false;
|
||||||
hasMore = data.length == amountQuiz;
|
}
|
||||||
}
|
|
||||||
|
Future<void> _loadBySubject({required String subjectId, bool resetPage = false}) async {
|
||||||
|
isSearchMode = true;
|
||||||
|
currentSubjectId = subjectId;
|
||||||
|
if (resetPage) currentPage = 1;
|
||||||
|
|
||||||
|
isLoading.value = true;
|
||||||
|
|
||||||
|
final response = await _quizService.searchQuiz("", currentPage, subjectId: subjectId);
|
||||||
|
_handleResponse(response, resetPage: resetPage);
|
||||||
|
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> loadMoreQuiz() async {
|
Future<void> loadMoreQuiz() async {
|
||||||
|
if (!hasMore) return;
|
||||||
|
|
||||||
isLoadingMore.value = true;
|
isLoadingMore.value = true;
|
||||||
currentPage++;
|
currentPage++;
|
||||||
BaseResponseModel? response = await _quizService.recomendationQuiz(page: currentPage, amount: amountQuiz);
|
|
||||||
|
BaseResponseModel? response;
|
||||||
|
if (isSearchMode && currentSubjectId != null) {
|
||||||
|
response = await _quizService.searchQuiz("", currentPage, subjectId: currentSubjectId!);
|
||||||
|
} else {
|
||||||
|
response = await _quizService.recomendationQuiz(page: currentPage, amount: amountQuiz);
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleResponse(response, resetPage: false);
|
||||||
|
|
||||||
|
isLoadingMore.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleResponse(BaseResponseModel? response, {required bool resetPage}) {
|
||||||
if (response != null && response.data != null) {
|
if (response != null && response.data != null) {
|
||||||
final data = response.data as List<QuizListingModel>;
|
final data = response.data as List<QuizListingModel>;
|
||||||
quizzes.addAll(data);
|
if (resetPage) {
|
||||||
|
quizzes.assignAll(data);
|
||||||
|
} else {
|
||||||
|
quizzes.addAll(data);
|
||||||
|
}
|
||||||
hasMore = data.length == amountQuiz;
|
hasMore = data.length == amountQuiz;
|
||||||
}
|
}
|
||||||
isLoadingMore.value = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void goToDetailQuiz(String quizId) => Get.toNamed(AppRoutes.detailQuizPage, arguments: quizId);
|
void goToDetailQuiz(String quizId) => Get.toNamed(AppRoutes.detailQuizPage, arguments: quizId);
|
||||||
|
|
|
@ -13,8 +13,10 @@ class ListingsQuizView extends GetView<ListingQuizController> {
|
||||||
backgroundColor: AppColors.background,
|
backgroundColor: AppColors.background,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
title: const Text(
|
title: Obx(
|
||||||
'Daftar Kuis',
|
() => Text(
|
||||||
|
controller.appBarTitle.value,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: Padding(
|
body: Padding(
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:quiz_app/app/const/enums/listing_type.dart';
|
||||||
import 'package:quiz_app/app/routes/app_pages.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/base/base_model.dart';
|
||||||
import 'package:quiz_app/data/models/quiz/quiz_listing_model.dart';
|
import 'package:quiz_app/data/models/quiz/quiz_listing_model.dart';
|
||||||
|
@ -35,7 +36,7 @@ class SearchQuizController extends GetxController {
|
||||||
debounce<String>(
|
debounce<String>(
|
||||||
searchText,
|
searchText,
|
||||||
(value) => getSearchData(value),
|
(value) => getSearchData(value),
|
||||||
time: Duration(seconds: 2),
|
time: Duration(seconds: 1),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +49,7 @@ class SearchQuizController extends GetxController {
|
||||||
|
|
||||||
void getSearchData(String keyword) async {
|
void getSearchData(String keyword) async {
|
||||||
searchQData.clear();
|
searchQData.clear();
|
||||||
BaseResponseModel? response = await _quizService.searchQuiz(keyword);
|
BaseResponseModel? response = await _quizService.searchQuiz(keyword, 1);
|
||||||
if (response != null) {
|
if (response != null) {
|
||||||
searchQData.assignAll(response.data);
|
searchQData.assignAll(response.data);
|
||||||
}
|
}
|
||||||
|
@ -56,7 +57,10 @@ 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);
|
void goToListingsQuizPage(ListingType page, {String? subjectId, String? subjecName}) => Get.toNamed(
|
||||||
|
AppRoutes.listingQuizPage,
|
||||||
|
arguments: {"page": page, "id": subjectId, "subject_name": subjecName},
|
||||||
|
);
|
||||||
|
|
||||||
void loadSubjectData() async {
|
void loadSubjectData() async {
|
||||||
BaseResponseModel<List<SubjectModel>>? respnse = await _subjectService.getSubject();
|
BaseResponseModel<List<SubjectModel>>? respnse = await _subjectService.getSubject();
|
||||||
|
@ -65,8 +69,6 @@ class SearchQuizController extends GetxController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void goToDetailQuizListing() {}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onClose() {
|
void onClose() {
|
||||||
searchController.dispose();
|
searchController.dispose();
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:quiz_app/app/const/enums/listing_type.dart';
|
||||||
import 'package:quiz_app/component/quiz_container_component.dart';
|
import 'package:quiz_app/component/quiz_container_component.dart';
|
||||||
import 'package:quiz_app/component/widget/recomendation_component.dart';
|
import 'package:quiz_app/component/widget/recomendation_component.dart';
|
||||||
import 'package:quiz_app/data/models/subject/subject_model.dart';
|
import 'package:quiz_app/data/models/subject/subject_model.dart';
|
||||||
|
@ -33,7 +34,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,
|
allOnTap: () => controller.goToListingsQuizPage(ListingType.recomendation),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 30),
|
const SizedBox(height: 30),
|
||||||
|
@ -42,7 +43,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,
|
allOnTap: () => controller.goToListingsQuizPage(ListingType.populer),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -85,7 +86,7 @@ class SearchView extends GetView<SearchQuizController> {
|
||||||
runSpacing: 1,
|
runSpacing: 1,
|
||||||
children: data.map((cat) {
|
children: data.map((cat) {
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () => controller.goToDetailQuizListing,
|
onTap: () => controller.goToListingsQuizPage(ListingType.subject, subjectId: cat.id, subjecName: cat.alias),
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||||
margin: const EdgeInsets.symmetric(vertical: 2),
|
margin: const EdgeInsets.symmetric(vertical: 2),
|
||||||
|
|
Loading…
Reference in New Issue