step 11 : proses base64 string, upload ke BE

This commit is contained in:
sindhu
2025-02-17 06:52:04 +07:00
parent d6bdf1a76c
commit 0c318136e9
5 changed files with 558 additions and 118 deletions

View File

@@ -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(