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,134 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:initial_folder/models/section_model.dart';
import 'package:initial_folder/providers/selected_title_provider.dart';
import 'package:initial_folder/services/announcement_service.dart';
import 'package:initial_folder/size_config.dart';
import 'package:initial_folder/theme.dart';
import 'package:initial_folder/widgets/announcement_user_page.dart';
import 'package:provider/provider.dart';
class Announcement extends StatefulWidget {
const Announcement({
Key? key,
required this.id,
required this.lesonOper,
required this.sectionOper,
required this.lessonMapIdoper,
required this.dataLessonOper,
}) : super(key: key);
final id;
final lesonOper;
final sectionOper;
final lessonMapIdoper;
final List<DataLesson> dataLessonOper;
@override
State<Announcement> createState() => _AnnouncementState();
String? showSummary(String? selectedTitle) {
String? selectedSummary = dataLessonOper
.firstWhere((data) => data.title == selectedTitle,
orElse: () => DataLesson(
summary: ' Tidak ada ringkasan'))
.summary;
return selectedSummary;
}
}
class _AnnouncementState extends State<Announcement> {
double value = 0;
final _controller = TextEditingController();
@override
void initState() {
super.initState();
AnnouncementService().getAnnouncement(widget.id);
}
@override
Widget build(BuildContext context) {
final selectedTitle =
Provider.of<SelectedTitleProvider>(context).selectedTitle;
return SingleChildScrollView(
child: GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: EdgeInsets.only(
left: getProportionateScreenWidth(16),
right: getProportionateScreenWidth(16),
top: getProportionateScreenWidth(16),
),
child: Text(
'Ringkasan Kursus',
style: thirdTextStyle.copyWith(
fontWeight: semiBold,
letterSpacing: 1,
fontSize: getProportionateScreenWidth(14),
),
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(
left: getProportionateScreenWidth(16),
right: getProportionateScreenWidth(16),
top: getProportionateScreenWidth(10),
),
child: Column(
children: [
if (widget.showSummary(selectedTitle) != "")
Text(
widget.showSummary(selectedTitle)!,
style: thirdTextStyle,
)
else
Center(
child: Text(
'Tidak Ada Ringkasan',
style: thirdTextStyle,
),
),
],
),
),
],
),
Container(
margin: EdgeInsets.only(
left: getProportionateScreenWidth(16),
right: getProportionateScreenWidth(16),
top: getProportionateScreenWidth(16),
),
child: Text(
'Pengumuman',
style: thirdTextStyle.copyWith(
fontWeight: semiBold,
letterSpacing: 1,
fontSize: getProportionateScreenWidth(14),
),
),
),
SizedBox(height: getProportionateScreenHeight(14)),
AnnouncementUserPage(idCourse: widget.id),
],
),
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}

View File

@ -0,0 +1,710 @@
import 'dart:convert';
import 'package:expandable/expandable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:initial_folder/models/detail_course_model.dart';
import 'package:initial_folder/providers/detail_course_provider.dart';
import 'package:initial_folder/providers/section_lesson_provider.dart';
import 'package:initial_folder/providers/theme_provider.dart';
import 'package:initial_folder/screens/detail_course/components/instruktur.dart';
import 'package:initial_folder/screens/detail_course/components/kursus_include_item.dart';
import 'package:provider/provider.dart';
import '../../../size_config.dart';
import '../../../theme.dart';
class DetailPlayCourse extends StatefulWidget {
const DetailPlayCourse({super.key});
@override
State<DetailPlayCourse> createState() => _DetailPlayCourseState();
}
class _DetailPlayCourseState extends State<DetailPlayCourse> {
bool isExpanded = false;
bool isExpanded2 = false;
@override
Widget build(BuildContext context) {
final Brightness brightnessValue =
MediaQuery.of(context).platformBrightness;
bool isDarkMode = brightnessValue == Brightness.dark;
SectionLessonProvider sectionLessonProvider =
Provider.of<SectionLessonProvider>(context);
final themeProvider = Provider.of<ThemeProvider>(context);
Widget kemampuanDiraih(String title) {
return Container(
margin: EdgeInsets.only(bottom: 10),
child: Row(
children: [
Icon(Icons.check,
size: getProportionateScreenHeight(13), color: themeProvider.themeData == ThemeClass.darkmode
?primaryColor : primaryColorligtmode,),
SizedBox(
width: 10,
),
Expanded(
child: Align(
alignment: Alignment.topLeft,
child: Text(
title,
style: thirdTextStyle.copyWith(
fontWeight: light,
color: Theme.of(context).colorScheme.onBackground,
fontSize: getProportionateScreenWidth(12),
),
),
),
),
],
),
);
}
Widget persyaratan(String title) {
return Container(
margin: EdgeInsets.only(bottom: 10),
child: Row(
children: [
title.isNotEmpty
? Icon(
Icons.brightness_1,
size: getProportionateScreenHeight(13),
color: themeProvider.themeData == ThemeClass.darkmode
?primaryColor : primaryColorligtmode,
)
: SizedBox.shrink(),
SizedBox(
width: 10,
),
Expanded(
child: Align(
alignment: Alignment.topLeft,
child: Text(
title,
style: thirdTextStyle.copyWith(
fontWeight: light,
color: Theme.of(context).colorScheme.onBackground,
fontSize: getProportionateScreenWidth(12),
),
),
),
),
],
),
);
}
return Consumer<DetailCourseProvider>(builder: (context, state, _) {
if (state.state == ResultState.Loading) {
return Center(
child: CircularProgressIndicator(
color: primaryColor,
strokeWidth: 2,
),
);
} else if (state.state == ResultState.HasData) {
var detailCourse = state.result!.data[0][0];
print(detailCourse.outcome);
print(detailCourse.requirement);
List requirement;
List outcomes = [];
if (detailCourse.outcome != '') {
outcomes = jsonDecode(detailCourse.outcome ?? 'gagal');
}
try {
requirement = jsonDecode(detailCourse.requirement ?? '[]');
} catch (e) {
requirement = [];
}
final bool hasDescription = detailCourse.description!.isNotEmpty;
return SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(left: 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: getProportionateScreenHeight(9)),
Container(
margin: EdgeInsets.only(
left: getProportionateScreenWidth(10),
top: getProportionateScreenHeight(10),
),
child: Text(
'Kursus Ini Sudah Termasuk',
style: thirdTextStyle.copyWith(
fontWeight: semiBold,
fontSize: getProportionateScreenWidth(14),
),
),
),
SizedBox(height: 10),
KursusIncludeItems(
svg: 'assets/icons/clock.svg',
text:
'${detailCourse.totalDuration} video pembelajaran'),
KursusIncludeItems(
svg: 'assets/icons/lesson.svg',
text: '${detailCourse.totalLesson} pelajaran'),
KursusIncludeItems(
svg: 'assets/icons/calendar.svg',
text: 'Akses full seumur hidup'),
KursusIncludeItems(
svg: 'assets/icons/phone.svg',
text: ' Akses di ponsel dan TV '),
],
),
),
SizedBox(
height: 5,
),
ExpandableNotifier(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: 12,
),
Container(
margin: EdgeInsets.only(
left: getProportionateScreenWidth(10),
right: getProportionateScreenWidth(106),
bottom: getProportionateScreenWidth(8),
),
child: Text('Kemampuan Yang Akan Diraih',
style: primaryTextStyle.copyWith(
fontWeight: semiBold,
letterSpacing: 1,
fontSize: getProportionateScreenWidth(14))),
),
Expandable(
collapsed: ExpandableButton(
child: Container(
margin: EdgeInsets.only(
left: getProportionateScreenWidth(10)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
outcomes.isEmpty
? Row(
children: [
Expanded(
child: Align(
alignment: Alignment.topLeft,
child: Text(
'',
),
),
),
],
)
: Column(children: [
...outcomes
.map((e) => kemampuanDiraih(e))
.take(2)
]),
if (outcomes.isNotEmpty && outcomes.length >= 3)
SizedBox(height: 8),
if (outcomes.isNotEmpty && outcomes.length >= 3)
Container(
child: Text(
"Tampilkan Lebih Banyak",
style: primaryTextStyle.copyWith(
fontWeight: semiBold,
color: Theme.of(context).brightness ==
Brightness.dark
? baruTextutih
: fourthColor,
fontSize:
getProportionateScreenWidth(12),
),
),
),
],
),
),
),
expanded: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ExpandableButton(
child: Container(
margin: EdgeInsets.only(
left: getProportionateScreenWidth(10)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
outcomes.isEmpty
? Row(
children: [
Expanded(
child: Align(
alignment: Alignment.topLeft,
child: Text(
'',
),
),
),
],
)
: Column(
children: outcomes
.map((e) => kemampuanDiraih(e))
.toList(),
),
if (outcomes.isNotEmpty &&
outcomes.length >= 3)
SizedBox(
height: 16,
),
if (outcomes.isNotEmpty &&
outcomes.length >= 3)
Container(
child: Text(
"Tampilkan Lebih Sedikit",
style: primaryTextStyle.copyWith(
fontWeight: semiBold,
color:
Theme.of(context).brightness ==
Brightness.dark
? baruTextutih
: fourthColor,
fontSize:
getProportionateScreenWidth(12),
),
),
)
],
),
),
)
],
),
),
],
),
),
ExpandableNotifier(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: 12,
),
Container(
margin: EdgeInsets.only(
left: getProportionateScreenWidth(10),
right: getProportionateScreenWidth(106),
bottom: getProportionateScreenWidth(8),
),
child: Text('Persyaratan',
style: primaryTextStyle.copyWith(
fontWeight: semiBold,
letterSpacing: 1,
fontSize: getProportionateScreenWidth(14))),
),
Expandable(
collapsed: ExpandableButton(
child: Container(
margin: EdgeInsets.only(
left: getProportionateScreenWidth(10)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
requirement.isEmpty
? Row(
children: [
Expanded(
child: Align(
alignment: Alignment.topLeft,
child: Text(
'',
),
),
),
],
)
: Column(children: [
...requirement
.map((e) => persyaratan(e))
.take(2)
]),
if (requirement.isNotEmpty &&
requirement.length >= 3)
SizedBox(height: 8),
if (requirement.isNotEmpty &&
requirement.length >= 3)
Container(
child: Text(
"Tampilkan Lebih Banyak",
style: primaryTextStyle.copyWith(
fontWeight: semiBold,
color: Theme.of(context).brightness ==
Brightness.dark
? baruTextutih
: fourthColor,
fontSize:
getProportionateScreenWidth(12),
),
),
),
],
),
),
),
expanded: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ExpandableButton(
child: Container(
margin: EdgeInsets.only(
left: getProportionateScreenWidth(10)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
requirement.isEmpty
? Row(
children: [
Expanded(
child: Align(
alignment: Alignment.topLeft,
child: Text(
'',
),
),
),
],
)
: Column(
children: requirement
.map((e) => persyaratan(e))
.toList(),
),
if (requirement.isNotEmpty &&
requirement.length >= 3)
SizedBox(
height: 16,
),
if (requirement.isNotEmpty &&
requirement.length >= 3)
Container(
child: Text(
"Tampilkan Lebih Sedikit",
style: primaryTextStyle.copyWith(
fontWeight: semiBold,
color:
Theme.of(context).brightness ==
Brightness.dark
? baruTextutih
: fourthColor,
fontSize:
getProportionateScreenWidth(12),
),
),
)
],
),
),
)
],
),
),
],
),
),
SizedBox(height: getProportionateScreenHeight(5)),
SizedBox(
height: 14,
),
Align(
alignment: Alignment.topLeft,
child: Container(
margin:
EdgeInsets.only(left: getProportionateScreenWidth(10)),
child: Text(
'Deskripsi',
style: primaryTextStyle.copyWith(
fontWeight: semiBold,
letterSpacing: 1,
fontSize: getProportionateScreenWidth(14),
),
),
),
),
AnimatedSize(
curve: Curves.fastOutSlowIn,
duration: const Duration(milliseconds: 300),
child: detailCourse.description!.isNotEmpty
? Container(
height: sectionLessonProvider.isExpanded ? 55 : null,
margin: EdgeInsets.symmetric(
horizontal: getProportionateScreenWidth(5)),
child: Html(
data: detailCourse.description!
.split('\n')
.take(2)
.join('\n'),
style: {
"body": Style(
fontSize:
FontSize(getProportionateScreenWidth(12)),
fontWeight: FontWeight.bold,
fontFamily: 'Poppins',
textAlign: TextAlign.justify,
color: Theme.of(context).brightness ==
Brightness.dark
? Colors.grey[100]
: Colors.grey[600],
),
},
),
)
: SizedBox(),
),
Padding(
padding:
EdgeInsets.only(left: getProportionateScreenWidth(3)),
child: detailCourse.description!.length > 140
? TextButton(
style: TextButton.styleFrom(
foregroundColor: primaryColor),
onPressed: () => sectionLessonProvider.expanded(),
child: sectionLessonProvider.isExpanded
? Text(
'Tampilkan Lebih Banyak',
style: primaryTextStyle.copyWith(
fontWeight: semiBold,
color: Theme.of(context).brightness ==
Brightness.dark
? baruTextutih
: fourthColor,
fontSize: getProportionateScreenWidth(12),
),
textAlign: TextAlign.left,
)
: Text(
'Tampilkan Lebih Sedikit',
style: primaryTextStyle.copyWith(
fontWeight: semiBold,
color: Theme.of(context).brightness ==
Brightness.dark
? baruTextutih
: fourthColor,
fontSize: getProportionateScreenWidth(12),
),
),
)
: SizedBox(),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (hasDescription &&
detailCourse.description!.length > 120)
SizedBox(height: getProportionateScreenHeight(10)),
Padding(
padding: EdgeInsets.symmetric(
horizontal: getProportionateScreenWidth(10)),
child: Row(
children: [
CircleAvatar(
radius: 20,
backgroundColor: primaryColor,
backgroundImage: detailCourse.fotoProfile == null
? AssetImage("assets/images/Profile Image.png")
: NetworkImage(detailCourse.fotoProfile!)
as ImageProvider,
),
SizedBox(width: getProportionateScreenWidth(10)),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
detailCourse.instructor ?? '',
style: thirdTextStyle.copyWith(
fontSize: getProportionateScreenWidth(12),
fontWeight: bold,
),
),
Row(
children: [
Text(
'Instructor, ',
style: thirdTextStyle.copyWith(
fontSize: getProportionateScreenWidth(10),
fontWeight: light,
),
),
Text(
'${detailCourse.totalStudents ?? '0'} Murid, ',
style: thirdTextStyle.copyWith(
fontSize: getProportionateScreenWidth(10),
fontWeight: light,
),
),
Text(
'${detailCourse.totalLesson ?? ''} Kursus',
style: thirdTextStyle.copyWith(
fontSize: getProportionateScreenWidth(10),
fontWeight: light,
),
)
],
),
],
),
],
),
),
// SizedBox(height: 2),
// widget.headline != null
// ? Container(
// margin: EdgeInsets.only(left: 10),
// child: Text(
// widget.headline!,
// style: TextStyle(fontSize: 13),
// ),
// )
// : const SizedBox.shrink(),
// Container(
// margin: EdgeInsets.only(left: 10),
// child: Row(
// children: [
// RatingBarIndicator(
// itemSize: 11,
// rating: double.parse(widget.rating ?? '0'),
// direction: Axis.horizontal,
// itemCount: 5,
// itemBuilder: (context, _) => const FaIcon(
// FontAwesomeIcons.solidStar,
// color: Colors.amber,
// ),
// ),
// SizedBox(width: 4),
// Text(
// double.parse(widget.rating ?? '0').toString(),
// style: TextStyle(fontSize: 10),
// ),
// SizedBox(width: 4),
// Text(
// '(${widget.review ?? '0'})',
// style: TextStyle(fontSize: 10),
// ),
// ],
// ),
// ),
if (detailCourse.bio == null || detailCourse.bio!.isEmpty)
Padding(
padding: EdgeInsets.only(
left: getProportionateScreenHeight(10),
right: getProportionateScreenHeight(10),
bottom: getProportionateScreenHeight(10),
),
child: const SizedBox(height: 10)
)
else
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(
left: getProportionateScreenWidth(5),
right: getProportionateScreenWidth(10),
),
child: isExpanded2
? Html(
data: detailCourse.bio,
style: {
"body": Style(
fontSize: FontSize(
getProportionateScreenWidth(12)),
fontWeight: light,
fontFamily: 'Poppins',
),
},
)
: Html(
data: detailCourse.bio != null &&
detailCourse.bio!.length > 100
? detailCourse.bio!.substring(0, 200)
: detailCourse.bio!,
style: {
"body": Style(
fontSize: FontSize(
getProportionateScreenWidth(12)),
fontWeight: reguler,
fontFamily: 'Poppins',
),
},
),
),
if (detailCourse.bio!.isNotEmpty &&
detailCourse.bio!.length > 100)
Padding(
padding: EdgeInsets.only(
left: getProportionateScreenWidth(12),
bottom: getProportionateScreenHeight(10),
),
child: GestureDetector(
onTap: () {
setState(() {
isExpanded2 = !isExpanded2;
print('asdasd');
});
},
child: Text(
isExpanded2
? 'Tampilkan Lebih Sedikit'
: 'Tampilkan Lebih Banyak',
style: thirdTextStyle.copyWith(
fontWeight: semiBold,
color: Theme.of(context).brightness ==
Brightness.dark
? baruTextutih
: fourthColor,
fontSize: getProportionateScreenWidth(12),
),
),
),
),
],
),
],
),
// Instruktur(
// id: detailCourse.instructorId ?? '1',
// bio: detailCourse.bio,
// instructor: detailCourse.instructor,
// rating: detailCourse.rating[0].avgRating.toString(),
// review: detailCourse.rating[0].totalReview ?? '',
// fotoProfile: detailCourse.fotoProfile,
// totalLesson: detailCourse.totalLesson,
// totalStudent: detailCourse.totalStudents,
// headline: detailCourse.headlineInstructor,
// ),
],
),
),
);
} else if (state.state == ResultState.Error) {
return Center(
child: Text('Terjadi Kesalahan'),
);
} else {
return Center(child: Text('error'));
}
});
}
}

View File

@ -0,0 +1,400 @@
import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:initial_folder/models/qna_model.dart';
import 'package:initial_folder/providers/like_or_unlike_provider.dart';
import 'package:initial_folder/providers/posting_qna_reply_provider.dart';
import 'package:initial_folder/providers/qna_provider.dart';
import 'package:initial_folder/size_config.dart';
import 'package:initial_folder/theme.dart';
import 'package:initial_folder/widgets/reply_qna_user_page.dart';
import 'package:provider/provider.dart';
import '../../../get_it.dart';
import '../../../models/comment_qna_model.dart';
// import '../../../widgets/qna_user.dart';
final scaffoldKey = GlobalKey<ScaffoldState>();
class DetailQuestAndAnswer extends StatefulWidget {
const DetailQuestAndAnswer({
Key? key,
required this.id,
required this.qnaDataModel,
required this.index,
required this.userId,
}) : super(key: key);
final QnaDataModel qnaDataModel;
final id;
final int index;
final int userId;
@override
State<DetailQuestAndAnswer> createState() => _DetailQuestAndAnswerState();
}
class _DetailQuestAndAnswerState extends State<DetailQuestAndAnswer> {
final _controller = TextEditingController();
final provider = qnaGetIt<QnaProvider>();
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
void onReplyDeleted(String idRep) {
setState(() {
// Temukan indeks balasan yang akan dihapus
final int indexToRemove = widget.qnaDataModel.comment.indexWhere((comment) => comment.idRep == idRep);
// Jika balasan ditemukan, hapus dan perbarui jumlah komentar
if (indexToRemove != -1) {
widget.qnaDataModel.comment.removeAt(indexToRemove);
widget.qnaDataModel.countComment = (widget.qnaDataModel.countComment ?? 1) - 1;
}
});
}
@override
Widget build(BuildContext context) {
PostingQnaReplyProvider postingQnaReplyProvider = Provider.of<PostingQnaReplyProvider>(context);
LikeOrUnlikeProvider _likeOrUnlikeProvider = Provider.of<LikeOrUnlikeProvider>(context);
likeOrUnlikes(int idQna) async {
final provider = qnaGetIt<QnaProvider>();
if (await _likeOrUnlikeProvider.likeOrUnlike(idQna)) {
provider.getQna(widget.id);
print("Respon Baik");
}
}
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text(
'Pertanyaan',
style: primaryTextStyle.copyWith(
fontWeight: semiBold,
fontSize: getProportionateScreenWidth(16),
letterSpacing: 0.2,
),
),
),
body: GestureDetector(
child: Stack(
children: [
ListView(
children: [
// Tampilan pertanyaan utama
Padding(
padding: EdgeInsets.all(getProportionateScreenWidth(0)),
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primaryContainer,
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(15),
bottomRight: Radius.circular(15),
),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 2,
blurRadius: 5,
offset: Offset(0, 3),
),
],
),
child: Padding(
padding: EdgeInsets.all(getProportionateScreenWidth(16)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
CircleAvatar(
backgroundColor: primaryColor,
backgroundImage: widget.qnaDataModel.fotoProfile == null
? AssetImage("assets/images/Profile Image.png")
: NetworkImage(widget.qnaDataModel.fotoProfile ?? '') as ImageProvider,
),
SizedBox(width: getProportionateScreenWidth(8)),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.qnaDataModel.username ?? '',
style: thirdTextStyle.copyWith(
fontSize: getProportionateScreenWidth(13),
color: Theme.of(context).colorScheme.onBackground,
),
),
Text(
widget.qnaDataModel.date ?? '',
style: primaryTextStyle.copyWith(
fontSize: getProportionateScreenWidth(12),
color: Theme.of(context).colorScheme.onBackground,
),
),
],
),
],
),
SizedBox(height: getProportionateScreenHeight(10)),
if (widget.qnaDataModel.title != '')
Text(
widget.qnaDataModel.title!,
style: secondaryTextStyle.copyWith(
fontSize: getProportionateScreenWidth(16),
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.onBackground,
),
),
SizedBox(height: getProportionateScreenHeight(10)),
Html(
data: widget.qnaDataModel.quest ?? 'Pertanyaan tidak tersedia',
style: {
"*": Style(margin: Margins.zero),
},
),
SizedBox(height: getProportionateScreenHeight(16)),
Row(
children: [
kLike(
widget.qnaDataModel.selfLiked ?? false,
int.parse(widget.qnaDataModel.countLike ?? '0'),
() async {
await likeOrUnlikes(int.parse(widget.qnaDataModel.idQna.toString()));
setState(() {
widget.qnaDataModel.selfLiked = !(widget.qnaDataModel.selfLiked ?? false);
widget.qnaDataModel.countLike = widget.qnaDataModel.selfLiked!
? (int.parse(widget.qnaDataModel.countLike ?? '0') + 1).toString()
: (int.parse(widget.qnaDataModel.countLike ?? '0') - 1).toString();
});
},
),
SizedBox(width: getProportionateScreenWidth(13)),
kComment(widget.qnaDataModel.comment.length),
],
),
],
),
),
),
),
SizedBox(height: getProportionateScreenHeight(10)),
// Divider(),
Padding(
padding: EdgeInsets.symmetric(
horizontal: getProportionateScreenWidth(16),
vertical: getProportionateScreenWidth(8),
),
child: Text(
'Balasan',
style: thirdTextStyle.copyWith(
color: Theme.of(context).colorScheme.onBackground,
fontWeight: semiBold,
fontSize: getProportionateScreenWidth(16),
letterSpacing: 1,
),
),
),
SizedBox(height: getProportionateScreenHeight(13)),
ReplyQnaUserPage(
idCourse: widget.id,
idQna: widget.qnaDataModel.idQna ?? '',
userId: widget.userId,
onReplyDeleted: onReplyDeleted,
),
SizedBox(height: getProportionateScreenHeight(65)),
],
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).brightness == Brightness.dark
? Color.fromARGB(255, 54, 61, 96)
: Color.fromARGB(255, 221, 221, 221),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
),
),
height: getProportionateScreenWidth(72),
width: double.infinity,
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: getProportionateScreenWidth(16),
vertical: getProportionateScreenWidth(16),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
width: SizeConfig.screenWidth * 0.65,
child: TextFormField(
scrollPhysics: AlwaysScrollableScrollPhysics(),
textAlignVertical: TextAlignVertical.center,
controller: _controller,
scrollPadding: EdgeInsets.zero,
cursorColor: secondaryColor,
maxLines: 1,
minLines: 1,
keyboardType: TextInputType.multiline,
decoration: InputDecoration(
filled: true,
fillColor: Theme.of(context).brightness == Brightness.dark
? seventeenColor
: Colors.grey[200],
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide.none,
),
hintStyle: secondaryTextStyle.copyWith(
color: secondaryColor,
letterSpacing: 0.5,
fontSize: getProportionateScreenWidth(12),
),
hintText: "Balas Pertanyaan",
),
),
),
ElevatedButton(
onPressed: () async {
if (_controller.text.trim().isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
duration: Duration(seconds: 2),
backgroundColor: Colors.orange,
content: Text(
'Ups, balasan masih kosong. isi dulu yuk!',
style: primaryTextStyle.copyWith(color: Colors.white),
),
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
);
} else {
bool isSuccessful = await postingQnaReplyProvider.postQnaReply(
_controller.text,
widget.qnaDataModel.idQna.toString(),
);
if (isSuccessful) {
setState(() {
// Tambahkan balasan baru ke dalam komentar
widget.qnaDataModel.comment.add(
Comment(
textRep: _controller.text,
username: '',
createAt: DateTime.now().toString(),
),
);
});
provider.getQna(widget.id);
_controller.clear();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
duration: Duration(seconds: 2),
backgroundColor: Colors.green,
content: Text(
'Balasan berhasil dikirim',
style: primaryTextStyle.copyWith(color: Colors.white),
),
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
duration: Duration(seconds: 2),
backgroundColor: Colors.red,
content: Text(
'Gagal mengirim balasan, silakan coba lagi',
style: primaryTextStyle.copyWith(color: Colors.white),
),
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
);
}
}
},
child: Text(
'Kirim',
style: thirdTextStyle.copyWith(
color: Colors.white,
letterSpacing: 0.3,
),
),
style: ElevatedButton.styleFrom(
backgroundColor: primaryColor,
minimumSize: Size(
getProportionateScreenWidth(35),
getProportionateScreenWidth(35),
),
),
)
],
),
),
),
),
],
),
),
);
}
}
Widget kLike(bool isLiked, int likeCount, Function onTap) {
return GestureDetector(
onTap: () => onTap(),
child: Row(
children: [
Icon(
isLiked ? Icons.favorite : Icons.favorite_border_rounded,
color: isLiked ? Colors.red : secondaryColor,
size: 25,
),
SizedBox(width: 5),
Text(
"$likeCount",
style: secondaryTextStyle.copyWith(
fontSize: getProportionateScreenWidth(12),
letterSpacing: 0.3,
),
),
],
),
);
}
Widget kComment(int? commentCount) {
return Row(
children: [
Icon(
FontAwesomeIcons.comment,
color: secondaryColor,
size: 25,
),
SizedBox(width: 5),
Text(
"${commentCount ?? 0}",
style: secondaryTextStyle.copyWith(
fontSize: getProportionateScreenWidth(12),
letterSpacing: 0.3,
),
),
],
);
}

View File

@ -0,0 +1,32 @@
import 'dart:js';
import 'package:flutter/material.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:initial_folder/screens/course/sertif.dart';
import 'package:initial_folder/providers/user_info_provider.dart';
import 'package:initial_folder/size_config.dart';
import 'package:initial_folder/theme.dart';
import 'package:provider/provider.dart';
import 'package:initial_folder/providers/certificate_provider.dart';
class DownloadCertificate extends StatelessWidget {
final int? idCourse;
const DownloadCertificate({Key? key, this.idCourse}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Column(
children: [
Row(
children: [
ElevatedButton(onPressed: () {}, child: Text('PNG')),
ElevatedButton(onPressed: () {}, child: Text('PDF'))
],
)
],
),
);
}
}

View File

@ -0,0 +1,487 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
import 'package:initial_folder/theme.dart';
const Duration _kExpand = Duration(milliseconds: 200);
/// A single-line [ListTile] with an expansion arrow icon that expands or collapses
/// the tile to reveal or hide the [children].
///
/// This widget is typically used with [ListView] to create an
/// "expand / collapse" list entry. When used with scrolling widgets like
/// [ListView], a unique [PageStorageKey] must be specified to enable the
/// [ExpansionTile] to save and restore its expanded state when it is scrolled
/// in and out of view.
///
/// This class overrides the [ListTileThemeData.iconColor] and [ListTileThemeData.textColor]
/// theme properties for its [ListTile]. These colors animate between values when
/// the tile is expanded and collapsed: between [iconColor], [collapsedIconColor] and
/// between [textColor] and [collapsedTextColor].
///
/// The expansion arrow icon is shown on the right by default in left-to-right languages
/// (i.e. the trailing edge). This can be changed using [controlAffinity]. This maps
/// to the [leading] and [trailing] properties of [ExpansionTile].
///
/// {@tool dartpad}
/// This example demonstrates different configurations of ExpansionTile.
///
/// ** See code in examples/api/lib/material/expansion_tile/expansion_tile.0.dart **
/// {@end-tool}
///
/// See also:
///
/// * [ListTile], useful for creating expansion tile [children] when the
/// expansion tile represents a sublist.
/// * The "Expand and collapse" section of
/// <https://material.io/components/lists#types>
class ExpansionTileCopy extends StatefulWidget {
/// Creates a single-line [ListTile] with an expansion arrow icon that expands or collapses
/// the tile to reveal or hide the [children]. The [initiallyExpanded] property must
/// be non-null.
const ExpansionTileCopy({
Key? key,
this.leading,
required this.title,
this.subtitle,
this.onExpansionChanged,
this.children = const <Widget>[],
this.trailing,
this.initiallyExpanded = false,
this.maintainState = false,
this.tilePadding,
this.expandedCrossAxisAlignment,
this.expandedAlignment,
this.childrenPadding,
this.backgroundColor,
this.collapsedBackgroundColor,
this.textColor,
this.collapsedTextColor,
this.iconColor,
this.collapsedIconColor,
this.controlAffinity,
}) : assert(
expandedCrossAxisAlignment != CrossAxisAlignment.baseline,
'CrossAxisAlignment.baseline is not supported since the expanded children '
'are aligned in a column, not a row. Try to use another constant.',
),
super(key: key);
/// A widget to display before the title.
///
/// Typically a [CircleAvatar] widget.
///
/// Note that depending on the value of [controlAffinity], the [leading] widget
/// may replace the rotating expansion arrow icon.
final Widget? leading;
/// The primary content of the list item.
///
/// Typically a [Text] widget.
final Widget title;
/// Additional content displayed below the title.
///
/// Typically a [Text] widget.
final Widget? subtitle;
/// Called when the tile expands or collapses.
///
/// When the tile starts expanding, this function is called with the value
/// true. When the tile starts collapsing, this function is called with
/// the value false.
final ValueChanged<bool>? onExpansionChanged;
/// The widgets that are displayed when the tile expands.
///
/// Typically [ListTile] widgets.
final List<Widget> children;
/// The color to display behind the sublist when expanded.
///
/// If this property is null then [ExpansionTileThemeData.backgroundColor] is used. If that
/// is also null then Colors.transparent is used.
///
/// See also:
///
/// * [ExpansionTileTheme.of], which returns the nearest [ExpansionTileTheme]'s
/// [ExpansionTileThemeData].
final Color? backgroundColor;
/// When not null, defines the background color of tile when the sublist is collapsed.
///
/// If this property is null then [ExpansionTileThemeData.collapsedBackgroundColor] is used.
/// If that is also null then Colors.transparent is used.
///
/// See also:
///
/// * [ExpansionTileTheme.of], which returns the nearest [ExpansionTileTheme]'s
/// [ExpansionTileThemeData].
final Color? collapsedBackgroundColor;
/// A widget to display after the title.
///
/// Note that depending on the value of [controlAffinity], the [trailing] widget
/// may replace the rotating expansion arrow icon.
final Widget? trailing;
/// Specifies if the list tile is initially expanded (true) or collapsed (false, the default).
final bool initiallyExpanded;
/// Specifies whether the state of the children is maintained when the tile expands and collapses.
///
/// When true, the children are kept in the tree while the tile is collapsed.
/// When false (default), the children are removed from the tree when the tile is
/// collapsed and recreated upon expansion.
final bool maintainState;
/// Specifies padding for the [ListTile].
///
/// Analogous to [ListTile.contentPadding], this property defines the insets for
/// the [leading], [title], [subtitle] and [trailing] widgets. It does not inset
/// the expanded [children] widgets.
///
/// If this property is null then [ExpansionTileThemeData.tilePadding] is used. If that
/// is also null then the tile's padding is `EdgeInsets.symmetric(horizontal: 16.0)`.
///
/// See also:
///
/// * [ExpansionTileTheme.of], which returns the nearest [ExpansionTileTheme]'s
/// [ExpansionTileThemeData].
final EdgeInsetsGeometry? tilePadding;
/// Specifies the alignment of [children], which are arranged in a column when
/// the tile is expanded.
///
/// The internals of the expanded tile make use of a [Column] widget for
/// [children], and [Align] widget to align the column. The `expandedAlignment`
/// parameter is passed directly into the [Align].
///
/// Modifying this property controls the alignment of the column within the
/// expanded tile, not the alignment of [children] widgets within the column.
/// To align each child within [children], see [expandedCrossAxisAlignment].
///
/// The width of the column is the width of the widest child widget in [children].
///
/// If this property is null then [ExpansionTileThemeData.expandedAlignment]is used. If that
/// is also null then the value of `expandedAlignment` is [Alignment.center].
///
/// See also:
///
/// * [ExpansionTileTheme.of], which returns the nearest [ExpansionTileTheme]'s
/// [ExpansionTileThemeData].
final Alignment? expandedAlignment;
/// Specifies the alignment of each child within [children] when the tile is expanded.
///
/// The internals of the expanded tile make use of a [Column] widget for
/// [children], and the `crossAxisAlignment` parameter is passed directly into the [Column].
///
/// Modifying this property controls the cross axis alignment of each child
/// within its [Column]. Note that the width of the [Column] that houses
/// [children] will be the same as the widest child widget in [children]. It is
/// not necessarily the width of [Column] is equal to the width of expanded tile.
///
/// To align the [Column] along the expanded tile, use the [expandedAlignment] property
/// instead.
///
/// When the value is null, the value of `expandedCrossAxisAlignment` is [CrossAxisAlignment.center].
final CrossAxisAlignment? expandedCrossAxisAlignment;
/// Specifies padding for [children].
///
/// If this property is null then [ExpansionTileThemeData.childrenPadding] is used. If that
/// is also null then the value of `childrenPadding` is [EdgeInsets.zero].
///
/// See also:
///
/// * [ExpansionTileTheme.of], which returns the nearest [ExpansionTileTheme]'s
/// [ExpansionTileThemeData].
final EdgeInsetsGeometry? childrenPadding;
/// The icon color of tile's expansion arrow icon when the sublist is expanded.
///
/// Used to override to the [ListTileThemeData.iconColor].
///
/// If this property is null then [ExpansionTileThemeData.iconColor] is used. If that
/// is also null then the value of [ListTileThemeData.iconColor] is used.
///
/// See also:
///
/// * [ExpansionTileTheme.of], which returns the nearest [ExpansionTileTheme]'s
/// [ExpansionTileThemeData].
final Color? iconColor;
/// The icon color of tile's expansion arrow icon when the sublist is collapsed.
///
/// Used to override to the [ListTileThemeData.iconColor].
final Color? collapsedIconColor;
/// The color of the tile's titles when the sublist is expanded.
///
/// Used to override to the [ListTileThemeData.textColor].
///
/// If this property is null then [ExpansionTileThemeData.textColor] is used. If that
/// is also null then the value of [ListTileThemeData.textColor] is used.
///
/// See also:
///
/// * [ExpansionTileTheme.of], which returns the nearest [ExpansionTileTheme]'s
/// [ExpansionTileThemeData].
final Color? textColor;
/// The color of the tile's titles when the sublist is collapsed.
///
/// Used to override to the [ListTileThemeData.textColor].
///
/// If this property is null then [ExpansionTileThemeData.collapsedTextColor] is used. If that
/// is also null then the value of [ListTileThemeData.textColor] is used.
///
/// See also:
///
/// * [ExpansionTileTheme.of], which returns the nearest [ExpansionTileTheme]'s
/// [ExpansionTileThemeData].
final Color? collapsedTextColor;
/// Typically used to force the expansion arrow icon to the tile's leading or trailing edge.
///
/// By default, the value of `controlAffinity` is [ListTileControlAffinity.platform],
/// which means that the expansion arrow icon will appear on the tile's trailing edge.
final ListTileControlAffinity? controlAffinity;
@override
State<ExpansionTileCopy> createState() => _ExpansionTileCopyState();
}
class _ExpansionTileCopyState extends State<ExpansionTileCopy>
with SingleTickerProviderStateMixin {
static final Animatable<double> _easeOutTween =
CurveTween(curve: Curves.easeOut);
static final Animatable<double> _easeInTween =
CurveTween(curve: Curves.easeIn);
static final Animatable<double> _halfTween =
Tween<double>(begin: 0.0, end: 0.5);
final ColorTween _borderColorTween = ColorTween();
final ColorTween _headerColorTween = ColorTween();
final ColorTween _iconColorTween = ColorTween();
final ColorTween _backgroundColorTween = ColorTween();
late AnimationController _controller;
late Animation<double> _iconTurns;
late Animation<double> _heightFactor;
late Animation<Color?> _borderColor;
late Animation<Color?> _headerColor;
late Animation<Color?> _iconColor;
late Animation<Color?> _backgroundColor;
bool _isExpanded = false;
@override
void initState() {
super.initState();
_controller = AnimationController(duration: _kExpand, vsync: this);
_heightFactor = _controller.drive(_easeInTween);
_iconTurns = _controller.drive(_halfTween.chain(_easeInTween));
_borderColor = _controller.drive(_borderColorTween.chain(_easeOutTween));
_headerColor = _controller.drive(_headerColorTween.chain(_easeInTween));
_iconColor = _controller.drive(_iconColorTween.chain(_easeInTween));
_backgroundColor =
_controller.drive(_backgroundColorTween.chain(_easeOutTween));
_isExpanded = PageStorage.of(context)?.readState(context) as bool? ??
widget.initiallyExpanded;
if (_isExpanded) {
_controller.value = 1.0;
}
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
void _handleTap() {
setState(() {
_isExpanded = !_isExpanded;
if (_isExpanded) {
_controller.forward();
} else {
_controller.reverse().then<void>((void value) {
if (!mounted) {
return;
}
setState(() {
// Rebuild without widget.children.
});
});
}
PageStorage.of(context)?.writeState(context, _isExpanded);
});
widget.onExpansionChanged?.call(_isExpanded);
}
// Added to class
void closeExpansion() {
if (_isExpanded) _handleTap();
}
// Added to class
void openExpansion() {
if (!_isExpanded) _handleTap();
}
// Platform or null affinity defaults to trailing.
ListTileControlAffinity _effectiveAffinity(
ListTileControlAffinity? affinity) {
switch (affinity ?? ListTileControlAffinity.trailing) {
case ListTileControlAffinity.leading:
return ListTileControlAffinity.leading;
case ListTileControlAffinity.trailing:
case ListTileControlAffinity.platform:
return ListTileControlAffinity.trailing;
}
}
Widget? _buildIcon(BuildContext context) {
return RotationTransition(
turns: _iconTurns,
child: const Icon(Icons.expand_more),
);
}
Widget? _buildLeadingIcon(BuildContext context) {
if (_effectiveAffinity(widget.controlAffinity) !=
ListTileControlAffinity.leading) {
return null;
}
return _buildIcon(context);
}
// Ubah trailing menjadi "+" dan "-" saat ExpansionTileCopy dibuka
Widget? _buildTrailingIcon(BuildContext context) {
if (_effectiveAffinity(widget.controlAffinity) !=
ListTileControlAffinity.trailing) {
return null;
}
final Color trailingIconColor = Theme.of(context).brightness == Brightness.light
? primaryColor
: primaryColorligtmode;
return _isExpanded
? Icon(
Icons.remove,
color: trailingIconColor,
)
: Icon(
Icons.add,
color: trailingIconColor,
);
}
Widget _buildChildren(BuildContext context, Widget? child) {
final ExpansionTileThemeData expansionTileTheme =
ExpansionTileTheme.of(context);
final Color borderSideColor = _borderColor.value ?? Colors.transparent;
return Container(
decoration: BoxDecoration(
color: _backgroundColor.value ??
expansionTileTheme.backgroundColor ??
Colors.transparent,
border: Border(
top: BorderSide(color: borderSideColor),
bottom: BorderSide(color: borderSideColor),
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTileTheme.merge(
iconColor: _iconColor.value ?? expansionTileTheme.iconColor,
textColor: _headerColor.value,
child: ListTile(
onTap: _handleTap,
contentPadding:
widget.tilePadding ?? expansionTileTheme.tilePadding,
leading: widget.leading ?? _buildLeadingIcon(context),
title: widget.title,
subtitle: widget.subtitle,
trailing: widget.trailing ?? _buildTrailingIcon(context),
),
),
ClipRect(
child: Align(
alignment: widget.expandedAlignment ??
expansionTileTheme.expandedAlignment ??
Alignment.center,
heightFactor: _heightFactor.value,
child: child,
),
),
],
),
);
}
@override
void didChangeDependencies() {
final ThemeData theme = Theme.of(context);
final ExpansionTileThemeData expansionTileTheme =
ExpansionTileTheme.of(context);
final ColorScheme colorScheme = theme.colorScheme;
_borderColorTween.end = theme.dividerColor;
_headerColorTween
..begin = widget.collapsedTextColor ??
expansionTileTheme.collapsedTextColor ??
theme.textTheme.subtitle1!.color
..end = widget.textColor ??
expansionTileTheme.textColor ??
colorScheme.primary;
_iconColorTween
..begin = widget.collapsedIconColor ??
expansionTileTheme.collapsedIconColor ??
theme.unselectedWidgetColor
..end = widget.iconColor ??
expansionTileTheme.iconColor ??
colorScheme.primary;
_backgroundColorTween
..begin = widget.collapsedBackgroundColor ??
expansionTileTheme.collapsedBackgroundColor
..end = widget.backgroundColor ?? expansionTileTheme.backgroundColor;
super.didChangeDependencies();
}
@override
Widget build(BuildContext context) {
final ExpansionTileThemeData expansionTileTheme =
ExpansionTileTheme.of(context);
final bool closed = !_isExpanded && _controller.isDismissed;
final bool shouldRemoveChildren = closed && !widget.maintainState;
final Widget result = Offstage(
offstage: closed,
child: TickerMode(
enabled: !closed,
child: Padding(
padding: widget.childrenPadding ??
expansionTileTheme.childrenPadding ??
EdgeInsets.zero,
child: Column(
crossAxisAlignment:
widget.expandedCrossAxisAlignment ?? CrossAxisAlignment.center,
children: widget.children,
),
),
),
);
return AnimatedBuilder(
animation: _controller.view,
builder: _buildChildren,
child: shouldRemoveChildren ? null : result,
);
}
}

View File

@ -0,0 +1,308 @@
import 'package:flutter/material.dart';
import 'package:initial_folder/models/announcement_model.dart';
import 'package:initial_folder/providers/announcement_provider.dart';
import 'package:initial_folder/providers/posting_announcement_reply_provider.dart';
import 'package:initial_folder/size_config.dart';
import 'package:initial_folder/theme.dart';
import 'package:initial_folder/widgets/announcement_user.dart';
import 'package:initial_folder/widgets/reply_announcement_user_page.dart';
import 'package:provider/provider.dart';
import '../../../get_it.dart';
final scaffoldKey = GlobalKey<ScaffoldState>();
class InsideAnnouncement extends StatefulWidget {
const InsideAnnouncement({
Key? key,
required this.id,
required this.announcementDataModel,
required this.index,
required this.userId,
}) : super(key: key);
final AnnouncementDataModel announcementDataModel;
final id;
final int index;
final int userId;
@override
State<InsideAnnouncement> createState() => _InsideAnnouncementState();
}
class _InsideAnnouncementState extends State<InsideAnnouncement> {
final _controller = TextEditingController();
final provider = announcementGetIt<AnnouncementProvider>();
late Widget announcement;
@override
Widget build(BuildContext context) {
PostingAnnouncementReplyProvider postingAnnouncementReplyProvider =
Provider.of<PostingAnnouncementReplyProvider>(context);
return Scaffold(
key: scaffoldKey,
appBar: AppBar(
title: Text(
'Pengumuman',
style: primaryTextStyle.copyWith(
fontWeight: semiBold,
fontSize: getProportionateScreenWidth(16),
letterSpacing: 0.2,
),
),
),
body: GestureDetector(
// onTap: () => FocusScope.of(context).unfocus(),
child: Stack(
children: [
ListView(
children: [
// QnaUser(
// qnaDataModel: widget.qnaDataModel,
// id: widget.id,
// index: widget.index,
// userId: widget.userId,
// ),
StreamBuilder<AnnouncementModel>(
stream: provider.announcementStream,
builder:
(context, AsyncSnapshot<AnnouncementModel> snapshot) {
if (snapshot.hasError) {
return Center(
child: Text(
'Terjadi Kesalahan',
style: thirdTextStyle,
),
);
} else {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
announcement = Center(
child: CircularProgressIndicator(
color: primaryColor,
strokeWidth: 2,
),
);
break;
case ConnectionState.none:
announcement = Center(
child: Text(
'Tidak ada koneksi',
style: thirdTextStyle,
),
);
break;
case ConnectionState.active:
print('masuk siniiiiiiiiiii active' +
snapshot.data!.error.toString());
announcement = snapshot.data!.data[0].length > 0
? Container(
padding: EdgeInsets.only(top: 12),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomRight: Radius.circular(10),
bottomLeft: Radius.circular(10)),
color: Theme.of(context)
.colorScheme
.primaryContainer,
boxShadow: [
BoxShadow(
color: Theme.of(context)
.brightness ==
Brightness.dark
? Color(0xff212643)
: Colors.grey,
blurRadius: 0.5,
offset: Offset(0, 2),
spreadRadius: 0.001)
]),
child: AnnouncementUser(
announcementDataModel:
snapshot.data!.data[0][widget.index],
id: widget.id,
index: widget.index,
userId: widget.userId,
))
: Center(
child: Text(
'Belum ada pengumuman',
style: thirdTextStyle,
),
);
break;
case ConnectionState.done:
announcement = snapshot.data!.data[0].length > 0
? AnnouncementUser(
announcementDataModel:
snapshot.data!.data[0][widget.index],
id: widget.id,
index: widget.index,
userId: widget.userId,
)
: Center(
child: Text(
'Belum ada pertanyaan',
style: thirdTextStyle,
),
);
break;
}
}
return announcement;
}),
Padding(
padding: EdgeInsets.symmetric(
horizontal: getProportionateScreenWidth(16),
vertical: getProportionateScreenWidth(8)),
child: InkWell(
onTap: () {},
child: Text(
'Balasan',
style: thirdTextStyle.copyWith(
color: Theme.of(context).colorScheme.onBackground,
fontWeight: semiBold,
fontSize: getProportionateScreenWidth(16),
letterSpacing: 1),
),
),
),
SizedBox(height: getProportionateScreenHeight(13)),
ReplyAnnouncementUserPage(
idCourse: widget.id,
index: widget.index,
userId: widget.userId,
),
SizedBox(height: 50)
],
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).brightness == Brightness.dark
? Color.fromARGB(255, 54, 61, 96)
: Color.fromARGB(255, 221, 221, 221),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20))),
height: getProportionateScreenWidth(72),
width: double.infinity,
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: getProportionateScreenWidth(16),
vertical: getProportionateScreenWidth(16),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
width: SizeConfig.screenWidth * 0.65,
child: TextFormField(
controller: _controller,
scrollPadding: EdgeInsets.zero,
cursorColor: secondaryColor,
decoration: InputDecoration(
filled: true,
fillColor:
Theme.of(context).brightness == Brightness.dark
? seventeenColor
: Colors.grey[200],
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(
10,
),
borderSide: BorderSide.none),
hintStyle: secondaryTextStyle.copyWith(
color: secondaryColor,
letterSpacing: 0.5,
fontSize: getProportionateScreenWidth(12),
),
hintText: "Balas",
),
),
),
ElevatedButton(
onPressed: () async {
if (await postingAnnouncementReplyProvider
.postAnnouncementReply(
_controller.text,
widget.announcementDataModel.idAnnouncement
.toString(),
widget.announcementDataModel.tokenAnnouncement
.toString(),
widget.announcementDataModel.idAnnouncement
.toString(),
)) {
provider.getAnnouncement(widget.id);
_controller.clear();
ScaffoldMessenger.of(scaffoldKey.currentContext!)
.showSnackBar(
SnackBar(
duration: Duration(seconds: 2),
backgroundColor: primaryColor,
content: Text(
'Balasan Terkirim',
style: primaryTextStyle.copyWith(
color: backgroundColor),
),
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5),
),
action: SnackBarAction(
label: 'Lihat',
onPressed: () {
ScaffoldMessenger.of(
scaffoldKey.currentContext!)
.hideCurrentSnackBar();
},
),
),
);
} else {
ScaffoldMessenger.of(scaffoldKey.currentContext!)
.showSnackBar(
SnackBar(
duration: Duration(seconds: 2),
backgroundColor: primaryColor,
content: Text(
'Terjadi kesalahan',
style: primaryTextStyle.copyWith(
color: backgroundColor,
),
),
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5),
),
),
);
}
},
child: Text(
'Kirim',
style: thirdTextStyle.copyWith(
color: Colors.white,
letterSpacing: 0.3,
),
),
style: ElevatedButton.styleFrom(
backgroundColor: primaryColor,
minimumSize: Size(
getProportionateScreenWidth(35),
getProportionateScreenWidth(35),
),
),
)
],
),
),
),
),
],
),
),
);
}
}

View File

@ -0,0 +1,53 @@
import 'package:flutter/material.dart';
import 'package:easy_pdf_viewer/easy_pdf_viewer.dart';
import 'package:initial_folder/size_config.dart';
import 'package:initial_folder/theme.dart';
class pdfReader extends StatefulWidget {
final String link;
final String title;
const pdfReader({super.key, required this.link, required this.title});
@override
State<pdfReader> createState() => _pdfReaderState();
}
class _pdfReaderState extends State<pdfReader> {
late PDFDocument document;
bool _isLoading = true;
@override
void initState() {
super.initState();
// Load from URL
loadPDF();
}
void loadPDF() async {
document = await PDFDocument.fromURL(widget.link);
setState(() {
_isLoading = false;
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text(
widget.title,
style: secondaryTextStyle.copyWith(
letterSpacing: 1,
fontWeight: semiBold,
fontSize: getProportionateScreenWidth(16)),
),
),
body: Center(
child: _isLoading
? Center(child: CircularProgressIndicator())
: PDFViewer(
document: document,
pickerButtonColor: primaryColor,
)),
);
}
}

View File

@ -0,0 +1,338 @@
import 'package:flutter/material.dart';
import 'package:initial_folder/main.dart';
import 'package:initial_folder/providers/qna_provider.dart';
import 'package:initial_folder/services/qna_service.dart';
import 'package:initial_folder/size_config.dart';
import 'package:initial_folder/theme.dart';
import 'package:initial_folder/providers/posting_qna_provider.dart';
import 'package:initial_folder/widgets/qna_user_page.dart';
import 'package:provider/provider.dart';
import 'package:quill_html_editor/quill_html_editor.dart';
class QuestAndAnswer extends StatefulWidget {
const QuestAndAnswer({
Key? key,
required this.id,
required this.idLesson,
}) : super(key: key);
final id;
final idLesson;
@override
State<QuestAndAnswer> createState() => _QuestAndAnswerState();
}
class _QuestAndAnswerState extends State<QuestAndAnswer> {
double value = 0;
final _controllerTitle = TextEditingController();
final _controller = TextEditingController();
final QuillEditorController _controllerQuest = QuillEditorController();
final customToolbar = [
ToolBarStyle.headerOne,
ToolBarStyle.headerTwo,
ToolBarStyle.bold,
ToolBarStyle.italic,
ToolBarStyle.underline,
ToolBarStyle.color,
ToolBarStyle.listBullet,
ToolBarStyle.listOrdered,
];
@override
void initState() {
// TODO: implement initState
QnaService().getMyQna(widget.id);
super.initState();
}
@override
Widget build(BuildContext context) {
PostingQnaProvider postingQnaProvider =
Provider.of<PostingQnaProvider>(context);
return SingleChildScrollView(
child: GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: EdgeInsets.symmetric(
horizontal: getProportionateScreenWidth(16),
),
child: Column(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: getProportionateScreenWidth(16),
),
Text(
'Ajukan Pertanyaan',
style: thirdTextStyle.copyWith(
fontWeight: semiBold,
letterSpacing: 0.1,
fontSize: getProportionateScreenWidth(16),
),
),
SizedBox(
height: getProportionateScreenWidth(8),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color:
Theme.of(context).colorScheme.primaryContainer,
boxShadow: [
BoxShadow(
color: Colors.grey,
blurRadius: 0.5,
offset: Offset(0, 2),
spreadRadius: 0.001)
]),
child: TextField(
controller: _controllerTitle,
cursorColor: secondaryColor,
scrollPadding: EdgeInsets.zero,
decoration: InputDecoration(
filled: true,
fillColor:
Theme.of(context).brightness == Brightness.dark
? seventeenColor
: Colors.grey[200],
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(
10,
),
borderSide: BorderSide.none),
hintStyle: secondaryTextStyle.copyWith(
color: secondaryColor,
letterSpacing: 0.5,
fontSize: getProportionateScreenWidth(12),
),
hintText: "Masukan Judul Pertanyaan",
),
),
),
SizedBox(
height: getProportionateScreenWidth(18),
),
ToolBar(
toolBarColor:
Theme.of(context).brightness == Brightness.dark
? seventeenColor
: Colors.grey[200]!,
activeIconColor: primaryColor,
iconColor: secondaryColor,
padding: const EdgeInsets.all(8),
iconSize: 20,
controller: _controllerQuest,
toolBarConfig: customToolbar,
),
Container(
height: 180,
width: double.infinity,
padding: EdgeInsets.all(15),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(10),
bottomRight: Radius.circular(10),
),
color:
Theme.of(context).colorScheme.primaryContainer,
boxShadow: [
BoxShadow(
color: Colors.grey,
blurRadius: 0.5,
offset: Offset(0, 2),
spreadRadius: 0.001)
]),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: QuillHtmlEditor(
hintText: 'Ketik pertanyaan mu',
controller: _controllerQuest,
isEnabled: true,
minHeight: 100,
backgroundColor: Theme.of(context).brightness ==
Brightness.dark
? seventeenColor
: Colors.grey[200]!,
textStyle: secondaryTextStyle.copyWith(
color: secondaryColor,
fontSize: getProportionateScreenWidth(16),
),
hintTextStyle: secondaryTextStyle.copyWith(
color: secondaryColor,
fontSize: getProportionateScreenWidth(16),
),
hintTextAlign: TextAlign.start,
padding: const EdgeInsets.all(3),
hintTextPadding: const EdgeInsets.all(0),
loadingBuilder: (context) {
return const Center(
child: CircularProgressIndicator(
strokeWidth: 0.4,
));
},
),
),
// TextField(
// controller: _controller,
// cursorColor: secondaryColor,
// scrollPadding: EdgeInsets.zero,
// minLines: 2,
// keyboardType: TextInputType.multiline,
// maxLines: 2,
// decoration: InputDecoration(
// filled: true,
// fillColor: Theme.of(context).brightness ==
// Brightness.dark
// ? seventeenColor
// : Colors.grey[200],
// border: OutlineInputBorder(
// borderRadius: BorderRadius.circular(
// 10,
// ),
// borderSide: BorderSide.none),
// hintStyle: secondaryTextStyle.copyWith(
// color: secondaryColor,
// letterSpacing: 0.5,
// fontSize: getProportionateScreenWidth(12),
// ),
// hintText: "Ketikkan Pertanyaanmu disini",
// ),
// ),
Align(
alignment: Alignment.topRight,
child: ElevatedButton(
onPressed: () async {
if (await postingQnaProvider.postingQna(
_controllerTitle.text,
await _controllerQuest.getText(),
widget.id,
widget.idLesson)) {
_controllerQuest.clear();
_controllerTitle.clear();
ScaffoldMessenger.of(
globalScaffoldKey.currentContext!)
.showSnackBar(
SnackBar(
duration: Duration(seconds: 2),
backgroundColor: primaryColor,
content: Text(
'Pertanyaan berhasil dikirimkan',
style: primaryTextStyle.copyWith(
color: backgroundColor,
),
),
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(5),
),
action: SnackBarAction(
label: 'Lihat',
onPressed: () {
ScaffoldMessenger.of(
globalScaffoldKey
.currentContext!)
.hideCurrentSnackBar();
},
),
),
);
} else {
ScaffoldMessenger.of(
globalScaffoldKey.currentContext!)
.showSnackBar(
SnackBar(
duration: Duration(seconds: 2),
backgroundColor: primaryColor,
content: Text(
'Terjadi kesalahan',
style: primaryTextStyle.copyWith(
color: backgroundColor,
),
),
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(5),
),
),
);
}
},
child: Text(
'Kirim',
style: thirdTextStyle.copyWith(
color: Colors.white,
fontSize: SizeConfig.blockHorizontal! * 4,
),
),
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
backgroundColor: primaryColor,
),
),
),
],
)),
),
],
),
],
),
),
SizedBox(
height: 15,
),
Container(
margin: EdgeInsets.symmetric(
horizontal: getProportionateScreenWidth(16),
),
child: Text(
'Pertanyaan Dan Jawaban',
style: primaryTextStyle.copyWith(
fontWeight: semiBold,
letterSpacing: 1,
fontSize: getProportionateScreenWidth(16),
),
),
),
SizedBox(
height: getProportionateScreenHeight(14),
),
QnaUserPage(idCourse: widget.id),
SizedBox(
height: getProportionateScreenHeight(14),
),
// QandA(
// divider: Divider(),
// ),
// QandA(
// divider: Divider(),
// ),
],
),
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}

View File

@ -0,0 +1,46 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:easy_pdf_viewer/easy_pdf_viewer.dart';
import 'package:flutter_text_viewer/model/text_viewer.dart';
import 'package:flutter_text_viewer/screen/text_viewer_page.dart';
import 'package:initial_folder/size_config.dart';
import 'package:initial_folder/theme.dart';
class txtReader extends StatefulWidget {
final String link;
// final String title;
const txtReader({Key? key, required this.link});
@override
State<txtReader> createState() => _txtReaderState();
}
class _txtReaderState extends State<txtReader> {
@override
void initState() {
super.initState();
print(widget.link);
}
@override
void dispose() {
super.dispose();
// Hapus file .txt saat widget di-dispose
File(widget.link).deleteSync(recursive: true);
}
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: TextViewerPage(
textViewer: TextViewer.asset(
widget.link,
highLightColor: Colors.yellow,
focusColor: Colors.orange,
ignoreCase: true,
),
showSearchAppBar: true,
)));
}
}

View File

@ -0,0 +1,79 @@
import 'package:flutter/material.dart';
import 'package:chewie/chewie.dart';
import 'package:video_player/video_player.dart';
class Html5Vid extends StatefulWidget {
const Html5Vid({Key? key, required this.link, required this.title});
final String link;
final String title;
@override
State<Html5Vid> createState() => Html5VidState();
}
class Html5VidState extends State<Html5Vid> {
late VideoPlayerController _videoPlayerController;
late ChewieController _chewieController;
bool _isVideoLoading = true;
@override
void initState() {
super.initState();
print(widget.link);
print(widget.title);
_videoPlayerController = VideoPlayerController.network('${widget.link}');
_chewieController = ChewieController(
videoPlayerController: _videoPlayerController,
aspectRatio: 16 / 9,
autoPlay: true,
looping: false,
);
_videoPlayerController.addListener(() {
if (_videoPlayerController.value.isInitialized &&
!_videoPlayerController.value.isBuffering) {
setState(() {
_isVideoLoading = false;
});
}
});
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: _onWillPop,
child: Scaffold(
appBar: AppBar(
title: Text('${widget.title}'),
),
body: Stack(
children: [
Center(
child: Chewie(
controller: _chewieController,
),
),
if (_isVideoLoading)
Center(
child: CircularProgressIndicator(),
),
],
),
),
);
}
Future<bool> _onWillPop() async {
await _videoPlayerController.pause();
await _videoPlayerController.dispose();
// await _chewieController.dispose();
return true;
}
@override
void dispose() {
_videoPlayerController.dispose();
_chewieController.dispose();
super.dispose();
}
}

View File

@ -0,0 +1,198 @@
import 'package:flutter/material.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:initial_folder/providers/my_course_provider.dart';
import 'package:initial_folder/providers/theme_provider.dart';
import 'package:initial_folder/screens/course/search_my_course_page.dart';
import 'package:initial_folder/screens/home/components/body_comp/latest_course.dart';
import 'package:initial_folder/screens/home/components/body_comp/populer_course.dart';
import 'package:initial_folder/screens/splash/splash_screen_login.dart';
import 'package:initial_folder/size_config.dart';
import 'package:initial_folder/widgets/loading/loading_my_course.dart';
import 'package:initial_folder/widgets/my_course_list.dart';
import 'package:provider/provider.dart';
import '../../theme.dart';
class MyCoursePage extends StatelessWidget {
const MyCoursePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final themeProvider = Provider.of<ThemeProvider>(context);
Widget notLogin() {
return Center(
child: Container(
margin: EdgeInsets.symmetric(
horizontal: getProportionateScreenWidth(40),
),
child: Text(
'Kamu belum login, silahkan login untuk melihat kursus mu',
textAlign: TextAlign.center,
style: primaryTextStyle,
),
),
);
}
Widget myCourse() {
return RefreshIndicator(
displacement: 40,
color: primaryColor,
onRefresh: () async {
await Provider.of<MyCourseProvider>(context, listen: false)
.getMyCourse();
},
child: Consumer<MyCourseProvider>(
builder: (BuildContext context, state, _) {
if (state.state == ResultState.Loading) {
return Column(
children: [
LoadingMyCourse(),
LoadingMyCourse(),
LoadingMyCourse(),
],
);
} else if (state.state == ResultState.HasData) {
return Container(
margin:
EdgeInsets.only(bottom: getProportionateScreenHeight(4)),
child: ListView.builder(
shrinkWrap: true,
itemCount: state.result!.data[0].length,
itemBuilder: (context, index) {
var myCourse = state.result!.data[0][index];
return MyCourseList(
dataMyCourseModel: myCourse,
);
},
),
);
} else if (state.state == ResultState.NoData) {
return ListView(
children: [
Container(
child: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(height: getProportionateScreenHeight(47)),
Container(
width: getProportionateScreenWidth(120),
height: getProportionateScreenWidth(120),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(2),
),
child: Image.asset(
'assets/images/kursuskosong.png',
scale: 1,
color: Theme.of(context).colorScheme.onBackground,
),
),
SizedBox(height: getProportionateScreenHeight(15)),
Text(
"Kursus tidak tersedia",
style: secondaryTextStyle.copyWith(
letterSpacing: 1,
fontWeight: semiBold,
fontSize: getProportionateScreenWidth(14),
color: tenthColor,
),
),
SizedBox(height: getProportionateScreenHeight(3)),
Container(
padding: EdgeInsets.symmetric(
horizontal: getProportionateScreenWidth(16)),
child: Text(
"Kamu belum memiliki kursus, daftar kursus sekarang agar kamu dapat mengikuti kursus",
textAlign: TextAlign.center,
style: thirdTextStyle.copyWith(
fontWeight: semiBold,
fontSize: getProportionateScreenWidth(12),
),
),
),
SizedBox(height: getProportionateScreenHeight(15)),
PopulerCourse(text: 'Kursus Teratas'),
LatestCourse(text: 'Kursus Terbaru'),
],
),
),
),
],
);
} else if (state.state == ResultState.Error) {
return Center(
child: ListView(
children: [
Container(
padding: const EdgeInsets.all(20.0),
constraints: BoxConstraints(
minHeight: MediaQuery.of(context).size.height / 1.5),
child: Center(
child: Text('Terjadi Kesalahan'),
),
)
],
),
);
} else {
return Center(child: Text(''));
}
},
),
);
}
return Scaffold(
appBar: (Condition.loginEmail || Condition.loginFirebase)
? AppBar(
scrolledUnderElevation: 0.0,
backgroundColor: Theme.of(context).colorScheme.background,
title: Text(
'Kursusku',
style: thirdTextStyle.copyWith(
fontWeight: semiBold,
letterSpacing: 1,
fontSize: getProportionateScreenWidth(20),
),
),
actions: [
IconButton(
onPressed: () {
Provider.of<MyCourseProvider>(context, listen: false)
.clearSearch();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SearchMyCourse(),
),
);
},
icon: Icon(
FeatherIcons.search,
color: themeProvider.themeData == ThemeClass.darkmode
?primaryColor : primaryColorligtmode,
),
),
SizedBox(width: getProportionateScreenWidth(5)),
],
)
: AppBar(
scrolledUnderElevation: 0.0,
backgroundColor: Theme.of(context).colorScheme.background,
title: Text(
'Kursusku',
style: secondaryTextStyle.copyWith(
fontWeight: semiBold,
letterSpacing: 2.3,
fontSize: getProportionateScreenWidth(16),
),
),
),
body: (Condition.loginEmail || Condition.loginFirebase)
? myCourse()
: notLogin(),
);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,161 @@
import 'package:cherry_toast/cherry_toast.dart';
import 'package:cherry_toast/resources/arrays.dart';
import 'package:flutter/material.dart';
import 'package:initial_folder/models/quiz_model.dart';
import 'package:initial_folder/screens/course/quiz_question_page.dart';
import 'package:initial_folder/services/quiz_service.dart';
import 'package:initial_folder/size_config.dart';
import 'package:initial_folder/theme.dart';
import 'package:initial_folder/widgets/login_regist/default_button.dart';
import 'package:tap_debouncer/tap_debouncer.dart';
class quizPage extends StatefulWidget {
final String judulQuiz;
final String lessonId;
const quizPage({super.key, required this.judulQuiz, required this.lessonId});
@override
State<quizPage> createState() => _quizPageState();
}
class _quizPageState extends State<quizPage> {
QuizModel? quizModel;
@override
void initState() {
super.initState();
print(widget.judulQuiz);
print(widget.lessonId);
getQuiz();
}
void getQuiz() {
quiz_service().get_quiz_info(widget.lessonId).then((value) => {
setState(() {
quizModel = value;
print("ini id apa gatau" + quizModel!.data.first.quizId);
})
});
}
Widget build(BuildContext context) {
return quizModel == null
? Center(
child: Scaffold(
body: Center(
child: CircularProgressIndicator(
color: primaryColor,
),
),
))
: Scaffold(
appBar: AppBar(
centerTitle: true,
backgroundColor: Colors.transparent,
),
body: SingleChildScrollView(
child: Center(
child: Column(
children: [
Container(
margin: EdgeInsets.all(25),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: Theme.of(context).brightness ==
Brightness.dark
? Colors.black
: Colors.grey,
blurRadius: 2,
spreadRadius: 1,
offset: Offset(0, 3))
]),
child: Column(
children: [
SizedBox(
height: getProportionateScreenHeight(71),
),
Image.asset(
'assets/images/quizLogo-page.png',
scale: 0.8,
),
SizedBox(
height: getProportionateScreenHeight(36),
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 30),
child: Text(
widget.judulQuiz,
textAlign: TextAlign.center,
style: thirdTextStyle.copyWith(
fontWeight: bold,
fontSize: getProportionateScreenWidth(15)),
),
),
SizedBox(
height: getProportionateScreenHeight(10),
),
Text(
"Jumlah Pertanyaan : " +
quizModel!.total.toString(),
textAlign: TextAlign.center,
style: thirdTextStyle.copyWith(
fontWeight: bold,
fontSize: getProportionateScreenWidth(15)),
),
SizedBox(
height: getProportionateScreenHeight(20),
),
Padding(
padding: EdgeInsets.all(
getProportionateScreenHeight(15)),
child: TapDebouncer(
cooldown: const Duration(milliseconds: 1000),
onTap: () async => await {
if (quizModel!.total.toString() != "0")
{
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
quiz_questionPage(
judulQuiz: widget.judulQuiz,
lessonId: widget.lessonId,
totalQuiz: quizModel!.total,
quizId: int.parse(quizModel!
.data.first.quizId),
)))
}
else
{
CherryToast.error(
animationDuration: Durations.long1,
title: Text(
"Pertanyaan untuk quiz ini \nbelum tersedia",
style: TextStyle(
color: Colors.black,
fontSize: 15,
),
),
animationType: AnimationType.fromTop,
).show(context)
}
},
builder: (BuildContext context,
TapDebouncerFunc? onTap) {
return DefaultButton(
text: 'Mulai Quiz', press: onTap);
},
))
],
),
)
],
),
),
),
);
}
}

View File

@ -0,0 +1,225 @@
import 'package:cherry_toast/cherry_toast.dart';
import 'package:cherry_toast/resources/arrays.dart';
import 'package:flutter/material.dart';
import 'package:initial_folder/models/quiz_model.dart';
import 'package:initial_folder/models/quiz_question_model.dart';
import 'package:initial_folder/screens/course/quiz_result_page.dart';
import 'package:initial_folder/services/quiz_service.dart';
import 'package:initial_folder/size_config.dart';
import 'package:initial_folder/theme.dart';
import 'package:initial_folder/widgets/login_regist/default_button.dart';
import 'package:tap_debouncer/tap_debouncer.dart';
import 'package:html/parser.dart';
class quiz_questionPage extends StatefulWidget {
final String judulQuiz;
final String lessonId;
final int totalQuiz;
final int quizId;
const quiz_questionPage(
{super.key,
required this.judulQuiz,
required this.lessonId,
required this.totalQuiz,
required this.quizId});
@override
State<quiz_questionPage> createState() => _quiz_questionPageState();
}
class _quiz_questionPageState extends State<quiz_questionPage> {
int currentQuestionIndex = 0; // Menyimpan indeks pertanyaan saat ini
QuizModel? quizModel;
String? selectedOption;
bool isSelected = false;
List<Map<String, dynamic>> savedAnswers = [];
@override
void initState() {
super.initState();
getQuiz();
}
void getQuiz() {
quiz_service().get_quiz_info(widget.lessonId).then((value) {
setState(() {
quizModel = value;
});
});
}
Widget build(BuildContext context) {
String _parseHtmlString(String htmlText) {
RegExp exp =
RegExp(r"<[^>]*>|&nbsp;", multiLine: true, caseSensitive: true);
return htmlText.replaceAll(exp, '');
}
return quizModel == null
? Center(
child: Scaffold(
body: Center(
child: CircularProgressIndicator(
color: primaryColor,
),
),
))
: Scaffold(
body: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Pertanyaan ${currentQuestionIndex + 1}', // Menampilkan nomor pertanyaan
textAlign: TextAlign.center,
style: primaryTextStyle.copyWith(
fontWeight: reguler,
fontSize: getProportionateScreenWidth(15),
),
),
SizedBox(
height: getProportionateScreenHeight(15),
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 30),
child: Text(
_parseHtmlString(quizModel!.data[currentQuestionIndex]
.title), // Menampilkan judul pertanyaan
textAlign: TextAlign.center,
style: thirdTextStyle.copyWith(
fontWeight: reguler,
fontSize: getProportionateScreenWidth(15),
),
),
),
SizedBox(
height: getProportionateScreenHeight(20),
),
Container(
width: getProportionateScreenWidth(350),
height: getProportionateScreenHeight(400),
// color: Colors.amber,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: quizModel!.data[currentQuestionIndex].options
.map((option) {
return Padding(
padding: EdgeInsets.fromLTRB(
getProportionateScreenWidth(15),
getProportionateScreenHeight(5),
getProportionateScreenWidth(15),
getProportionateScreenHeight(5)),
child: Container(
decoration: BoxDecoration(
color: Theme.of(context)
.colorScheme
.primaryContainer,
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: Theme.of(context).brightness ==
Brightness.dark
? Colors.black
: Colors.grey,
blurRadius: 2,
spreadRadius: 1,
offset: Offset(0, 3))
]),
child: CheckboxListTile(
activeColor: primaryColor,
checkColor: Colors.white,
checkboxShape: RoundedRectangleBorder(
side: BorderSide(
color:
primaryColor), // Mengatur warna dan ketebalan outline checkbox
borderRadius: BorderRadius.circular(2),
),
title: Text(
option,
style: thirdTextStyle.copyWith(fontSize: 14),
),
value: selectedOption == option,
controlAffinity: ListTileControlAffinity
.leading, // Checkbox di sebelah kiri
onChanged: (value) {
setState(() {
selectedOption = value! ? option : null!;
isSelected = true;
});
},
),
),
);
}).toList(),
),
)),
SizedBox(height: 20),
Padding(
padding: EdgeInsets.only(
left: getProportionateScreenWidth(100),
right: getProportionateScreenWidth(100)),
child: TapDebouncer(
cooldown: const Duration(milliseconds: 1000),
onTap: () async => await {
setState(() {
//ini save quiz
if (isSelected != false) {
savedAnswers.add({
'"question_id"':
quizModel!.data[currentQuestionIndex].id,
'"answers"': [
'"${quizModel!.data[currentQuestionIndex].options.indexOf(selectedOption!) + 1}"'
]
});
if (currentQuestionIndex <
quizModel!.data.length - 1) {
currentQuestionIndex++;
} else {
if (widget.totalQuiz ==
currentQuestionIndex + 1) {
print('sudah habis');
// print(savedAnswers);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => quiz_resultPage(
judulQuiz: widget.judulQuiz,
lessonId: widget.lessonId,
totalQuiz: quizModel!.total,
allAnswer:
savedAnswers.toString(),
IdQuiz: widget.quizId,
)));
}
}
} else {
CherryToast.error(
animationDuration: Durations.long1,
title: Text(
"Silakhan Pilih salah satu \nJawaban yang tersedia",
style: TextStyle(
color: Colors.black,
fontSize: 15,
),
),
animationType: AnimationType.fromTop,
).show(context);
print('object');
}
// print(savedAnswers);
isSelected = false;
})
},
builder: (BuildContext context, TapDebouncerFunc? onTap) {
return DefaultButton(
text: 'Submit & Next', press: onTap);
},
))
],
),
);
}
}

View File

@ -0,0 +1,500 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:initial_folder/models/quiz_model.dart';
import 'package:initial_folder/models/quiz_perquestion_result_model.dart';
import 'package:initial_folder/models/quiz_question_model.dart';
import 'package:initial_folder/models/quiz_question_result_model.dart';
import 'package:initial_folder/screens/course/play_course_page.dart';
import 'package:initial_folder/screens/course/quiz_page.dart';
import 'package:initial_folder/services/quiz_service.dart';
import 'package:initial_folder/size_config.dart';
import 'package:initial_folder/theme.dart';
import 'package:initial_folder/widgets/login_regist/default_button.dart';
import 'package:tap_debouncer/tap_debouncer.dart';
import 'package:html/parser.dart';
class quiz_resultPage extends StatefulWidget {
final String judulQuiz;
final String lessonId;
final int totalQuiz;
final String allAnswer;
final int IdQuiz;
const quiz_resultPage(
{super.key,
required this.judulQuiz,
required this.lessonId,
required this.totalQuiz,
required this.allAnswer,
required this.IdQuiz});
@override
State<quiz_resultPage> createState() => _quiz_resultPageState();
}
class _quiz_resultPageState extends State<quiz_resultPage> {
QuizQuestionResult? quizQuestionResult;
QuizPerQuestionResult? quizPerQuestionResult;
int currentQuestionIndex = 1; // Menyimpan indeks pertanyaan saat ini
bool fromAmbilLagi = false;
int _selectedQuesNum = 1;
String _selectedQuesText = "";
List<Map<String, dynamic>> allAnswersList = [];
@override
void initState() {
super.initState();
print("ini id apa gatau" + widget.IdQuiz.toString());
// Parsing the allAnswer JSON string to List
allAnswersList =
List<Map<String, dynamic>>.from(json.decode(widget.allAnswer));
getAnswerQuiz();
getQuestionPerNumber();
}
void getAnswerQuiz() {
quiz_service().get_result_quiz(widget.allAnswer).then((value) {
setState(() {
quizQuestionResult = value;
});
});
}
void getQuestionPerNumber() {
print(_selectedQuesNum);
quiz_service()
.get_result_quiz_pernumber(widget.IdQuiz, _selectedQuesNum)
.then((value) {
setState(() {
quizPerQuestionResult = value;
});
});
}
bool _isUserSelectedAnswer(int questionId, int optionIndex) {
for (var answer in allAnswersList) {
if (answer['question_id'] == questionId &&
answer['answers'].contains(optionIndex.toString())) {
return true;
}
}
return false;
}
Widget build(BuildContext context) {
String _parseHtmlString(String htmlText) {
RegExp exp =
RegExp(r"<[^>]*>|&nbsp;", multiLine: true, caseSensitive: true);
return htmlText.replaceAll(exp, '');
}
int countCorrectAnswers() {
if (quizQuestionResult != null && quizQuestionResult!.data.isNotEmpty) {
// Menggunakan metode where untuk menyaring data yang memiliki is_correct true
int correctCount = quizQuestionResult!.data
.where((question) => question.isCorrect == true)
.length;
return correctCount;
} else {
return 0;
}
}
return quizQuestionResult == null
? Center(
child: Scaffold(
body: Center(
child: CircularProgressIndicator(
color: primaryColor,
),
),
))
: PopScope(
child: Scaffold(
appBar: AppBar(
centerTitle: true,
backgroundColor: Colors.transparent,
automaticallyImplyLeading:
false, // Menonaktifkan tombol kembali default
leading: IconButton(
// Menambahkan tombol kustom
icon: Icon(Icons.arrow_back),
onPressed: () {
// Tambahkan fungsi yang diinginkan saat tombol kembali ditekan
Future.delayed(Duration.zero, () {
Navigator.of(context).pop();
});
},
),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background,
boxShadow: [
BoxShadow(
color: Theme.of(context).brightness == Brightness.dark
? Colors.black
: Colors.grey,
spreadRadius: 1,
blurRadius: 5,
offset: Offset(0, 1), // Shadow position
),
],
),
child: Column(
children: [
Container(
margin: EdgeInsetsDirectional.only(
top: getProportionateScreenHeight(15)),
height: getProportionateScreenHeight(110),
width: getProportionateScreenWidth(320),
decoration: BoxDecoration(
color: Theme.of(context)
.colorScheme
.primaryContainer,
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: Theme.of(context).brightness ==
Brightness.dark
? Colors.black
: Colors.grey,
blurRadius: 2,
spreadRadius: 1,
offset: Offset(0, 0))
]),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Tinjau materi kursus untuk memperluas pembelajaran kamu.', // Menampilkan nomor pertanyaan
textAlign: TextAlign.center,
style: thirdTextStyle.copyWith(
fontWeight: FontWeight.w200,
fontSize: getProportionateScreenWidth(13),
),
),
SizedBox(
height: getProportionateScreenHeight(15),
),
Text(
'Kamu mendapatkan ${countCorrectAnswers()} Dari ${widget.totalQuiz} Benar', // Menampilkan nomor pertanyaan
textAlign: TextAlign.center,
style: thirdTextStyle.copyWith(
fontWeight: FontWeight.w200,
fontSize: getProportionateScreenWidth(13),
),
),
],
),
),
SizedBox(
height: 25,
),
SizedBox(
height: 90, // Adjust height to control the size
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: quizQuestionResult!.data.length,
itemBuilder: (context, index) {
var dataques = quizQuestionResult!.data[index];
return GestureDetector(
onTap: () {
setState(() {
_selectedQuesNum = index + 1;
// getQuestionPerNumber()
_selectedQuesText = dataques.question;
});
getQuestionPerNumber();
// print(
// quizPerQuestionResult!.data.first.title);
// print(dataques.question);
// print(dataques.questionId);
},
child: SizedBox(
width: 80, // Adjust width here
height: 65, // Adjust height here
child: Container(
margin: EdgeInsets.all(10),
decoration: BoxDecoration(
color: Theme.of(context)
.colorScheme
.primaryContainer,
borderRadius:
BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: Theme.of(context)
.brightness ==
Brightness.dark
? Colors.black
: Colors.grey,
blurRadius: 2,
spreadRadius: 1,
offset: Offset(0, 3))
]),
child: Column(
mainAxisAlignment:
MainAxisAlignment.end,
children: [
Center(
child: Text(
'${index + 1}',
// dataques.isCorrect.toString(),
style: thirdTextStyle.copyWith(
fontSize: 16,
fontWeight: FontWeight.w800),
),
),
SizedBox(
height: 5,
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
height:
getProportionateScreenHeight(
18),
decoration: BoxDecoration(
color: dataques.isCorrect ==
true
? Colors.green
: dataques.isCorrect != true
? Colors.red
: Colors.red,
borderRadius:
BorderRadius.vertical(
bottom: Radius.circular(
15)),
),
child: Center(
child: dataques.isCorrect ==
true
? Icon(
Icons
.check_circle_outline,
color: Colors.white,
size:
getProportionateScreenHeight(
13),
)
: dataques.isCorrect !=
true
? Icon(
Icons
.cancel_outlined,
color:
Colors.white,
size:
getProportionateScreenHeight(
13),
)
: Icon(
Icons
.cancel_outlined,
color:
Colors.white,
size:
getProportionateScreenHeight(
13),
)),
),
),
],
)),
),
);
},
),
),
SizedBox(
height: 15,
)
],
),
),
SizedBox(
height: getProportionateScreenHeight(15),
),
Text(
'Pertanyaan ${_selectedQuesNum}',
style: thirdTextStyle.copyWith(
fontSize: getProportionateScreenWidth(14)),
),
SizedBox(
height: getProportionateScreenHeight(15),
),
Text(
_parseHtmlString(quizPerQuestionResult!.data.first.title),
style: thirdTextStyle.copyWith(
fontSize: getProportionateScreenWidth(14)),
),
Expanded(
child: ListView.builder(
scrollDirection: Axis.vertical,
itemCount:
quizPerQuestionResult!.data.first.options.length,
itemBuilder: (context, index) {
var dataques = quizPerQuestionResult!.data.first;
bool isCorrectAnswer = dataques.correctAnswers
.contains((index + 1).toString());
return Padding(
padding: EdgeInsets.all(10),
child: Container(
decoration: BoxDecoration(
color: fourthColor,
borderRadius: BorderRadius.circular(10),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: getProportionateScreenHeight(45),
decoration: BoxDecoration(
color: Theme.of(context)
.colorScheme
.primaryContainer,
borderRadius:
BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: Theme.of(context)
.brightness ==
Brightness.dark
? Colors.black
: Colors.grey,
blurRadius: 5,
spreadRadius: 3,
offset: Offset(0, 0))
]),
child: Row(
children: [
Container(
margin: EdgeInsets.symmetric(
horizontal: 25),
height:
getProportionateScreenHeight(
20),
width:
getProportionateScreenWidth(25),
decoration: BoxDecoration(
color: isCorrectAnswer
? Colors.green
: _isUserSelectedAnswer(
int.parse(
dataques.id),
index + 1)
? Colors.red
: Colors.transparent,
borderRadius: BorderRadius.all(
Radius.circular(10)),
border: Border.all(
color: isCorrectAnswer
? Colors.green
: _isUserSelectedAnswer(
int.parse(
dataques
.id),
index + 1)
? Colors.red
: Colors.grey,
width: 2)),
child: Center(
child: isCorrectAnswer
? Icon(
Icons.check,
color: Colors.white,
weight: 5,
)
: _isUserSelectedAnswer(
int.parse(
dataques.id),
index + 1)
? Icon(
Icons.check,
color: Colors.white,
weight: 5,
)
: SizedBox(),
),
),
Text(
' ${_parseHtmlString(dataques.options[index])}',
style: thirdTextStyle.copyWith(
color: isCorrectAnswer
? Colors.green
: _isUserSelectedAnswer(
int.parse(
dataques.id),
index + 1)
? Colors.red
: Colors.grey,
fontSize:
getProportionateScreenWidth(
12)),
),
],
),
),
],
)));
// Helper function to check if the current option is the user's selected answer
},
),
),
// SingleChildScrollView(child: ,),
Padding(
padding: EdgeInsets.only(
bottom: getProportionateScreenHeight(20),
top: getProportionateScreenHeight(10)),
child: Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: EdgeInsets.only(
left: getProportionateScreenWidth(100),
right: getProportionateScreenWidth(100)),
child: DefaultButton(
text: 'Ambil Lagi',
press: () {
setState(() {
fromAmbilLagi = true;
_quizscr(context);
});
},
),
),
),
)
],
),
),
onPopInvoked: (didPop) => _willPop(context),
);
}
void _willPop(BuildContext context) {
Future.delayed(Duration.zero, () {
if (fromAmbilLagi != true) {
Navigator.of(context)..pop();
}
Navigator.of(context)
.pop(); // Hanya pop satu halaman jika tidak dari _quizscr
fromAmbilLagi = false;
});
}
void _quizscr(BuildContext context) {
Navigator.pop(context);
}
}

View File

@ -0,0 +1,90 @@
import 'package:flutter/material.dart';
import 'package:initial_folder/providers/my_course_provider.dart';
import 'package:initial_folder/widgets/loading/loading_my_course.dart';
import 'package:initial_folder/widgets/my_course_list.dart';
import 'package:provider/provider.dart';
import '../../theme.dart';
class SearchMyCourse extends StatelessWidget {
const SearchMyCourse({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
TextEditingController controller = TextEditingController();
void refreshCourseList() {
Provider.of<MyCourseProvider>(context, listen: false)
.getSearchMyCourse(controller.text);
}
return Scaffold(
appBar: AppBar(
scrolledUnderElevation: 0.0,
backgroundColor: Theme.of(context).colorScheme.background,
title: TextField(
autofocus: true,
decoration: new InputDecoration.collapsed(
hintText: 'Cari Kursusku',
),
controller: controller,
onSubmitted: (value) => refreshCourseList(),
),
),
body: RefreshIndicator(
displacement: 40,
color: primaryColor,
onRefresh: () async {
refreshCourseList();
},
child: Consumer<MyCourseProvider>(
builder: (BuildContext context, state, _) {
if (state.searchResultState == SearchResultState.Loading) {
return SingleChildScrollView(
child: Column(
children: [
LoadingMyCourse(),
LoadingMyCourse(),
LoadingMyCourse(),
],
),
);
} else if (state.searchResultState == SearchResultState.HasData) {
return ListView.builder(
shrinkWrap: true,
itemCount: state.searchResult!.data[0].length,
itemBuilder: (context, index) {
var myCourse = state.searchResult!.data[0][index];
return MyCourseList(
dataMyCourseModel: myCourse,
onDialogClose: refreshCourseList,
);
},
);
} else if (state.searchResultState == SearchResultState.NoData) {
Provider.of<MyCourseProvider>(context, listen: false)
.clearSearch();
return Center(
child: Text(
'Kursus Tidak Ditemukan',
style: thirdTextStyle,
),
);
} else if (state.searchResultState == SearchResultState.Error) {
Provider.of<MyCourseProvider>(context, listen: false)
.clearSearch();
return Center(
child: Text(
'Terjadi Kesalahan',
style: thirdTextStyle,
),
);
} else {
return Center(child: Text(''));
}
},
),
),
);
}
}

View File

@ -0,0 +1,784 @@
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:initial_folder/services/all_certificate_service.dart';
import 'package:initial_folder/size_config.dart';
import 'package:initial_folder/theme.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:initial_folder/providers/certificate_provider.dart'
as certifProvider;
import 'package:qr_flutter/qr_flutter.dart';
class Sertif extends StatefulWidget {
Sertif({
Key? key,
this.totalProgress,
this.idCourse,
this.idPayment,
this.finishDate,
this.text,
this.pdfBytes,
this.saving,
}) : super(key: key);
static String routeName = "/sertif";
final int? totalProgress;
final int? idCourse;
final String? idPayment;
final String? text;
final dynamic finishDate;
bool? saving = false;
final Uint8List? pdfBytes;
@override
State<Sertif> createState() => _SertifState();
}
class _SertifState extends State<Sertif> {
final _globalKeySertif = GlobalKey();
bool _isLoading = false;
@override
Widget build(BuildContext context) {
Future.delayed(Duration(seconds: 0), () {
if (widget.text == null) {
Provider.of<certifProvider.CertificateProvider>(context, listen: false)
.getCertif(widget.idCourse.toString());
} else {
Provider.of<certifProvider.CertificateProvider>(context, listen: false)
.checkSertif(widget.text!);
}
});
certifProvider.CertificateProvider? certifProv =
Provider.of<certifProvider.CertificateProvider>(context, listen: false);
Widget notFinish() {
return SafeArea(
child: Scaffold(
appBar: AppBar(
centerTitle: true,
),
body: Center(
child: Container(
height: getProportionateScreenHeight(270),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: Theme.of(context).brightness == Brightness.dark
? Colors.black
: Colors.grey,
blurRadius: 2,
spreadRadius: 1,
offset: Offset(0, 3))
]),
margin: EdgeInsets.only(
left: getProportionateScreenWidth(16),
right: getProportionateScreenWidth(16),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Sertifikat',
textAlign: TextAlign.center,
style: thirdTextStyle.copyWith(
letterSpacing: -0.5,
color: Theme.of(context).colorScheme.onBackground,
fontWeight: reguler,
fontSize: getProportionateScreenWidth(28),
),
),
Text(
'Belum Tersedia',
textAlign: TextAlign.center,
style: thirdTextStyle.copyWith(
letterSpacing: -0.5,
color: Theme.of(context).colorScheme.onBackground,
fontWeight: reguler,
fontSize: getProportionateScreenWidth(28),
),
),
SizedBox(height: getProportionateScreenHeight(24)),
Padding(
padding: EdgeInsets.symmetric(horizontal: 15),
child: Stack(
children: [
Container(
width: double.infinity,
height: getProportionateScreenWidth(20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.grey[300],
),
),
Container(
width: (SizeConfig.screenWidth -
getProportionateScreenWidth(32)) *
(widget.totalProgress ?? 0) /
100,
height: getProportionateScreenWidth(20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: primaryColor,
),
child: Text(
'${widget.totalProgress ?? 0}%',
textAlign: TextAlign.center,
style: primaryTextStyle.copyWith(
fontSize: getProportionateScreenWidth(12),
fontWeight: reguler,
color: Colors.white,
letterSpacing: 0.5,
),
),
),
],
),
),
SizedBox(height: getProportionateScreenHeight(16)),
Text(
'Anda baru menyelesaikan ${widget.totalProgress ?? 0}% kursus',
textAlign: TextAlign.center,
style: thirdTextStyle.copyWith(
letterSpacing: 0.5,
color: Theme.of(context).colorScheme.onBackground,
fontWeight: reguler,
fontSize: getProportionateScreenWidth(13),
),
),
SizedBox(height: getProportionateScreenHeight(16)),
Text(
'Anda belum memenuhi persyaratan untuk mendapatkan sertfikat. Selesaikan kursus anda untuk mendapatkan sertifikat penyelesaian kursus.',
textAlign: TextAlign.center,
style: primaryTextStyle.copyWith(
letterSpacing: 0.5,
color: Theme.of(context).colorScheme.onBackground,
fontWeight: reguler,
fontSize: getProportionateScreenWidth(13),
),
),
],
),
),
),
),
);
}
Widget checkCertificateFinish() {
return SafeArea(
child: Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text(
'Sertifikat',
style: thirdTextStyle.copyWith(
letterSpacing: 0.23,
fontWeight: semiBold,
fontSize: getProportionateScreenWidth(14),
),
)),
body: Center(
child: Container(
margin: EdgeInsets.only(
left: getProportionateScreenWidth(16),
right: getProportionateScreenWidth(16),
),
child: Consumer<certifProvider.CertificateProvider>(
builder: (context, state, _) {
if (state.state == certifProvider.ResultState.Loading) {
return Center(
child: CircularProgressIndicator(
strokeWidth: 2,
color: primaryColor,
),
);
} else if (state.state ==
certifProvider.ResultState.HasData) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
margin: EdgeInsets.all(15),
width: double.infinity,
height: 280,
child: LayoutBuilder(
builder: (context, constraint) {
return Stack(
alignment: Alignment.center,
children: [
Image.asset(
'assets/images/certif_template_new.png'),
Positioned(
top: constraint.maxHeight / 2.55,
left: 0,
right: 0,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 25),
child: Text(
certifProv.name!,
textAlign: TextAlign.center,
style: primaryTextStyle.copyWith(
fontWeight: FontWeight.bold,
fontSize: 14,
color: Color(0xff575553)),
),
),
),
Positioned(
top: constraint.maxHeight / 1.9,
left: 0,
right: 0,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 33),
child: Text('"${certifProv.title}"',
style: primaryTextStyle.copyWith(
fontWeight: FontWeight.bold,
fontSize:
(certifProv.title!.length >
20)
? 9
: 12,
color: Color(0xff575553)),
textAlign: TextAlign.center),
),
),
Positioned(
top: constraint.maxHeight / 1.6,
left: 0,
right: 0,
child: Center(
child: Text(
'${DateFormat('dd MMMM yyyy ').format(DateTime.fromMillisecondsSinceEpoch(certifProv.finishDate * 1000))}',
style: primaryTextStyle.copyWith(
fontWeight: FontWeight.normal,
fontSize: 8,
color: Color(0xff575553)),
),
),
),
Positioned(
bottom: constraint.maxHeight / 5.6,
left: constraint.maxWidth / 10,
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
'Certificate no : ${certifProv.certificateNo}',
style: primaryTextStyle.copyWith(
fontWeight: FontWeight.normal,
fontSize: 4,
color: Color(0xff575553),
),
),
Text(
'Certificate URL : https://vocasia-v4-develop.vercel.app/certificate/${certifProv.certificateNo}',
style: primaryTextStyle.copyWith(
fontWeight: FontWeight.normal,
fontSize: 4,
color: Color(0xff575553),
),
),
],
),
)
],
);
},
),
),
],
);
} else if (state.state == certifProvider.ResultState.Error) {
return Center(
child: Column(
children: [
Text(
'Terjadi Kesalahan Coba Lagi',
style: thirdTextStyle,
),
],
),
);
} else {
return Center(
child: Text(
'Terjadi kesalahan',
style: thirdTextStyle,
),
);
}
},
),
),
),
),
);
}
Widget checkCertificateNotFinished() {
return SafeArea(
child: Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text(
'Sertifikat',
style: thirdTextStyle.copyWith(
letterSpacing: 0.23,
fontWeight: semiBold,
fontSize: getProportionateScreenWidth(14),
),
),
),
body: Center(
child: Text(
'Sertifikat Belum Diterbitkan',
textAlign: TextAlign.center,
style: primaryTextStyle.copyWith(
letterSpacing: 0.5,
color: Theme.of(context).colorScheme.onBackground,
fontWeight: reguler,
fontSize: getProportionateScreenWidth(20),
),
),
),
),
);
}
Widget buildCertificate() {
return Container(
color: Colors.red,
margin: EdgeInsets.all(15),
width: double.infinity,
height: 300,
child: Stack(
alignment: Alignment.center,
children: [
Image.asset('assets/certif_template_new.png'),
Positioned(
top: 120,
left: 0,
right: 0,
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: getProportionateScreenHeight(25)),
child: Text(
'Safinatun Najah Unju',
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: getProportionateScreenWidth(14)),
),
),
),
Positioned(
top: 155,
left: 0,
right: 0,
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: getProportionateScreenHeight(33)),
child: Column(
children: [
Text(
'"Basic Excel Trainig For Professional Employees & Recruitment Selection Candidates"',
style: TextStyle(
fontWeight: FontWeight.w600, fontSize: 12),
textAlign: TextAlign.center),
SizedBox(height: getProportionateScreenHeight(10)),
Text(
'Jakarta, 24 October 2021',
style: TextStyle(
fontWeight: FontWeight.normal,
fontSize: getProportionateScreenWidth(8)),
)
],
),
),
),
Positioned(
bottom: getProportionateScreenHeight(50),
left: (65 / 640) * MediaQuery.of(context).size.width,
child: Column(
children: [
Text(
'Certificate no : 257911829183',
style: TextStyle(
fontWeight: FontWeight.normal,
fontSize: getProportionateScreenWidth(6)),
),
Text(
'Certificate no : 257911829183',
style: TextStyle(
fontWeight: FontWeight.normal,
fontSize: getProportionateScreenWidth(6)),
),
],
),
)
],
),
);
}
Widget finish() {
return SafeArea(
child: Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.background,
centerTitle: true,
title: Text(
'Sertifikat',
style: thirdTextStyle.copyWith(
letterSpacing: 0.23,
fontWeight: semiBold,
fontSize: getProportionateScreenWidth(14)),
),
),
body: Center(
child: Container(
child: Consumer<certifProvider.CertificateProvider>(
builder: (context, state, _) {
if (state.state == certifProvider.ResultState.Loading) {
return Center(
child: CircularProgressIndicator(
color: primaryColor,
strokeWidth: 2,
),
);
} else if (state.state ==
certifProvider.ResultState.HasData) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
InteractiveViewer(
child: RepaintBoundary(
key: _globalKeySertif,
child: Container(
width: getProportionateScreenWidth(300),
height: getProportionateScreenHeight(173),
child: LayoutBuilder(
builder: (context, constraint) {
return Stack(
alignment: Alignment.center,
children: [
Image.asset(
'assets/images/certif_template_new.png'),
Positioned.fill(
child: FractionallySizedBox(
alignment: Alignment.center,
widthFactor: 1.0,
heightFactor: 0.6,
child: Padding(
padding: EdgeInsets.only(
left:
getProportionateScreenWidth(28.5),
right:
getProportionateScreenWidth(240),
top: getProportionateScreenHeight(10),
bottom: getProportionateScreenHeight(90)
),
child: Text(
certifProv.certificateNo!.toUpperCase(),
textAlign: TextAlign.left,
style: primaryTextStyle.copyWith(
fontFamily: 'Arial',
fontWeight: FontWeight.w500,
fontSize:
getProportionateScreenWidth(
4),
color: Color.fromARGB(255, 255, 255, 255),
),
),
),
),
),
Positioned.fill(
child: FractionallySizedBox(
alignment: Alignment.center,
widthFactor: 1.0,
heightFactor: 0.3,
child: Padding(
padding: EdgeInsets.only(
left:
getProportionateScreenWidth(27.5),
right:
getProportionateScreenHeight(
80),
top: getProportionateScreenHeight(
6),
),
child: Text(
certifProv.name!.toUpperCase(),
textAlign: TextAlign.left,
style: primaryTextStyle.copyWith(
fontFamily: 'Arial',
fontWeight: FontWeight.bold,
fontSize:
getProportionateScreenWidth(
9),
color: Color.fromARGB(255, 248, 124, 0),
),
),
),
),
),
Positioned.fill(
child: FractionallySizedBox(
alignment: Alignment.bottomCenter,
widthFactor: 1.0,
heightFactor: 0.5,
child: Padding(
padding: EdgeInsets.only(
right:
getProportionateScreenWidth(
85),
left: 27.5),
child: Text('"${certifProv.title}"',
style: primaryTextStyle.copyWith(
fontFamily: 'Arial',
fontWeight: FontWeight.bold,
fontSize: certifProv
.title!.length >
40
? getProportionateScreenWidth(
9)
: getProportionateScreenWidth(
11),
color: Color.fromARGB(255, 248, 124, 0)),
textAlign: TextAlign.left,
),
),
),
),
Positioned.fill(
child: FractionallySizedBox(
alignment: Alignment.bottomLeft,
widthFactor: 0.299,
heightFactor: 0.62,
child: Center(
child: Padding(
padding: EdgeInsets.only(
bottom:
getProportionateScreenHeight(
2),
left: 5),
child: Text(
'Jakarta, ${DateFormat('dd MMMM yyyy ').format(DateTime.fromMillisecondsSinceEpoch(certifProv.finishDate * 1000))}',
style: primaryTextStyle.copyWith(
fontWeight:
FontWeight.normal,
fontSize:
getProportionateScreenWidth(
4),
color: Color.fromARGB(255, 0, 0, 0)),
),
),
),
),
),
Positioned.fill(
child: FractionallySizedBox(
alignment: Alignment.bottomRight,
// widthFactor
heightFactor: 0.495,
child: Padding(
padding: EdgeInsets.only(
right: 19
),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.end,
children: [
QrImageView(
data: "https://vocasia.id/?no-certificate=${certifProv.certificateNo}",
version: QrVersions.auto,
size: getProportionateScreenHeight(48),
gapless: false,),
],
),
),
),
),
Positioned.fill(
child: FractionallySizedBox(
alignment: Alignment.bottomRight,
// widthFactor
heightFactor: 0.18,
child: Padding(
padding: EdgeInsets.only(
right: 25
),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.end,
children: [
RichText(
text:
TextSpan(
text:
'Issued : ${DateFormat('dd MMMM yyyy ').format(DateTime.fromMillisecondsSinceEpoch(certifProv.finishDate * 1000))}',
style: primaryTextStyle
.copyWith(
fontWeight:
FontWeight.normal,
fontSize:
getProportionateScreenWidth(
3),
color:
Color(0xff575553),
),
),
)
],
),
),
),
),
],
);
},
),
),
),
),
SizedBox(height: getProportionateScreenHeight(10)),
Padding(
padding: EdgeInsets.symmetric(
horizontal: getProportionateScreenWidth(70)),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
minimumSize: Size(double.infinity,
getProportionateScreenHeight(40)),
backgroundColor: primaryColor,
),
onPressed: _isLoading
? null
: () async {
setState(() {
_isLoading = true;
});
try {
await AllCertificateServices()
.convertAndUpload(_globalKeySertif,
certifProv.idPayment!);
ScaffoldMessenger.of(context)
.showSnackBar(
SnackBar(
duration: Duration(seconds: 2),
backgroundColor: primaryColor,
content: Text(
'Cek email anda!',
textAlign: TextAlign.center,
style: primaryTextStyle.copyWith(
color: baruTextutih),
),
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(5),
),
),
);
} catch (e) {
print('Error: $e');
ScaffoldMessenger.of(context)
.showSnackBar(
SnackBar(
content: Text(
'Gagal mengunduh sertifikat')),
);
} finally {
setState(() {
_isLoading = false;
});
}
},
child: _isLoading
? CircularProgressIndicator(color: baruTextutih)
: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
'assets/icons/download.svg',
color: baruTextutih,
width: 18,
height: 18,
),
Text(
'Download Sertifikat',
style: thirdTextStyle.copyWith(
color: baruTextutih,
fontSize:
getProportionateScreenWidth(12),
),
),
],
),
),
)
],
);
} else if (state.state == certifProvider.ResultState.Error) {
return Center(
child: Column(
children: [
Text(
'Terjadi Kesalahan Coba Lagi',
style: thirdTextStyle,
),
],
),
);
} else {
return Center(
child: Text(
'Terjadi kesalahan',
style: thirdTextStyle,
),
);
}
},
),
),
),
),
);
}
if (widget.totalProgress == 100 && widget.idCourse != null) {
return finish();
} else if (widget.totalProgress != 100 && widget.idCourse != null) {
return notFinish();
} else if (widget.finishDate != false) {
return checkCertificateFinish();
} else {
return checkCertificateNotFinished();
}
}
}

View File

@ -0,0 +1,676 @@
import 'dart:async';
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:initial_folder/size_config.dart';
import 'package:initial_folder/theme.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:initial_folder/providers/certificate_provider.dart'
as certifProvider;
class SertifView extends StatelessWidget {
final _globalKeySertif = GlobalKey();
static String routeName = "/sertif";
final int? totalProgress;
final int? idCourse;
final String? idPayment;
final String? text;
final dynamic finishDate;
SertifView({
Key? key,
this.totalProgress,
this.idCourse,
this.idPayment,
this.finishDate,
this.text,
}) : super(key: key);
@override
Widget build(BuildContext context) {
Future.delayed(Duration(seconds: 0), () {
if (text == null) {
Provider.of<certifProvider.CertificateProvider>(context, listen: false)
.getCertif(idCourse.toString());
} else {
Provider.of<certifProvider.CertificateProvider>(context, listen: false)
.checkSertif(text!);
}
});
certifProvider.CertificateProvider? certifProv =
Provider.of<certifProvider.CertificateProvider>(context, listen: false);
Widget finish() {
return SafeArea(
child: Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.background,
centerTitle: true,
title: Text(
'Sertifikat',
style: secondaryTextStyle.copyWith(
letterSpacing: 0.23,
fontWeight: semiBold,
fontSize: getProportionateScreenWidth(14)),
),
),
body: Center(
child: Container(
child: Consumer<certifProvider.CertificateProvider>(
builder: (context, state, _) {
if (state.state == certifProvider.ResultState.Loading) {
return Center(
child: CircularProgressIndicator(
color: primaryColor,
strokeWidth: 2,
),
);
} else if (state.state ==
certifProvider.ResultState.HasData) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
InteractiveViewer(
child: RepaintBoundary(
key: _globalKeySertif,
child: Container(
width: getProportionateScreenWidth(300),
height: getProportionateScreenHeight(173),
child: LayoutBuilder(
builder: (context, constraint) {
return Stack(
alignment: Alignment.center,
children: [
Image.asset(
'assets/images/certif_template_new.png'),
Positioned.fill(
child: FractionallySizedBox(
alignment: Alignment.center,
widthFactor: 1.0,
heightFactor: 0.6,
child: Padding(
padding: EdgeInsets.only(
left:
getProportionateScreenWidth(28.5),
right:
getProportionateScreenWidth(240),
top: getProportionateScreenHeight(10),
bottom: getProportionateScreenHeight(90)
),
child: Text(
certifProv.certificateNo!.toUpperCase(),
textAlign: TextAlign.left,
style: primaryTextStyle.copyWith(
fontFamily: 'Arial',
fontWeight: FontWeight.w500,
fontSize:
getProportionateScreenWidth(
4),
color: Color.fromARGB(255, 255, 255, 255),
),
),
),
),
),
Positioned.fill(
child: FractionallySizedBox(
alignment: Alignment.center,
widthFactor: 1.0,
heightFactor: 0.3,
child: Padding(
padding: EdgeInsets.only(
left:
getProportionateScreenWidth(27.5),
right:
getProportionateScreenHeight(
80),
top: getProportionateScreenHeight(
6),
),
child: Text(
certifProv.name!.toUpperCase(),
textAlign: TextAlign.left,
style: primaryTextStyle.copyWith(
fontFamily: 'Arial',
fontWeight: FontWeight.bold,
fontSize:
getProportionateScreenWidth(
9),
color: Color.fromARGB(255, 248, 124, 0),
),
),
),
),
),
Positioned.fill(
child: FractionallySizedBox(
alignment: Alignment.bottomCenter,
widthFactor: 1.0,
heightFactor: 0.5,
child: Padding(
padding: EdgeInsets.only(
right:
getProportionateScreenWidth(
85),
left: 27.5),
child: Text('"${certifProv.title}"',
style: primaryTextStyle.copyWith(
fontFamily: 'Arial',
fontWeight: FontWeight.bold,
fontSize: certifProv
.title!.length >
40
? getProportionateScreenWidth(
9)
: getProportionateScreenWidth(
11),
color: Color.fromARGB(255, 248, 124, 0)),
textAlign: TextAlign.left,
),
),
),
),
Positioned.fill(
child: FractionallySizedBox(
alignment: Alignment.bottomLeft,
widthFactor: 0.299,
heightFactor: 0.62,
child: Center(
child: Padding(
padding: EdgeInsets.only(
bottom:
getProportionateScreenHeight(
2),
left: 5),
child: Text(
'Jakarta, ${DateFormat('dd MMMM yyyy ').format(DateTime.fromMillisecondsSinceEpoch(certifProv.finishDate * 1000))}',
style: primaryTextStyle.copyWith(
fontWeight:
FontWeight.normal,
fontSize:
getProportionateScreenWidth(
4),
color: Color.fromARGB(255, 0, 0, 0)),
),
),
),
),
),
Positioned.fill(
child: FractionallySizedBox(
alignment: Alignment.bottomRight,
// widthFactor
heightFactor: 0.495,
child: Padding(
padding: EdgeInsets.only(
right: 19
),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.end,
children: [
QrImageView(
data: "https://vocasia.id/?no-certificate=${certifProv.certificateNo}",
version: QrVersions.auto,
size: getProportionateScreenHeight(48),
gapless: false,),
],
),
),
),
),
Positioned.fill(
child: FractionallySizedBox(
alignment: Alignment.bottomRight,
// widthFactor
heightFactor: 0.18,
child: Padding(
padding: EdgeInsets.only(
right: 25
),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.end,
children: [
RichText(
text:
TextSpan(
text:
'Issued : ${DateFormat('dd MMMM yyyy ').format(DateTime.fromMillisecondsSinceEpoch(certifProv.finishDate * 1000))}',
style: primaryTextStyle
.copyWith(
fontWeight:
FontWeight.normal,
fontSize:
getProportionateScreenWidth(
3),
color:
Color(0xff575553),
),
),
)
],
),
),
),
),
],
);
},
),
),
),
),
],
);
} else if (state.state == certifProvider.ResultState.Error) {
return Center(
child: Column(
children: [
Text(
'Terjadi Kesalahan Coba Lagi',
style: thirdTextStyle,
),
],
),
);
} else {
return Center(
child: Text(
'Terjadi kesalahan',
style: thirdTextStyle,
),
);
}
},
),
),
),
),
);
}
Widget notFinish() {
return SafeArea(
child: Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.background,
centerTitle: true,
title: Text(
'Sertifikat',
style: thirdTextStyle.copyWith(
letterSpacing: 0.23,
fontWeight: semiBold,
fontSize: getProportionateScreenWidth(14),
),
),
),
body: Center(
child: Container(
margin: EdgeInsets.only(
left: getProportionateScreenWidth(16),
right: getProportionateScreenWidth(16),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Sertifikat',
textAlign: TextAlign.center,
style: thirdTextStyle.copyWith(
fontWeight: reguler,
fontSize: getProportionateScreenWidth(28),
),
),
Text(
'Belum Tersedia',
textAlign: TextAlign.center,
style: thirdTextStyle.copyWith(
fontWeight: reguler,
fontSize: getProportionateScreenWidth(28),
),
),
SizedBox(height: getProportionateScreenHeight(24)),
Stack(
children: [
Container(
width: double.infinity,
height: getProportionateScreenWidth(20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.white,
),
),
Container(
width: (SizeConfig.screenWidth -
getProportionateScreenWidth(32)) *
(totalProgress ?? 0) /
100,
height: getProportionateScreenWidth(20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: primaryColor,
),
child: Text(
'${totalProgress ?? 0}%',
textAlign: TextAlign.center,
style: primaryTextStyle.copyWith(
fontSize: getProportionateScreenWidth(12),
fontWeight: reguler,
color: baruTextutih,
),
),
),
],
),
SizedBox(height: getProportionateScreenHeight(16)),
Text(
'Anda baru menyelesaikan ${totalProgress ?? 0}% kursus',
textAlign: TextAlign.center,
style: thirdTextStyle.copyWith(
fontWeight: reguler,
fontSize: getProportionateScreenWidth(13),
),
),
SizedBox(height: getProportionateScreenHeight(16)),
Text(
'Anda belum memenuhi persyaratan untuk mendapatkan sertfikat. Selesaikan kursus anda untuk mendapatkan sertifikat penyelesaian kursus.',
textAlign: TextAlign.center,
style: thirdTextStyle.copyWith(
fontWeight: reguler,
fontSize: getProportionateScreenWidth(13),
),
),
],
),
),
),
),
);
}
Widget checkCertificateFinish() {
return SafeArea(
child: Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.background,
centerTitle: true,
title: Text(
'Sertifikat',
style: thirdTextStyle.copyWith(
fontWeight: semiBold,
fontSize: getProportionateScreenWidth(14),
),
)),
body: Center(
child: Container(
margin: EdgeInsets.only(
left: getProportionateScreenWidth(16),
right: getProportionateScreenWidth(16),
),
child: Consumer<certifProvider.CertificateProvider>(
builder: (context, state, _) {
if (state.state == certifProvider.ResultState.Loading) {
return Center(
child: CircularProgressIndicator(
strokeWidth: 2,
color: primaryColor,
),
);
} else if (state.state ==
certifProvider.ResultState.HasData) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
margin: EdgeInsets.all(15),
width: double.infinity,
height: 280,
child: LayoutBuilder(
builder: (context, constraint) {
return Stack(
alignment: Alignment.center,
children: [
Image.asset(
'assets/images/certif_template_new.png'),
Positioned(
top: constraint.maxHeight / 2.55,
left: 0,
right: 0,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 25),
child: Text(
certifProv.name!,
textAlign: TextAlign.center,
style: primaryTextStyle.copyWith(
fontWeight: FontWeight.bold,
fontSize: 14,
color: Color(0xff575553)),
),
),
),
Positioned(
top: constraint.maxHeight / 1.9,
left: 0,
right: 0,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 33),
child: Text('"${certifProv.title}"',
style: primaryTextStyle.copyWith(
fontWeight: FontWeight.bold,
fontSize:
(certifProv.title!.length >
20)
? 9
: 12,
color: Color(0xff575553)),
textAlign: TextAlign.center),
),
),
Positioned(
top: constraint.maxHeight / 1.6,
left: 0,
right: 0,
child: Center(
child: Text(
'${DateFormat('dd MMMM yyyy ').format(DateTime.fromMillisecondsSinceEpoch(certifProv.finishDate * 1000))}',
style: primaryTextStyle.copyWith(
fontWeight: FontWeight.normal,
fontSize: 8,
color: Color(0xff575553)),
),
),
),
Positioned(
bottom: constraint.maxHeight / 5.6,
left: constraint.maxWidth / 10,
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
'Certificate no : ${certifProv.certificateNo}',
style: primaryTextStyle.copyWith(
fontWeight: FontWeight.normal,
fontSize: 4,
color: Color(0xff575553),
),
),
Text(
'Certificate URL : https://vocasia-v4-develop.vercel.app/certificate/${certifProv.idPayment}-VOCASIA-${certifProv.certificateNo}',
style: primaryTextStyle.copyWith(
fontWeight: FontWeight.normal,
fontSize: 4,
color: Color(0xff575553),
),
),
],
),
)
],
);
},
),
),
],
);
} else if (state.state == certifProvider.ResultState.Error) {
return Center(
child: Column(
children: [
Text(
'Terjadi Kesalahan Coba Lagi',
style: thirdTextStyle,
),
],
),
);
} else {
return Center(
child: Text(
'Terjadi kesalahan',
style: thirdTextStyle,
),
);
}
},
),
),
),
),
);
}
Widget checkCertificateNotFinished() {
return SafeArea(
child: Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.background,
centerTitle: true,
title: Text(
'Sertifikat',
style: thirdTextStyle.copyWith(
fontWeight: semiBold,
fontSize: getProportionateScreenWidth(14),
),
),
),
body: Center(
child: Text(
'Sertifikat Belum Diterbitkan',
textAlign: TextAlign.center,
style: thirdTextStyle.copyWith(
fontWeight: reguler,
fontSize: getProportionateScreenWidth(20),
),
),
),
),
);
}
Widget buildCertificate() {
return Container(
color: Colors.red,
margin: EdgeInsets.all(15),
width: double.infinity,
height: 300,
child: Stack(
alignment: Alignment.center,
children: [
Image.asset('assets/certif_template_new.png'),
Positioned(
top: 120,
left: 0,
right: 0,
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: getProportionateScreenHeight(25)),
child: Text(
'Safinatun Najah Unju',
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: getProportionateScreenWidth(14)),
),
),
),
Positioned(
top: 155,
left: 0,
right: 0,
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: getProportionateScreenHeight(33)),
child: Column(
children: [
Text(
'"Basic Excel Trainig For Professional Employees & Recruitment Selection Candidates"',
style: TextStyle(
fontWeight: FontWeight.w600, fontSize: 12),
textAlign: TextAlign.center),
SizedBox(height: getProportionateScreenHeight(10)),
Text(
'Jakarta, 24 October 2021',
style: TextStyle(
fontWeight: FontWeight.normal,
fontSize: getProportionateScreenWidth(8)),
)
],
),
),
),
Positioned(
bottom: getProportionateScreenHeight(50),
left: (65 / 640) * MediaQuery.of(context).size.width,
child: Column(
children: [
Text(
'Certificate no : 257911829183',
style: TextStyle(
fontWeight: FontWeight.normal,
fontSize: getProportionateScreenWidth(6)),
),
Text(
'Certificate no : 257911829183',
style: TextStyle(
fontWeight: FontWeight.normal,
fontSize: getProportionateScreenWidth(6)),
),
],
),
)
],
),
);
}
if (totalProgress == 100 && idCourse != null) {
return finish();
} else if (totalProgress != 100 && idCourse != null) {
return finish();
} else if (finishDate != false) {
return checkCertificateFinish();
} else {
return checkCertificateNotFinished();
}
}
}