Files
Vocasia-LMS-Mobile-apps--TA…/lib/screens/detail_course/components/ulasan.dart

431 lines
19 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:initial_folder/helper/validator.dart';
import 'package:initial_folder/models/detail_rating_course_model.dart';
import 'package:initial_folder/providers/detail_rating_course_provider.dart';
import 'package:initial_folder/screens/detail_course/components/detail_list_ulasan.dart';
import 'package:initial_folder/size_config.dart';
import 'package:initial_folder/theme.dart';
import 'package:provider/provider.dart';
class Ulasan extends StatefulWidget {
const Ulasan({
Key? key,
required this.id,
}) : super(key: key);
final String id;
@override
_UlasanState createState() => _UlasanState();
}
class _UlasanState extends State<Ulasan> {
int _currentPage = 1;
int _totalPages = 1;
List<DataReview> _filteredReviews = []; // Menyimpan review yang sudah difilter
// Fungsi untuk membatasi ulasan per halaman
List<DataReview> _getPaginatedReviews(List<DataReview> reviews) {
int startIndex = (_currentPage - 1) * 5;
int endIndex = startIndex + 5;
return reviews.sublist(startIndex, endIndex.clamp(0, reviews.length));
}
void _filterReviews(int rating, List<DataReview> allReviews) {
setState(() {
_filteredReviews = allReviews.where((review) => double.parse(review.rating ?? '0') == rating).toList();
_totalPages = (_filteredReviews.length / 5).ceil();
_currentPage = 1; // Reset ke halaman pertama saat filter diubah
});
}
@override
Widget build(BuildContext context) {
final listChoices = <ItemChoice>[
ItemChoice(0, 0, 'Semua '),
ItemChoice(5, 1, '5'),
ItemChoice(4, 1, '4'),
ItemChoice(3, 1, '3'),
ItemChoice(2, 1, '2'),
ItemChoice(1, 1, '1'),
];
return SingleChildScrollView(
child: Column(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: EdgeInsets.only(
left: getProportionateScreenWidth(10),
right: getProportionateScreenWidth(10),
),
child: Consumer<DetailRatingCourseProvider>(
builder: (context, state, _) {
if (state.state == ResultState.Loading) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: CircularProgressIndicator(
color: primaryColor,
strokeWidth: 2,
),
),
);
} else if (state.state == ResultState.HasData) {
var ulasan = state.result;
// Hitung total halaman berdasarkan jumlah ulasan jika semua ulasan ditampilkan
if (state.currentIndex == 0) {
_filteredReviews = ulasan!.dataReview;
_totalPages = (_filteredReviews.length / 5).ceil();
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: getProportionateScreenHeight(20)),
Text(
'Ulasan Kursus',
style: thirdTextStyle.copyWith(
fontWeight: semiBold,
fontSize: getProportionateScreenWidth(15),
),
),
Row(
children: [
Text(
ulasan!.data.avgRating is List<dynamic>
? '0'
: double.parse(ulasan.data.avgRating)
.toStringAsFixed(1)
.replaceAll('.', ','),
style: thirdTextStyle.copyWith(
fontSize: getProportionateScreenWidth(29),
),
),
SizedBox(width: getProportionateScreenWidth(8)),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: getProportionateScreenHeight(2)),
RatingBarIndicator(
itemSize: getProportionateScreenWidth(10),
rating: ulasan.data.avgRating is List<dynamic>
? 5.0
: double.parse(ulasan.data.avgRating),
direction: Axis.horizontal,
itemCount: 5,
itemBuilder: (context, _) => FaIcon(
FontAwesomeIcons.solidStar,
color: thirteenColor,
),
),
SizedBox(
height: getProportionateScreenHeight(2)),
Text(
'(${ulasan.dataReview.length} Ulasan)',
style: thirdTextStyle.copyWith(
fontSize: getProportionateScreenWidth(12),
),
),
],
),
],
),
SizedBox(height: getProportionateScreenHeight(11)),
VerticalRatingBar(
lebar: ulasan.data.precentageRating.rating5.toString(),
text: '5.0',
total: (ulasan.data.precentageRating.rating5.runtimeType == String)
? '${persentaseUlasan(ulasan.data.precentageRating.rating5)}'
: '${ulasan.data.precentageRating.rating5}'),
VerticalRatingBar(
lebar: ulasan.data.precentageRating.rating4.toString(),
text: '4.0',
total: (ulasan.data.precentageRating.rating4.runtimeType == String)
? '${persentaseUlasan(ulasan.data.precentageRating.rating4)}'
: '${ulasan.data.precentageRating.rating4}'),
VerticalRatingBar(
lebar: ulasan.data.precentageRating.rating3.toString(),
text: '3.0',
total: (ulasan.data.precentageRating.rating3.runtimeType == String)
? '${persentaseUlasan(ulasan.data.precentageRating.rating3)}'
: '${ulasan.data.precentageRating.rating3}'),
VerticalRatingBar(
lebar: ulasan.data.precentageRating.rating2.toString(),
text: '2.0',
total: (ulasan.data.precentageRating.rating2.runtimeType == String)
? '${persentaseUlasan(ulasan.data.precentageRating.rating2)}'
: '${ulasan.data.precentageRating.rating2}'),
VerticalRatingBar(
lebar: ulasan.data.precentageRating.rating1.toString(),
text: '1.0',
total: (ulasan.data.precentageRating.rating1.runtimeType == String)
? '${persentaseUlasan(ulasan.data.precentageRating.rating1)}'
: '${ulasan.data.precentageRating.rating1}'),
],
);
}
return Text(
'Terjadi Kesalan ',
style: primaryTextStyle,
);
}),
),
],
),
SizedBox(height: getProportionateScreenHeight(11)),
Consumer<DetailRatingCourseProvider>(
builder: (context, state, _) {
if (state.state == ResultState.HasData) {
var ulasan = state.result;
return Column(
children: [
SizedBox(height: getProportionateScreenHeight(8)),
Wrap(
spacing: 6,
runSpacing: 3,
children: listChoices
.map(
(e) => InkWell(
onTap: () {
state.currentIndex = e.id;
if (state.currentIndex == 0) {
// Reset ke semua review jika memilih filter "Semua"
Provider.of<DetailRatingCourseProvider>(context, listen: false)
.getDetailCourse();
_filteredReviews = ulasan!.dataReview;
_totalPages = (_filteredReviews.length / 5).ceil();
_currentPage = 1;
} else {
// Filter sesuai dengan rating yang dipilih
_filterReviews(e.id, ulasan!.dataReview);
}
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 5),
decoration: BoxDecoration(
color: state.currentIndex == e.id
? Theme.of(context)
.colorScheme
.onBackground
: Theme.of(context)
.colorScheme
.primaryContainer,
border: Border.all(
color: Theme.of(context)
.colorScheme
.brightness ==
Brightness.dark
? seventeenColor
: secondaryColor,
),
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: baruTexthitam.withOpacity(0.2),
blurRadius: 2,
spreadRadius: 1,
offset: Offset(0, 2),
)
],
),
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: getProportionateScreenWidth(5),
vertical: getProportionateScreenHeight(5),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
(e.icon == 0)
? Text('')
: FaIcon(
FontAwesomeIcons.solidStar,
color: thirteenColor,
size: getProportionateScreenWidth(11),
),
SizedBox(width: getProportionateScreenWidth(3)),
Text(
e.label,
style: thirdTextStyle.copyWith(
color: state.currentIndex == e.id
? Theme.of(context)
.colorScheme
.background
: e.label == "Semua "
? primaryColor
: Theme.of(context)
.colorScheme
.onBackground,
fontSize: getProportionateScreenWidth(12),
fontWeight: reguler,
),
),
],
),
),
),
),
)
.toList(),
),
SizedBox(height: getProportionateScreenHeight(17)),
Container(
margin: EdgeInsets.only(
left: getProportionateScreenWidth(15),
right: getProportionateScreenWidth(10),
),
child: (_filteredReviews.isEmpty)
? SizedBox(height: getProportionateScreenHeight(15))
: Column(
children: _getPaginatedReviews(_filteredReviews)
.map(
(e) => DetailListUlasan(
review: e.review ?? '',
date: e.date ?? '-',
name: e.name ?? '',
starRating: double.parse(e.rating ?? '5'),
),
)
.toList()),
),
// Navigasi Pagination
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (_filteredReviews.isEmpty)
Text(
'Belum ada rating untuk kategori bintang ini',
style: thirdTextStyle.copyWith(
fontSize: getProportionateScreenWidth(12),
),
)
else ...[
if (_currentPage > 1)
IconButton(
onPressed: () {
setState(() {
_currentPage--;
});
},
icon: Icon(Icons.arrow_back),
),
Text('$_currentPage of $_totalPages'),
if (_currentPage < _totalPages)
IconButton(
onPressed: () {
setState(() {
_currentPage++;
});
},
icon: Icon(Icons.arrow_forward),
),
]
],
),
],
);
}
return Text('');
},
),
SizedBox(
height: 30,
)
],
),
);
}
}
class VerticalRatingBar extends StatelessWidget {
const VerticalRatingBar({
Key? key,
required this.text,
this.lebar = '0',
required this.total,
}) : super(key: key);
final String text;
final String total;
final String lebar;
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(bottom: getProportionateScreenWidth(8)),
child: Column(
children: [
Row(
children: [
FaIcon(
FontAwesomeIcons.solidStar,
color: thirteenColor,
size: getProportionateScreenWidth(11),
),
SizedBox(width: getProportionateScreenWidth(4)),
Text(
text,
style: thirdTextStyle.copyWith(
fontSize: getProportionateScreenWidth(10),
),
),
SizedBox(width: getProportionateScreenWidth(6)),
Expanded(
child: Column(
children: [
Stack(
children: [
Container(
height: getProportionateScreenWidth(4),
decoration: BoxDecoration(
color: fourthColor,
borderRadius: BorderRadius.circular(10)),
),
Container(
width: (SizeConfig.screenWidth -
getProportionateScreenWidth(110)) *
double.parse(persentaseUlasan(lebar)
.replaceAll(',', '.')) /
100,
height: getProportionateScreenWidth(4),
decoration: BoxDecoration(
color: thirteenColor,
borderRadius: BorderRadius.circular(10)),
)
],
),
],
),
),
SizedBox(width: getProportionateScreenWidth(8)),
Container(
width: getProportionateScreenWidth(30),
child: Text(
total,
style: thirdTextStyle.copyWith(
fontSize: getProportionateScreenWidth(10),
),
textAlign: TextAlign.start,
),
)
],
),
],
),
);
}
}
class ItemChoice {
final int id;
final int icon;
final String label;
ItemChoice(this.id, this.icon, this.label);
}