Initial commit: Penyerahan final Source code Tugas Akhir
This commit is contained in:
134
lib/screens/course/component/announcement.dart
Normal file
134
lib/screens/course/component/announcement.dart
Normal 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();
|
||||
}
|
||||
}
|
710
lib/screens/course/component/detail_play_course.dart
Normal file
710
lib/screens/course/component/detail_play_course.dart
Normal 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'));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
400
lib/screens/course/component/detail_quest_and_answer.dart
Normal file
400
lib/screens/course/component/detail_quest_and_answer.dart
Normal 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,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
|
32
lib/screens/course/component/download_certificate.dart
Normal file
32
lib/screens/course/component/download_certificate.dart
Normal 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'))
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
487
lib/screens/course/component/expansion_tile_copy.dart
Normal file
487
lib/screens/course/component/expansion_tile_copy.dart
Normal 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,
|
||||
);
|
||||
}
|
||||
}
|
308
lib/screens/course/component/inside_announcement.dart
Normal file
308
lib/screens/course/component/inside_announcement.dart
Normal 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),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
53
lib/screens/course/component/pdfReader.dart
Normal file
53
lib/screens/course/component/pdfReader.dart
Normal 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,
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
338
lib/screens/course/component/quest_and_answer.dart
Normal file
338
lib/screens/course/component/quest_and_answer.dart
Normal 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();
|
||||
}
|
||||
}
|
46
lib/screens/course/component/txtReader.dart
Normal file
46
lib/screens/course/component/txtReader.dart
Normal 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,
|
||||
)));
|
||||
}
|
||||
}
|
79
lib/screens/course/html5_video_page.dart
Normal file
79
lib/screens/course/html5_video_page.dart
Normal 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();
|
||||
}
|
||||
}
|
198
lib/screens/course/my_course_page.dart
Normal file
198
lib/screens/course/my_course_page.dart
Normal 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(),
|
||||
);
|
||||
}
|
||||
}
|
1974
lib/screens/course/play_course_page.dart
Normal file
1974
lib/screens/course/play_course_page.dart
Normal file
File diff suppressed because it is too large
Load Diff
2022
lib/screens/course/play_course_page_old.dart
Normal file
2022
lib/screens/course/play_course_page_old.dart
Normal file
File diff suppressed because it is too large
Load Diff
2075
lib/screens/course/play_course_page_old_2.dart
Normal file
2075
lib/screens/course/play_course_page_old_2.dart
Normal file
File diff suppressed because it is too large
Load Diff
161
lib/screens/course/quiz_page.dart
Normal file
161
lib/screens/course/quiz_page.dart
Normal 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);
|
||||
},
|
||||
))
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
225
lib/screens/course/quiz_question_page.dart
Normal file
225
lib/screens/course/quiz_question_page.dart
Normal 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"<[^>]*>| ", 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);
|
||||
},
|
||||
))
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
500
lib/screens/course/quiz_result_page.dart
Normal file
500
lib/screens/course/quiz_result_page.dart
Normal 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"<[^>]*>| ", 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);
|
||||
}
|
||||
}
|
90
lib/screens/course/search_my_course_page.dart
Normal file
90
lib/screens/course/search_my_course_page.dart
Normal 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(''));
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
784
lib/screens/course/sertif.dart
Normal file
784
lib/screens/course/sertif.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
676
lib/screens/course/sertif_view.dart
Normal file
676
lib/screens/course/sertif_view.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user