431 lines
19 KiB
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);
|
|
}
|