step 11 : proses base64 string, upload ke BE
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
import 'package:camera/camera.dart';
|
||||
@@ -8,6 +9,9 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:image/image.dart' as img;
|
||||
|
||||
import '../../app/constant.dart';
|
||||
import '../../provider/current_user_provider.dart';
|
||||
import '../../widget/customsnackbarwidget.dart';
|
||||
import 'upload_scan_provider.dart';
|
||||
|
||||
class ScanScreen extends HookConsumerWidget {
|
||||
const ScanScreen({super.key});
|
||||
@@ -23,7 +27,9 @@ class ScanScreen extends HookConsumerWidget {
|
||||
final capturedImage = useState<XFile?>(null);
|
||||
final croppedImage = useState<File?>(null);
|
||||
final isLoading = useState<bool>(false);
|
||||
final isModalOpen = useState<bool>(false);
|
||||
final isLoadingUpload = useState<bool>(false);
|
||||
final currentUser = ref.watch(currentUserProvider);
|
||||
final host = currentUser?.host ?? "";
|
||||
|
||||
useEffect(() {
|
||||
Future<void> initializeCamera() async {
|
||||
@@ -86,122 +92,6 @@ class ScanScreen extends HookConsumerWidget {
|
||||
return rotatedFile;
|
||||
}
|
||||
|
||||
void showResultModal(BuildContext context) {
|
||||
isModalOpen.value = true;
|
||||
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
enableDrag: false,
|
||||
backgroundColor: Colors.white,
|
||||
builder: (context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Header dengan tombol close
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Hasil Foto",
|
||||
style: Constant.cardText(context: context).copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 18,
|
||||
color: Constant.textBlack,
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.close, color: Colors.black),
|
||||
onPressed: () {
|
||||
isModalOpen.value = false;
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Gambar hasil foto
|
||||
if (croppedImage.value != null)
|
||||
Container(
|
||||
width: double.infinity,
|
||||
height: 300,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Image.file(
|
||||
croppedImage.value!,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Tombol Hapus dan Upload
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
// Tombol Hapus
|
||||
ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
capturedImage.value = null;
|
||||
croppedImage.value = null;
|
||||
isModalOpen.value = false;
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
icon: const Icon(Icons.delete,
|
||||
size: 17, color: Colors.white),
|
||||
label: Text(
|
||||
'Hapus',
|
||||
style: Constant.cardText(context: context).copyWith(
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Constant.textRed,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
elevation: 8,
|
||||
),
|
||||
),
|
||||
|
||||
// Tombol Upload
|
||||
ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
print('upload baru');
|
||||
},
|
||||
icon: const Icon(Icons.upload,
|
||||
size: 17, color: Colors.white),
|
||||
label: Text(
|
||||
'Upload',
|
||||
style: Constant.cardText(context: context).copyWith(
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.green,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
elevation: 8,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
).whenComplete(() => isModalOpen.value = false);
|
||||
}
|
||||
|
||||
Future<void> captureAndCropImage() async {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
@@ -218,7 +108,13 @@ class ScanScreen extends HookConsumerWidget {
|
||||
capturedImage.value = image;
|
||||
croppedImage.value = rotatedImage;
|
||||
|
||||
showResultModal(context);
|
||||
// post ke BE
|
||||
Uint8List bytes = await croppedImage.value!.readAsBytes();
|
||||
String base64String = base64Encode(bytes);
|
||||
ref.read(uploadScanProvider.notifier).uploadScan(
|
||||
host: host,
|
||||
base64File: base64String,
|
||||
);
|
||||
} catch (e) {
|
||||
print("Error capturing image: $e");
|
||||
} finally {
|
||||
@@ -226,7 +122,66 @@ class ScanScreen extends HookConsumerWidget {
|
||||
}
|
||||
}
|
||||
|
||||
// proses upload
|
||||
ref.listen(uploadScanProvider, (prev, next) {
|
||||
if (next is UploadScanStateLoading) {
|
||||
isLoadingUpload.value = true;
|
||||
} else if (next is UploadScanStateError) {
|
||||
isLoadingUpload.value = false;
|
||||
// errorMessage.value = next.message;
|
||||
debugPrint(next.message);
|
||||
snackbarWidget(
|
||||
context,
|
||||
next.message,
|
||||
snackbarType.error,
|
||||
Duration(seconds: 3),
|
||||
);
|
||||
} else if (next is UploadScanStateDone) {
|
||||
isLoadingUpload.value = false;
|
||||
snackbarWidget(
|
||||
context,
|
||||
next.pesan,
|
||||
snackbarType.success,
|
||||
Duration(seconds: 3),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// loading proses upload useEffect
|
||||
useEffect(() {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (isLoadingUpload.value == true) {
|
||||
snackbarWidget(
|
||||
context,
|
||||
'Sedang Upload Foto...',
|
||||
snackbarType.warning,
|
||||
Duration(seconds: 3),
|
||||
);
|
||||
}
|
||||
});
|
||||
return () {};
|
||||
}, [isLoadingUpload.value]);
|
||||
|
||||
// loading proses image useEffect
|
||||
useEffect(() {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (isLoading.value == true) {
|
||||
snackbarWidget(
|
||||
context,
|
||||
'Sedang Proses Gambar...',
|
||||
snackbarType.warning,
|
||||
Duration(seconds: 3),
|
||||
);
|
||||
}
|
||||
});
|
||||
return () {};
|
||||
}, [isLoading.value]);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Posisi Foto Landscape'),
|
||||
automaticallyImplyLeading: false,
|
||||
),
|
||||
backgroundColor: Colors.black.withOpacity(0.5),
|
||||
body: Stack(
|
||||
children: [
|
||||
@@ -269,6 +224,8 @@ class ScanScreen extends HookConsumerWidget {
|
||||
children: [
|
||||
ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
capturedImage.value = null;
|
||||
croppedImage.value = null;
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
icon: Icon(
|
||||
|
||||
Reference in New Issue
Block a user