last63

AttendancePage

Mar 10th, 2025
59
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.53 KB | None | 0 0
  1. import 'dart:io';
  2.  
  3. import 'package:camera/camera.dart';
  4. import 'package:cloud_firestore/cloud_firestore.dart';
  5. import 'package:dotted_border/dotted_border.dart';
  6. import 'package:firebase_auth/firebase_auth.dart';
  7. import 'package:firebase_test/ui/ui/attedance/attendance/camera_page.dart';
  8. import 'package:firebase_test/ui/ui/attedance/main_page.dart';
  9. import 'package:flutter/material.dart';
  10. import 'package:geocoding/geocoding.dart';
  11. import 'package:geolocator/geolocator.dart';
  12. import 'package:intl/intl.dart';
  13.  
  14. class AttendancePage extends StatefulWidget {
  15. final XFile? image;
  16. const AttendancePage({super.key, this.image});
  17.  
  18. @override
  19. State<AttendancePage> createState() => _AttendancePageState();
  20. }
  21.  
  22. class _AttendancePageState extends State<AttendancePage> {
  23. XFile? image;
  24. String strAddress = '';
  25. String strDate = '';
  26. String strTime = '';
  27. String strDateTime = '';
  28. String strStatus = 'Attendance';
  29. bool isLoading = false;
  30. int dateHour = 0;
  31. int dateMinute = 0;
  32. double dLat = 0.0;
  33. double dLong = 0.0;
  34. final controllerName = TextEditingController();
  35. final FirebaseFirestore firestore = FirebaseFirestore.instance;
  36. String userId = FirebaseAuth.instance.currentUser?.uid ?? "unknown";
  37.  
  38.  
  39. @override
  40. void initState() {
  41. super.initState();
  42.  
  43. fetchUserName();
  44.  
  45. image = widget.image;
  46. setDateTime();
  47. setStatusAbsent();
  48.  
  49. if (image != null) {
  50. isLoading = true;
  51. getGeoLocationPosition();
  52. }
  53. }
  54.  
  55. Future<void> fetchUserName() async {
  56. String userId = FirebaseAuth.instance.currentUser?.uid ?? "unknown";
  57.  
  58. if (userId == "unknown") return;
  59.  
  60. try {
  61. DocumentSnapshot userDoc =
  62. await FirebaseFirestore.instance.collection('users').doc(userId).get();
  63.  
  64. if (userDoc.exists) {
  65. String firstName = userDoc['first_name'] ?? '';
  66. String lastName = userDoc['last_name'] ?? '';
  67. String fullName = '$firstName $lastName'.trim(); // Menggabungkan nama
  68.  
  69. setState(() {
  70. controllerName.text = fullName; // Mengisi field dengan nama pengguna
  71. });
  72. }
  73. } catch (e) {
  74. print("Error mengambil nama pengguna: $e");
  75. }
  76. }
  77.  
  78. Future<void> submitAbsen(String alamat, String nama, String status) async {
  79.  
  80. String userId = FirebaseAuth.instance.currentUser?.uid ?? "unknown";
  81. print("User ID: $userId"); // Debugging
  82.  
  83. if (userId == "unknown") {
  84. print("Error: User ID not found");
  85. return;
  86. }
  87.  
  88. DocumentReference userDocRef = firestore.collection('users').doc(userId);
  89. CollectionReference attendanceCollection = userDocRef.collection('attendance');
  90.  
  91. if (nama.isEmpty) {
  92. ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
  93. content: Text("Nama tidak boleh kosong!"),
  94. backgroundColor: Colors.red,
  95. ));
  96. return;
  97. }
  98.  
  99. showLoaderDialog(context);
  100.  
  101. try {
  102. await attendanceCollection.add({
  103. 'address': alamat,
  104. 'name': nama,
  105. 'description': status,
  106. 'datetime': strDateTime,
  107. 'createdAt': FieldValue.serverTimestamp(),
  108. });
  109.  
  110. Navigator.of(context).pop();
  111. ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
  112. content: Row(
  113. children: [
  114. Icon(Icons.check_circle_outline, color: Colors.white),
  115. SizedBox(width: 10),
  116. Text("Yeay! Attendance Report Succeeded!", style: TextStyle(color: Colors.white)),
  117. ],
  118. ),
  119. backgroundColor: Colors.orangeAccent,
  120. behavior: SnackBarBehavior.floating,
  121. ));
  122.  
  123. Navigator.pushReplacement(context, MaterialPageRoute(
  124. builder: (context) => const MainPage()));
  125. } catch (e) {
  126. Navigator.of(context).pop();
  127. ScaffoldMessenger.of(context).showSnackBar(SnackBar(
  128. content: Row(
  129. children: [
  130. const Icon(Icons.error_outline, color: Colors.white),
  131. const SizedBox(width: 10),
  132. Expanded(child: Text("Ups, $e", style: const TextStyle(color: Colors.white))),
  133. ],
  134. ),
  135. backgroundColor: Colors.blueGrey,
  136. behavior: SnackBarBehavior.floating,
  137. ));
  138. }
  139. }
  140.  
  141. Future<void> getGeoLocationPosition() async {
  142. Position position = await Geolocator
  143. .getCurrentPosition(desiredAccuracy: LocationAccuracy.low);
  144. setState(() {
  145. isLoading = false;
  146. getAddressFromLongLat(position);
  147. });
  148. }
  149.  
  150. Future<void> getAddressFromLongLat(Position position) async {
  151. List<Placemark> placemarks = await placemarkFromCoordinates(
  152. position.latitude,
  153. position.longitude);
  154. Placemark place = placemarks[0];
  155.  
  156. setState(() {
  157. dLat = position.latitude;
  158. dLong = position.longitude;
  159. strAddress =
  160. "${place.street}, ${place.subLocality}, ${place.locality}, ${place.postalCode}, ${place.country}";
  161. });
  162. }
  163.  
  164. Future<bool> handleLocationPermission() async {
  165. bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
  166. if (!serviceEnabled) {
  167. showLocationError("Location services are disabled."
  168. " Please enable the services.");
  169. return false;
  170. }
  171.  
  172. LocationPermission permission = await Geolocator.checkPermission();
  173. if (permission == LocationPermission.denied) {
  174. permission = await Geolocator.requestPermission();
  175. if (permission == LocationPermission.denied) {
  176. showLocationError("Location permission denied.");
  177. return false;
  178. }
  179. }
  180.  
  181. if (permission == LocationPermission.deniedForever) {
  182. showLocationError("Location permission denied forever, we cannot access.");
  183. return false;
  184. }
  185. return true;
  186. }
  187.  
  188. void showLocationError(String message) {
  189. ScaffoldMessenger.of(context).showSnackBar(SnackBar(
  190. content: Row(
  191. children: [
  192. const Icon(Icons.location_off, color: Colors.white),
  193. const SizedBox(width: 10),
  194. Text(message, style: const TextStyle(color: Colors.white)),
  195. ],
  196. ),
  197. backgroundColor: Colors.blueGrey,
  198. behavior: SnackBarBehavior.floating,
  199. ));
  200. }
  201.  
  202. void showLoaderDialog(BuildContext context) {
  203. AlertDialog alert = AlertDialog(
  204. content: Row(
  205. children: [
  206. const CircularProgressIndicator(valueColor: AlwaysStoppedAnimation<Color>(
  207. Colors.blueGrey)),
  208. Container(
  209. margin: const EdgeInsets.only(left: 20),
  210. child: const Text("Checking the data..."),
  211. ),
  212. ],
  213. ),
  214. );
  215. showDialog(
  216. barrierDismissible: false,
  217. context: context,
  218. builder: (BuildContext context) {
  219. return alert;
  220. },
  221. );
  222. }
  223.  
  224. void setDateTime() {
  225. var dateNow = DateTime.now();
  226. var dateFormat = DateFormat('dd MMMM yyyy');
  227. var dateTimeFormat = DateFormat('HH:mm:ss');
  228. var dateHourFormat = DateFormat('HH');
  229. var dateMinuteFormat = DateFormat('mm');
  230.  
  231. setState(() {
  232. strDate = dateFormat.format(dateNow);
  233. strTime = dateTimeFormat.format(dateNow);
  234. strDateTime = "$strDate | $strTime";
  235.  
  236. dateHour = int.parse(dateHourFormat.format(dateNow));
  237. dateMinute = int.parse(dateMinuteFormat.format(dateNow));
  238. });
  239. }
  240.  
  241. void setStatusAbsent() {
  242. if (dateHour < 8 || (dateHour == 8 && dateMinute <= 30)) {
  243. strStatus = 'Attendance';
  244. } else if (dateHour < 18) {
  245. strStatus = 'Late';
  246. } else {
  247. strStatus = 'Absent';
  248. }
  249. }
  250.  
  251. @override
  252. Widget build(BuildContext context) {
  253.  
  254. Size size = MediaQuery.of(context).size;
  255.  
  256. return Scaffold(
  257. backgroundColor: Colors.white,
  258. appBar: AppBar(
  259. elevation: 0,
  260. backgroundColor: Colors.blueAccent,
  261. leading: IconButton(
  262. icon: const Icon(Icons.arrow_back_ios, color: Colors.white),
  263. onPressed: () => Navigator.of(context).pop(),
  264. ),
  265. title: const Text(
  266. "Attendance Menu",
  267. style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white),
  268. ),
  269. ),
  270. body: SingleChildScrollView(
  271. child: Card(
  272. color: Colors.white,
  273. margin: const EdgeInsets.fromLTRB(10, 10, 10, 30),
  274. shape: RoundedRectangleBorder(
  275. borderRadius: BorderRadius.circular(10),
  276. ),
  277. elevation: 5,
  278. child: Column(
  279. crossAxisAlignment: CrossAxisAlignment.start,
  280. children: [
  281. Container(
  282. height: 50,
  283. decoration: const BoxDecoration(
  284. borderRadius: BorderRadius.only(
  285. topLeft: Radius.circular(10),
  286. topRight: Radius.circular(10)),
  287. color: Colors.blueAccent,
  288. ),
  289. child: const Row(
  290. children: [
  291. SizedBox(
  292. width: 12,
  293. ),
  294. Icon(Icons.face_retouching_natural_outlined, color: Colors.white),
  295. SizedBox(
  296. width: 12,
  297. ),
  298. Text(
  299. "Please make a selfie photo!",
  300. style: TextStyle(
  301. fontSize: 18,
  302. fontWeight: FontWeight.bold,
  303. color: Colors.white),
  304. ),
  305. ],
  306. ),
  307. ),
  308. const Padding(
  309. padding: EdgeInsets.fromLTRB(10, 20, 0, 20),
  310. child: Text(
  311. "Capture Photo",
  312. style: TextStyle(
  313. fontSize: 16,
  314. fontWeight: FontWeight.bold,
  315. color: Colors.black),
  316. ),
  317. ),
  318. GestureDetector(
  319. onTap: () {
  320. Navigator.push(
  321. context,
  322. MaterialPageRoute(builder: (context) => const CameraPage()));
  323. },
  324. child: Container(
  325. margin: const EdgeInsets.fromLTRB(10, 0, 10, 20),
  326. width: size.width,
  327. height: 150,
  328. child: DottedBorder(
  329. radius: const Radius.circular(10),
  330. borderType: BorderType.RRect,
  331. color: Colors.blueAccent,
  332. strokeWidth: 1,
  333. dashPattern: const [5, 5],
  334. child: SizedBox.expand(
  335. child: FittedBox(
  336. child: image != null
  337. ? Image.file(File(image!.path), fit: BoxFit.cover)
  338. : const Icon(
  339. Icons.camera_enhance_outlined,
  340. color: Colors.blueAccent,
  341. ),
  342. ),
  343. ),
  344. ),
  345. ),
  346. ),
  347. Padding(
  348. padding: const EdgeInsets.all(10),
  349. child: TextField(
  350. enabled: false,
  351. textInputAction: TextInputAction.done,
  352. keyboardType: TextInputType.text,
  353. controller: controllerName,
  354. decoration: InputDecoration(
  355. contentPadding: const EdgeInsets.symmetric(horizontal: 10),
  356. labelText: "Your Name",
  357. hintText: "Please enter your name",
  358. hintStyle: const TextStyle(
  359. fontSize: 14,
  360. color: Colors.grey),
  361. labelStyle: const TextStyle(
  362. fontSize: 14,
  363. color: Colors.black),
  364. border: OutlineInputBorder(
  365. borderRadius: BorderRadius.circular(10),
  366. borderSide: const BorderSide(color: Colors.blueAccent),
  367. ),
  368. enabledBorder: OutlineInputBorder(
  369. borderRadius: BorderRadius.circular(10),
  370. borderSide: const BorderSide(color: Colors.blueAccent),
  371. ),
  372. ),
  373. ),
  374. ),
  375. const Padding(
  376. padding: EdgeInsets.fromLTRB(10, 10, 10, 10),
  377. child: Text(
  378. "Your Location",
  379. style: TextStyle(
  380. fontSize: 16,
  381. fontWeight: FontWeight.bold,
  382. color: Colors.black),
  383. ),
  384. ),
  385. isLoading
  386. ? const Center(child: CircularProgressIndicator(color: Colors.blueAccent))
  387. : Padding(
  388. padding: const EdgeInsets.all(10),
  389. child: SizedBox(
  390. height: 5 * 24,
  391. child: TextField(
  392. enabled: false,
  393. maxLines: 5,
  394. decoration: InputDecoration(
  395. alignLabelWithHint: true,
  396. disabledBorder: OutlineInputBorder(
  397. borderRadius: BorderRadius.circular(10),
  398. borderSide: const BorderSide(color: Colors.blueAccent),
  399. ),
  400. hintText: strAddress != null
  401. ? strAddress
  402. : strAddress = 'Your Location',
  403. hintStyle: const TextStyle(
  404. fontSize: 14, color: Colors.grey),
  405. fillColor: Colors.transparent,
  406. filled: true,
  407. ),
  408. ),
  409. ),
  410. ),
  411. Container(
  412. alignment: Alignment.center,
  413. margin: const EdgeInsets.all(30),
  414. child: Material(
  415. elevation: 3,
  416. borderRadius: BorderRadius.circular(20),
  417. child: Container(
  418. width: size.width,
  419. height: 50,
  420. decoration: BoxDecoration(
  421. borderRadius: BorderRadius.circular(20),
  422. color: Colors.white),
  423. child: Material(
  424. borderRadius: BorderRadius.circular(20),
  425. color: Colors.blueAccent,
  426. child: InkWell(
  427. splashColor: Colors.blue,
  428. borderRadius: BorderRadius.circular(20),
  429. onTap: () {
  430. if (image == null || controllerName.text.isEmpty) {
  431. ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
  432. content: Row(
  433. children: [
  434. Icon(
  435. Icons.info_outline,
  436. color: Colors.white,
  437. ),
  438. SizedBox(width: 10),
  439. Text("Please Fill all the forms!",
  440. style: TextStyle(color: Colors.white))
  441. ],
  442. ),
  443. backgroundColor: Colors.blueGrey,
  444. shape: StadiumBorder(),
  445. behavior: SnackBarBehavior.floating,
  446. ));
  447. } else {
  448. submitAbsen(strAddress, controllerName.text.toString(), strStatus);
  449. }
  450. },
  451. child: const Center(
  452. child: Text(
  453. "Report Now",
  454. style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
  455. ),
  456. ),
  457. ),
  458. ),
  459. ),
  460. )),
  461. ],
  462. )),
  463. ),
  464. );
  465. }
  466. }
  467.  
Advertisement
Add Comment
Please, Sign In to add comment