Initial commit: Penyerahan final Source code Tugas Akhir

This commit is contained in:
ferdiakhh
2025-07-10 19:15:14 +07:00
commit e1f2206b8a
687 changed files with 80132 additions and 0 deletions

View File

@ -0,0 +1,462 @@
import 'package:flutter/material.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:initial_folder/helper/validator.dart';
import 'package:initial_folder/size_config.dart';
import 'package:initial_folder/theme.dart';
import 'package:initial_folder/widgets/custom_navigator_pop.dart';
import 'package:initial_folder/widgets/search_and_filter_course.dart';
import 'package:provider/provider.dart';
import 'package:initial_folder/providers/filters_course_provider.dart'
as filterCourseProv;
import 'package:initial_folder/providers/search_provider.dart'
as searchProvider;
class Filter extends StatelessWidget {
const Filter({
Key? key,
this.isNotSearch,
this.onApplyFilter,
}) : super(key: key);
final bool? isNotSearch;
final VoidCallback? onApplyFilter;
@override
Widget build(BuildContext context) {
filterCourseProv.FilterCourseProvider filterCourseProvider =
Provider.of<filterCourseProv.FilterCourseProvider>(context);
final listChoices = <ItemChoiceFilter>[
ItemChoiceFilter('', '0', 'Semua '),
ItemChoiceFilter('5', '1', '5'),
ItemChoiceFilter('4', '1', '4'),
ItemChoiceFilter('3', '1', '3'),
ItemChoiceFilter('2', '1', '2'),
ItemChoiceFilter('1', '1', '1'),
];
Widget categoriCheckBox(String id, String title, [Function? onChange]) {
return Container(
margin: EdgeInsets.only(bottom: getProportionateScreenWidth(8)),
child: Row(
children: [
SizedBox(
width: 24,
height: 24,
child: Checkbox(
value:
filterCourseProvider.categories.contains(id) ? true : false,
onChanged: id == ''
? (c) {
filterCourseProvider.allCategories();
filterCourseProvider.addCategory(id);
}
: (c) {
if (filterCourseProvider.categories.contains(id)) {
filterCourseProvider.removeCategory(id);
} else {
filterCourseProvider.removeCategory('');
filterCourseProvider.addCategory(id);
}
},
activeColor: Theme.of(context).brightness == Brightness.dark
? thirteenColor
: twelveColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5),
),
side:
BorderSide(color: Theme.of(context).colorScheme.onPrimary),
),
),
SizedBox(width: getProportionateScreenWidth(12)),
Expanded(
child: GestureDetector(
onTap: id == ''
? () {
filterCourseProvider.allCategories();
filterCourseProvider.addCategory(id);
}
: () {
if (filterCourseProvider.categories.contains(id)) {
filterCourseProvider.removeCategory(id);
} else {
filterCourseProvider.removeCategory('');
filterCourseProvider.addCategory(id);
}
},
child: Text(
title,
style: primaryTextStyle.copyWith(
color: secondaryColor,
fontSize: getProportionateScreenWidth(12),
letterSpacing: 0.5),
),
),
),
],
),
);
}
Widget levelCheckBox(String name, String title) {
return Container(
margin: EdgeInsets.only(bottom: getProportionateScreenWidth(8)),
child: Row(
children: [
SizedBox(
width: 24,
height: 24,
child: Checkbox(
checkColor: Theme.of(context).colorScheme.background,
value:
filterCourseProvider.levels.contains(name) ? true : false,
onChanged: name == ''
? (c) {
filterCourseProvider.allLevel();
filterCourseProvider.addLevel(name);
}
: (c) {
if (filterCourseProvider.levels.contains(name)) {
filterCourseProvider.removeLevel(name);
} else {
filterCourseProvider.removeLevel('');
filterCourseProvider.addLevel(name);
}
},
activeColor: primaryColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5),
),
side:
BorderSide(color: Theme.of(context).colorScheme.onPrimary),
),
),
SizedBox(width: getProportionateScreenWidth(12)),
Expanded(
child: GestureDetector(
onTap: name == ''
? () {
filterCourseProvider.allLevel();
filterCourseProvider.addLevel(name);
}
: () {
// filterCourseProvider.currentIndex = id;
if (filterCourseProvider.levels.contains(name)) {
filterCourseProvider.removeLevel(name);
} else {
filterCourseProvider.removeLevel('');
filterCourseProvider.addLevel(name);
}
},
child: Text(
title,
style: thirdTextStyle.copyWith(
fontSize: getProportionateScreenWidth(12),
letterSpacing: 0.5),
),
),
),
],
),
);
}
Widget radioPrice(
String val,
String title,
) {
return Container(
margin: EdgeInsets.only(bottom: getProportionateScreenWidth(8)),
child: Row(
children: [
SizedBox(
width: 24,
height: 24,
child: Theme(
data: ThemeData(
unselectedWidgetColor: fourthColor,
),
child: Radio(
activeColor: primaryColor,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
value: val,
groupValue: filterCourseProvider.currentIndexPrice,
onChanged: (c) {
filterCourseProvider.currentIndexPrice = val;
}),
),
),
SizedBox(
width: getProportionateScreenWidth(12),
),
Expanded(
child: GestureDetector(
onTap: () {
filterCourseProvider.currentIndexPrice = val;
},
child: Text(
title,
style: thirdTextStyle.copyWith(
fontSize: getProportionateScreenWidth(12),
letterSpacing: 0.5,
),
),
),
),
],
),
);
}
final provSearch =
Provider.of<searchProvider.SearchProvider>(context, listen: false);
Widget bottomNav() {
return Container(
width: double.infinity,
height: getProportionateScreenHeight(60),
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
offset: Offset(0, -2),
color: Theme.of(context).colorScheme.primaryContainer,
)
],
),
child: Center(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
minimumSize: Size(getProportionateScreenWidth(110),
getProportionateScreenHeight(33)),
backgroundColor: primaryColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15)),
),
onPressed: () {
provSearch.searchText = "";
Provider.of<filterCourseProv.FilterCourseProvider>(context,
listen: false)
.isSearchsTrue();
filterCourseProvider.filter(
category: filterCourseProvider.categories.join(','),
price: filterCourseProvider.currentIndexPrice,
level: filterCourseProvider.levels.join(','),
rating: filterCourseProvider.currentIndexRating,
keyword: Provider.of<searchProvider.SearchProvider>(context,
listen: false)
.searchTextFilter,
);
onApplyFilter!();
isNotSearch == null
? Navigator.pop(context)
: Navigator.pushReplacement(
context,
CustomNavigatorPop(
child: SearchAndFilterCourse(),
),
);
},
child: Text(
'Filter',
style: thirdTextStyle.copyWith(color: baruTextutih),
),
),
),
);
}
return Scaffold(
backgroundColor: Theme.of(context).colorScheme.background,
appBar: AppBar(
backgroundColor: Theme.of(context).brightness == Brightness.dark
? twelveColor
: baruTextutih,
leading: IconButton(
icon: Icon(Icons.close),
onPressed: () {
Navigator.pop(context);
},
),
actions: [
TextButton(
onPressed: () {
filterCourseProvider.resetFilter();
},
child: Text(
'Reset',
style: primaryTextStyle.copyWith(
letterSpacing: 0.5,
color: primaryColor,
),
),
)
],
),
body: Container(
margin:
EdgeInsets.symmetric(horizontal: getProportionateScreenWidth(16)),
child: ListView(
children: [
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Theme.of(context).brightness == Brightness.dark
? seventeenColor
: secondaryColor.withOpacity(0.3),
),
height: 40,
child: Consumer<searchProvider.SearchProvider>(
builder: (context, state, _) => TextField(
autofocus: false,
onSubmitted: (value) {
filterCourseProvider.isSearchsFalse();
state.searchTextFilter = validatorSearchFilter(value);
state.initSearchCourse(
price: filterCourseProvider.currentIndexPrice,
level: filterCourseProvider.levels.join(','),
rating: filterCourseProvider.currentIndexRating,
);
},
style: primaryTextStyle.copyWith(
fontSize: getProportionateScreenWidth(14),
letterSpacing: 0.5,
),
onChanged: (value) {
state.searchTextFilter = validatorSearchFilter(value);
},
cursorColor: secondaryColor,
decoration: InputDecoration(
border: InputBorder.none,
errorBorder: OutlineInputBorder(
borderSide: BorderSide(color: sevenColor),
borderRadius: BorderRadius.circular(10)),
contentPadding:
EdgeInsets.only(top: getProportionateScreenHeight(2)),
prefixIcon: Icon(
FeatherIcons.search,
size: 20,
color: primaryColor,
),
hintText: 'Cari Kursus',
hintStyle: primaryTextStyle.copyWith(
fontSize: getProportionateScreenWidth(12),
color: secondaryColor,
letterSpacing: 0.5,
),
),
),
),
),
SizedBox(height: getProportionateScreenHeight(20)),
Text(
'Harga',
style:
thirdTextStyle.copyWith(letterSpacing: 1, fontWeight: bold),
),
SizedBox(height: getProportionateScreenWidth(8)),
radioPrice('', 'Semua'),
radioPrice('1', 'Gratis'),
radioPrice('0', 'Berbayar'),
Text(
'Tingkat',
style:
thirdTextStyle.copyWith(letterSpacing: 1, fontWeight: bold),
),
SizedBox(height: getProportionateScreenWidth(8)),
levelCheckBox('', 'Semua'),
levelCheckBox('beginner', 'Pemula'),
levelCheckBox('intermediate', 'Menengah'),
levelCheckBox('high', 'Ahli'),
Text(
'Rating',
style:
thirdTextStyle.copyWith(letterSpacing: 1, fontWeight: bold),
),
SizedBox(height: getProportionateScreenWidth(8)),
Wrap(
spacing: 5,
runSpacing: 5,
children: listChoices
.map(
(e) => ChoiceChip(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30)),
showCheckmark: false,
materialTapTargetSize: MaterialTapTargetSize.padded,
backgroundColor:
Theme.of(context).colorScheme.primaryContainer,
side: BorderSide(
color: Theme.of(context).brightness == Brightness.dark
? baruTextutih
: primaryColor,
),
labelPadding: const EdgeInsets.symmetric(horizontal: 6),
label: Row(
mainAxisSize: MainAxisSize.min,
children: [
(e.icon == '0')
? const Text('')
: FaIcon(
FontAwesomeIcons.solidStar,
color: thirteenColor,
size: getProportionateScreenWidth(11),
),
const SizedBox(
width: 4,
),
Text(
e.label,
style: primaryTextStyle.copyWith(
color: filterCourseProvider.currentIndexRating ==
e.id.toString()
? Theme.of(context).colorScheme.background
: Theme.of(context).colorScheme.onPrimary,
letterSpacing: 0.5,
fontSize: getProportionateScreenWidth(12),
fontWeight: reguler,
),
),
],
),
selected: filterCourseProvider.currentIndexRating ==
e.id.toString(),
selectedColor:
Theme.of(context).brightness == Brightness.dark
? baruTextutih
: primaryColor,
onSelected: (_) {
filterCourseProvider.currentIndexRating =
e.id.toString();
},
elevation: 1,
),
)
.toList(),
),
SizedBox(
height: getProportionateScreenWidth(16),
),
],
),
),
bottomNavigationBar: bottomNav(),
);
}
}
class ItemChoiceFilter {
final String id;
final String icon;
final String label;
ItemChoiceFilter(this.id, this.icon, this.label);
}

View File

@ -0,0 +1,59 @@
import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import '../../../size_config.dart';
import '../../../theme.dart';
class ListCategoryIcon extends StatelessWidget {
const ListCategoryIcon({
Key? key,
required this.title,
required this.iconFa,
required this.onTap,
}) : super(key: key);
final String title;
final Icon iconFa;
final Function()? onTap;
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(
left: getProportionateScreenWidth(21),
right: getProportionateScreenWidth(17),
bottom: getProportionateScreenWidth(11)),
child: InkWell(
onTap: onTap,
child: Row(
children: [
iconFa,
SizedBox(
width: getProportionateScreenWidth(10),
),
Expanded(
child: Html(
data: title,
style: {
"body": Style(
margin: Margins.zero,
padding:
HtmlPaddings.only(left: getProportionateScreenWidth(6)),
fontSize: FontSize(getProportionateScreenWidth(13)),
fontWeight: reguler,
letterSpacing: 0.5,
fontFamily: 'Poppins',
),
},
),
),
Icon(
Icons.keyboard_arrow_right,
size: 22,
),
SizedBox(height: getProportionateScreenHeight(25)),
],
),
),
);
}
}