TTK_E32222585_flutter/lib/checkout_photo_screen.dart

223 lines
7.4 KiB
Dart

import 'dart:convert';
import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:http/http.dart' as http;
import 'package:image_picker/image_picker.dart';
class CheckoutPhotoScreen extends StatefulWidget {
final String token;
final Position position;
const CheckoutPhotoScreen({
Key? key,
required this.token,
required this.position,
}) : super(key: key);
@override
State<CheckoutPhotoScreen> createState() => _CheckoutPhotoScreenState();
}
class _CheckoutPhotoScreenState extends State<CheckoutPhotoScreen> {
File? _photo;
Uint8List? _webPhotoBytes;
String? _webPhotoName;
bool _isLoading = false;
Future<void> _pickPhoto() async {
try {
if (kIsWeb) {
final result = await FilePicker.platform.pickFiles(
type: FileType.image,
);
if (result != null && result.files.single.bytes != null) {
setState(() {
_webPhotoBytes = result.files.single.bytes;
_webPhotoName = result.files.single.name;
});
}
} else {
final picker = ImagePicker();
final picked = await picker.pickImage(
source: ImageSource.camera,
imageQuality: 70,
);
if (picked != null) {
setState(() {
_photo = File(picked.path);
});
}
}
} catch (e) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('Gagal mengambil foto: $e')));
}
}
bool _isPhotoReady() {
if (kIsWeb) {
return _webPhotoBytes != null && _webPhotoName != null;
} else {
return _photo != null;
}
}
Future<void> _submitCheckout() async {
if (!_isPhotoReady()) return;
setState(() => _isLoading = true);
final token = widget.token;
final uri = Uri.parse('http://localhost:8000/api/employee/attendance');
final request =
http.MultipartRequest('POST', uri)
..headers['Authorization'] = 'Bearer $token'
..fields['type'] = 'out'
..fields['latitude'] = widget.position.latitude.toString()
..fields['longitude'] = widget.position.longitude.toString();
if (kIsWeb && _webPhotoBytes != null && _webPhotoName != null) {
request.files.add(
await http.MultipartFile.fromBytes(
'photo',
_webPhotoBytes!,
filename: _webPhotoName,
),
);
} else if (_photo != null) {
request.files.add(
await http.MultipartFile.fromPath('photo', _photo!.path),
);
}
try {
final streamed = await request.send();
final response = await http.Response.fromStream(streamed);
setState(() => _isLoading = false);
final data = jsonDecode(response.body);
if (response.statusCode == 200) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(data['message'] ?? 'Check-out berhasil!')),
);
Navigator.popUntil(context, (route) => route.isFirst);
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(data['message'] ?? 'Check-out gagal!')),
);
}
} catch (e) {
setState(() => _isLoading = false);
ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text('Terjadi kesalahan.')));
}
}
@override
Widget build(BuildContext context) {
final buttonStyle = ElevatedButton.styleFrom(
minimumSize: const Size.fromHeight(48),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
);
return Scaffold(
appBar: AppBar(
title: const Text('Check-out: Ambil Foto'),
centerTitle: true,
),
body: SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
children: [
const SizedBox(height: 24),
const Text(
'Pastikan wajah terlihat jelas tanpa objek menutupi, dan latar belakang cukup terang.',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 16),
),
const SizedBox(height: 24),
ElevatedButton.icon(
style: buttonStyle,
onPressed: _pickPhoto,
icon: const Icon(Icons.camera_alt),
label: Text(!_isPhotoReady() ? 'Ambil Foto' : 'Ubah Foto'),
),
const SizedBox(height: 16),
Expanded(
child: Center(
child:
_isPhotoReady()
? Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black26,
blurRadius: 6,
offset: Offset(0, 3),
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(12),
child:
kIsWeb
? Image.memory(
_webPhotoBytes!,
height: 250,
fit: BoxFit.cover,
)
: Image.file(
_photo!,
height: 250,
fit: BoxFit.cover,
),
),
)
: Column(
mainAxisSize: MainAxisSize.min,
children: const [
Icon(
Icons.photo_camera_outlined,
size: 80,
color: Colors.grey,
),
SizedBox(height: 8),
Text(
'Belum ada foto',
style: TextStyle(color: Colors.grey),
),
],
),
),
),
const SizedBox(height: 16),
SizedBox(
width: double.infinity,
child: ElevatedButton(
style: buttonStyle,
onPressed:
_isPhotoReady() && !_isLoading ? _submitCheckout : null,
child:
_isLoading
? const SizedBox(
height: 24,
width: 24,
child: CircularProgressIndicator(
color: Colors.white,
strokeWidth: 2,
),
)
: const Text('Check-out'),
),
),
const SizedBox(height: 16),
],
),
),
),
);
}
}