feat: finish implement subject on the home and search

This commit is contained in:
akhdanre 2025-05-05 09:39:19 +07:00
parent 80e6704bec
commit cd38b79bef
8 changed files with 136 additions and 42 deletions

View File

@ -1,11 +1,18 @@
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:quiz_app/data/services/quiz_service.dart'; import 'package:quiz_app/data/services/quiz_service.dart';
import 'package:quiz_app/data/services/subject_service.dart';
import 'package:quiz_app/feature/home/controller/home_controller.dart'; import 'package:quiz_app/feature/home/controller/home_controller.dart';
class HomeBinding extends Bindings { class HomeBinding extends Bindings {
@override @override
void dependencies() { void dependencies() {
Get.lazyPut<QuizService>(() => QuizService()); Get.lazyPut<QuizService>(() => QuizService());
Get.lazyPut<HomeController>(() => HomeController(Get.find<QuizService>())); Get.lazyPut<SubjectService>(() => SubjectService());
Get.lazyPut<HomeController>(
() => HomeController(
Get.find<QuizService>(),
Get.find<SubjectService>(),
),
);
} }
} }

View File

@ -3,23 +3,40 @@ 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';
import 'package:quiz_app/data/models/quiz/quiz_listing_model.dart'; import 'package:quiz_app/data/models/quiz/quiz_listing_model.dart';
import 'package:quiz_app/data/models/subject/subject_model.dart';
import 'package:quiz_app/data/services/quiz_service.dart'; import 'package:quiz_app/data/services/quiz_service.dart';
import 'package:quiz_app/data/services/subject_service.dart';
import 'package:quiz_app/feature/navigation/controllers/navigation_controller.dart';
class HomeController extends GetxController { class HomeController extends GetxController {
final UserController _userController = Get.find<UserController>(); final UserController _userController = Get.find<UserController>();
final QuizService _quizService; final QuizService _quizService;
HomeController(this._quizService); final SubjectService _subjectService;
HomeController(
this._quizService,
this._subjectService,
);
Rx<String> get userName => _userController.userName; Rx<String> get userName => _userController.userName;
Rx<String?> get userImage => _userController.userImage; Rx<String?> get userImage => _userController.userImage;
RxList<QuizListingModel> data = <QuizListingModel>[].obs; RxList<QuizListingModel> data = <QuizListingModel>[].obs;
RxList<SubjectModel> subjects = <SubjectModel>[].obs;
void goToQuizCreation() => Get.toNamed(AppRoutes.quizCreatePage); void goToQuizCreation() => Get.toNamed(AppRoutes.quizCreatePage);
void goToSearch() {
final navController = Get.find<NavigationController>();
navController.changePage(1);
}
@override @override
void onInit() { void onInit() {
_getRecomendationQuiz(); _getRecomendationQuiz();
loadSubjectData();
super.onInit(); super.onInit();
} }
@ -30,6 +47,13 @@ class HomeController extends GetxController {
} }
} }
void loadSubjectData() async {
BaseResponseModel<List<SubjectModel>>? respnse = await _subjectService.getSubject();
if (respnse != null) {
subjects.assignAll(respnse.data!);
}
}
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() => Get.toNamed(AppRoutes.listingQuizPage);

View File

@ -1,8 +1,16 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.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/data/models/subject/subject_model.dart';
class SearchComponent extends StatelessWidget { class SearchComponent extends StatelessWidget {
const SearchComponent({super.key}); final Function() onSearchTap;
final List<SubjectModel> subject;
const SearchComponent({
super.key,
required this.onSearchTap,
required this.subject,
});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -52,12 +60,18 @@ class SearchComponent extends StatelessWidget {
} }
Widget _buildCategoryRow() { Widget _buildCategoryRow() {
return Row( return SizedBox(
children: [ height: 30, // Set height for horizontal ListView
_buildCategoryComponent("History"), child: ListView.builder(
const SizedBox(width: 8), scrollDirection: Axis.horizontal,
_buildCategoryComponent("Science"), itemCount: subject.length,
], itemBuilder: (context, index) {
return Padding(
padding: EdgeInsets.only(right: index != subject.length - 1 ? 8.0 : 0),
child: _buildCategoryComponent(subject[index].alias),
);
},
),
); );
} }
@ -80,24 +94,33 @@ class SearchComponent extends StatelessWidget {
} }
Widget _buildSearchInput() { Widget _buildSearchInput() {
return Container( return GestureDetector(
decoration: BoxDecoration( onTap: () => onSearchTap(),
color: Colors.white, child: Container(
borderRadius: BorderRadius.circular(12), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
boxShadow: [ decoration: BoxDecoration(
BoxShadow( color: Colors.white,
color: Colors.black.withValues(alpha: 0.05), borderRadius: BorderRadius.circular(12),
blurRadius: 6, boxShadow: [
offset: Offset(0, 2), BoxShadow(
), color: Colors.black.withValues(alpha: 0.05),
], blurRadius: 6,
), offset: const Offset(0, 2),
child: const TextField( ),
decoration: InputDecoration( ],
hintText: "Search for quizzes...", ),
hintStyle: TextStyle(color: Color(0xFF6B778C)), child: Row(
border: InputBorder.none, children: const [
contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 12), Icon(Icons.search, color: Color(0xFF6B778C)),
SizedBox(width: 8),
Text(
"Search for quizzes...",
style: TextStyle(
color: Color(0xFF6B778C),
fontSize: 16,
),
),
],
), ),
), ),
); );

View File

@ -41,7 +41,10 @@ class HomeView extends GetView<HomeController> {
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(20),
child: Column( child: Column(
children: [ children: [
SearchComponent(), Obx(() => SearchComponent(
onSearchTap: controller.goToSearch,
subject: controller.subjects.toList(),
)),
const SizedBox(height: 20), const SizedBox(height: 20),
Obx( Obx(
() => RecomendationComponent( () => RecomendationComponent(

View File

@ -154,7 +154,6 @@ class QuizPlayController extends GetxController {
await Future.delayed(Duration(seconds: 2)); await Future.delayed(Duration(seconds: 2));
print(quizData);
Get.offAllNamed( Get.offAllNamed(
AppRoutes.resultQuizPage, AppRoutes.resultQuizPage,

View File

@ -1,5 +1,6 @@
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:quiz_app/data/services/quiz_service.dart'; import 'package:quiz_app/data/services/quiz_service.dart';
import 'package:quiz_app/data/services/subject_service.dart';
import 'package:quiz_app/feature/search/controller/search_controller.dart'; import 'package:quiz_app/feature/search/controller/search_controller.dart';
class SearchBinding extends Bindings { class SearchBinding extends Bindings {
@ -8,6 +9,12 @@ class SearchBinding extends Bindings {
if (!Get.isRegistered<QuizService>()) { if (!Get.isRegistered<QuizService>()) {
Get.lazyPut<QuizService>(() => QuizService()); Get.lazyPut<QuizService>(() => QuizService());
} }
Get.lazyPut<SearchQuizController>(() => SearchQuizController(Get.find<QuizService>()));
if (!Get.isRegistered()) Get.lazyPut<SubjectService>(() => SubjectService());
Get.lazyPut<SearchQuizController>(() => SearchQuizController(
Get.find<QuizService>(),
Get.find<SubjectService>(),
));
} }
} }

View File

@ -3,12 +3,18 @@ import 'package:get/get.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';
import 'package:quiz_app/data/models/subject/subject_model.dart';
import 'package:quiz_app/data/services/quiz_service.dart'; import 'package:quiz_app/data/services/quiz_service.dart';
import 'package:quiz_app/data/services/subject_service.dart';
class SearchQuizController extends GetxController { class SearchQuizController extends GetxController {
final QuizService _quizService; final QuizService _quizService;
final SubjectService _subjectService;
SearchQuizController(this._quizService); SearchQuizController(
this._quizService,
this._subjectService,
);
final searchController = TextEditingController(); final searchController = TextEditingController();
final searchText = ''.obs; final searchText = ''.obs;
@ -16,9 +22,12 @@ class SearchQuizController extends GetxController {
RxList<QuizListingModel> recommendationQData = <QuizListingModel>[].obs; RxList<QuizListingModel> recommendationQData = <QuizListingModel>[].obs;
RxList<QuizListingModel> searchQData = <QuizListingModel>[].obs; RxList<QuizListingModel> searchQData = <QuizListingModel>[].obs;
RxList<SubjectModel> subjects = <SubjectModel>[].obs;
@override @override
void onInit() { void onInit() {
getRecomendation(); getRecomendation();
loadSubjectData();
super.onInit(); super.onInit();
searchController.addListener(() { searchController.addListener(() {
searchText.value = searchController.text; searchText.value = searchController.text;
@ -49,6 +58,15 @@ class SearchQuizController extends GetxController {
void goToListingsQuizPage() => Get.toNamed(AppRoutes.listingQuizPage); void goToListingsQuizPage() => Get.toNamed(AppRoutes.listingQuizPage);
void loadSubjectData() async {
BaseResponseModel<List<SubjectModel>>? respnse = await _subjectService.getSubject();
if (respnse != null) {
subjects.assignAll(respnse.data!);
}
}
void goToDetailQuizListing() {}
@override @override
void onClose() { void onClose() {
searchController.dispose(); searchController.dispose();

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.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/feature/search/controller/search_controller.dart'; import 'package:quiz_app/feature/search/controller/search_controller.dart';
class SearchView extends GetView<SearchQuizController> { class SearchView extends GetView<SearchQuizController> {
@ -21,7 +22,7 @@ class SearchView extends GetView<SearchQuizController> {
_buildSearchBar(), _buildSearchBar(),
const SizedBox(height: 20), const SizedBox(height: 20),
if (isSearching) ...[ if (isSearching) ...[
_buildCategoryFilter(), Obx(() => _buildCategoryFilter(controller.subjects.toList())),
const SizedBox(height: 20), const SizedBox(height: 20),
...controller.searchQData.map( ...controller.searchQData.map(
(e) => QuizContainerComponent(data: e, onTap: controller.goToDetailPage), (e) => QuizContainerComponent(data: e, onTap: controller.goToDetailPage),
@ -78,17 +79,29 @@ class SearchView extends GetView<SearchQuizController> {
); );
} }
Widget _buildCategoryFilter() { Widget _buildCategoryFilter(List<SubjectModel> data) {
final categories = ['Fisika', 'Matematika', 'Agama', 'English', 'Sejarah', 'Biologi'];
return Wrap( return Wrap(
spacing: 8, spacing: 6,
runSpacing: 8, runSpacing: 1,
children: categories.map((cat) { children: data.map((cat) {
return Chip( return InkWell(
label: Text(cat), onTap: () => controller.goToDetailQuizListing,
padding: const EdgeInsets.symmetric(horizontal: 12), child: Container(
backgroundColor: Colors.white, padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
side: const BorderSide(color: Colors.black12), margin: const EdgeInsets.symmetric(vertical: 2),
decoration: BoxDecoration(
color: Color(0xFFD6E4FF),
borderRadius: BorderRadius.circular(15),
),
child: Text(
cat.alias,
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF0052CC),
),
),
),
); );
}).toList(), }).toList(),
); );