step 9 : clock in & clock out selfie sudah bisa, need fix at clear camera controller after take photo

This commit is contained in:
sindhu
2024-08-27 14:13:00 +07:00
parent 3806462d7b
commit a7d83956ec
5 changed files with 475 additions and 178 deletions

View File

@@ -1,33 +1,63 @@
import 'dart:convert';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as p;
import 'package:shared_preferences/shared_preferences.dart';
final imgPhotoWebProvider = StateProvider<String?>((ref) => "");
final cameraControllerProvider =
StateNotifierProvider<CameraControllerNotifier, CameraController?>(
(ref) => CameraControllerNotifier());
StateNotifierProvider<CameraControllerNotifier, CameraController?>((ref) {
return CameraControllerNotifier();
});
// StateNotifier to manage CameraController
class CameraControllerNotifier extends StateNotifier<CameraController?> {
CameraControllerNotifier() : super(null);
Future<void> initializeCamera(CameraDescription camera) async {
final controller = CameraController(camera, ResolutionPreset.medium);
await controller.initialize();
state = controller;
CameraControllerNotifier() : super(null) {
_initializeCamera();
}
Future<String> takePicture() async {
if (state == null || !state!.value.isInitialized) {
throw Exception('Camera is not initialized');
Future<void> _initializeCamera() async {
WidgetsFlutterBinding.ensureInitialized();
try {
final cameras = await availableCameras();
if (cameras.isNotEmpty) {
final controller = CameraController(cameras[0], ResolutionPreset.max);
await controller.initialize();
state = controller;
}
} catch (e) {
print('Error initializing camera: $e');
}
}
final path = p.join(
(await getTemporaryDirectory()).path,
'${DateTime.now()}.png',
);
Future<void> takePicture(
// ValueNotifier<XFile?> imageFile,
ValueNotifier<String> base64ImageString,
ValueNotifier<String> imagePath,
) async {
// final flag = ValueNotifier<bool>(false);
if (state != null) {
try {
final image = await state!.takePicture();
final bytes = await image.readAsBytes();
String base64Image = base64Encode(bytes);
await state!.takePicture();
return path;
// imageFile.value = image;
base64ImageString.value = base64Image;
imagePath.value = image.path;
// final shared = await SharedPreferences.getInstance();
// shared.setString("base64Image", base64Image);
} catch (e) {
print('Error taking picture: $e');
// flag.value = false;
// RespErr(flag: false, message: 'Error taking picture: $e');
}
} else {
print('CameraController is not initialized.');
}
}
@override

View File

@@ -1,90 +1,223 @@
import 'dart:convert';
import 'dart:io';
import 'package:absensi_sas/widget/sankbar_widget.dart';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
class CameraPage extends StatefulWidget {
const CameraPage({Key? key}) : super(key: key);
import '../../app/constant.dart';
import '../../provider/camera_controller_provider.dart';
class CameraPage extends HookConsumerWidget {
const CameraPage({
Key? key,
required this.filePathParam,
required this.fileDataBase64Param,
}) : super(key: key);
final ValueNotifier<String> filePathParam;
final ValueNotifier<String> fileDataBase64Param;
@override
State<CameraPage> createState() => _CameraPageState();
}
Widget build(BuildContext context, WidgetRef ref) {
final controller = ref.watch(cameraControllerProvider);
// final imagePath = useState<String?>("");
// final fileDataBase64 = useState<String?>("");
class _CameraPageState extends State<CameraPage> {
CameraController? controller;
String imagePath = "";
List<CameraDescription>? cameras;
// final fileData = useState<XFile?>(null);
final filePath = useState<String>("");
final fileDataBase64 = useState<String>("");
final cameraController = useState<CameraController?>(null);
@override
void initState() {
super.initState();
initializeCamera();
}
Future<void> initializeCamera() async {
// Initialize cameras
WidgetsFlutterBinding.ensureInitialized();
try {
cameras = await availableCameras();
if (cameras != null && cameras!.isNotEmpty) {
// Use the first available camera
controller = CameraController(cameras![0], ResolutionPreset.max);
await controller?.initialize();
if (mounted) {
setState(() {});
useEffect(() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
try {
final cameras = await availableCameras();
if (cameras.isNotEmpty) {
final controller =
CameraController(cameras[0], ResolutionPreset.max);
await controller.initialize();
cameraController.value = controller;
}
} catch (e) {
print('Error initializing camera: $e');
}
});
return () {};
}, []);
Future<void> initializeCamera() async {
try {
final cameras = await availableCameras();
if (cameras.isNotEmpty) {
final controller = CameraController(cameras[0], ResolutionPreset.max);
await controller.initialize();
cameraController.value = controller;
}
} catch (e) {
print('Error initializing camera: $e');
}
} catch (e) {
print('Error initializing camera: $e');
}
}
@override
void dispose() {
controller?.dispose();
super.dispose();
}
Future<void> takePicture(
// ValueNotifier<XFile?> imageFile,
ValueNotifier<String> base64ImageString,
ValueNotifier<String> imagePath,
) async {
// final flag = ValueNotifier<bool>(false);
if (cameraController.value != null) {
try {
final image = await cameraController.value!.takePicture();
final bytes = await image.readAsBytes();
String base64Image = base64Encode(bytes);
@override
Widget build(BuildContext context) {
if (controller == null || !controller!.value.isInitialized) {
return Center(child: CircularProgressIndicator());
// imageFile.value = image;
base64ImageString.value = base64Image;
imagePath.value = image.path;
fileDataBase64Param.value = base64Image;
filePathParam.value = image.path;
// final shared = await SharedPreferences.getInstance();
// shared.setString("base64Image", base64Image);
} catch (e) {
print('Error taking picture: $e');
// flag.value = false;
// RespErr(flag: false, message: 'Error taking picture: $e');
}
} else {
print('CameraController is not initialized.');
}
}
processFoto(BuildContext context) async {
await takePicture(
// fileData,
fileDataBase64,
filePath,
);
print("fileDataBase64 : ${fileDataBase64.value}");
if (fileDataBase64.value != "") {
// ref.read(imgPhotoWebProvider.notifier).state = fileDataBase64.value;
Navigator.of(context).pop();
} else {
print('Error: Image result is null');
}
// proses base64
// final shared = await SharedPreferences.getInstance();
// final imageBase64 = shared.getString("base64Image");
// if (imageBase64 != "") {
// fileDataBase64.value = imageBase64;
// ref.read(imgPhotoWebProvider.notifier).state = imageBase64;
// Navigator.of(context).pop();
// } else {
// print('Error: Image result is null');
// }
}
return Scaffold(
appBar: AppBar(
title: const Text('Take Photo'),
),
body: SafeArea(
child: Center(
child: Column(
children: [
SizedBox(height: 50),
Container(
width: 200,
height: 200,
child: AspectRatio(
aspectRatio: controller!.value.aspectRatio,
child: CameraPreview(controller!),
child: controller == null || !controller.value.isInitialized
? const CircularProgressIndicator()
: Padding(
padding: EdgeInsets.all(20),
child: Column(
children: [
// const SizedBox(height: 50),
Expanded(
child: AspectRatio(
aspectRatio: controller.value.aspectRatio,
child: CameraPreview(controller),
),
),
SizedBox(
height:
Constant.getActualYPhone(context: context, y: 20),
),
TextButton(
onPressed: () async {
fileDataBase64Param.value = "";
filePathParam.value = "";
// cameraController.dispose();
// await controller.dispose();
// await initializeCamera();
},
child: const Text("Clear Foto"),
),
Spacer(),
ElevatedButton(
// onPressed: () {
// Navigator.of(context).pushNamed(homeRoute);
// },
onPressed: () async {
processFoto(context);
},
style: ButtonStyle(
backgroundColor: MaterialStateColor.resolveWith(
(st) => (fileDataBase64.value != "")
? Constant.textDarkGrey
: Constant.textOrange),
shape:
MaterialStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(
color: Constant.textOrange,
),
),
),
shadowColor:
MaterialStateProperty.all(Color(0xffff48423d)),
elevation: MaterialStateProperty.all(4.0),
),
child: Stack(
children: [
(fileDataBase64.value != "")
? SizedBox(
width: Constant.getActualXPhone(
context: context, x: 24),
height: Constant.getActualYPhone(
context: context, y: 32),
child: Center(
child: CircularProgressIndicator(
color: Constant.textOrange,
),
),
)
: Align(
alignment: Alignment.center,
child: Text(
'Process Photo',
style: Constant.titleH1_500_18(
context: context)
.copyWith(
color: Constant.textWhite,
),
),
),
],
),
),
// if (imagePath.value.isNotEmpty)
// Container(
// width: 300,
// height: 300,
// child: Image.network(imagePath.value),
// ),
],
),
),
),
TextButton(
onPressed: () async {
try {
final image = await controller!.takePicture();
setState(() {
imagePath = image.path;
});
} catch (e) {
print('Error taking picture: $e');
}
},
child: Text("Take Photo"),
),
if (imagePath.isNotEmpty)
Container(
width: 300,
height: 300,
child: Image.file(File(imagePath)),
),
],
),
),
),
);

View File

@@ -0,0 +1,92 @@
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
class CameraPageV1 extends StatefulWidget {
const CameraPageV1({Key? key}) : super(key: key);
@override
State<CameraPageV1> createState() => _CameraPageState();
}
class _CameraPageState extends State<CameraPageV1> {
CameraController? controller;
String imagePath = "";
List<CameraDescription>? cameras;
@override
void initState() {
super.initState();
initializeCamera();
}
Future<void> initializeCamera() async {
// Initialize cameras
WidgetsFlutterBinding.ensureInitialized();
try {
cameras = await availableCameras();
if (cameras != null && cameras!.isNotEmpty) {
// Use the first available camera
controller = CameraController(cameras![0], ResolutionPreset.max);
await controller?.initialize();
if (mounted) {
setState(() {});
}
}
} catch (e) {
print('Error initializing camera: $e');
}
}
@override
void dispose() {
controller?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (controller == null || !controller!.value.isInitialized) {
return Center(child: CircularProgressIndicator());
}
return Scaffold(
body: SafeArea(
child: Center(
child: Column(
children: [
SizedBox(height: 50),
Container(
width: 200,
height: 200,
child: AspectRatio(
aspectRatio: controller!.value.aspectRatio,
child: CameraPreview(controller!),
),
),
TextButton(
onPressed: () async {
try {
final image = await controller!.takePicture();
setState(() {
imagePath = image.path;
});
} catch (e) {
print('Error taking picture: $e');
}
},
child: Text("Take Photo"),
),
if (imagePath.isNotEmpty)
Container(
width: 300,
height: 300,
child: Image.file(File(imagePath)),
),
],
),
),
),
);
}
}

View File

@@ -16,9 +16,11 @@ import 'package:flutter_image_compress/flutter_image_compress.dart';
// import 'package:geolocator/geolocator.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:image_picker/image_picker.dart';
import 'package:image_picker_web/image_picker_web.dart';
import 'package:latlong2/latlong.dart';
import 'package:location/location.dart';
import 'package:mobkit_dashed_border/mobkit_dashed_border.dart';
import 'package:shared_preferences/shared_preferences.dart';
// import 'package:permission_handler/permission_handler.dart';
import '../../app/constant.dart';
@@ -35,7 +37,6 @@ import 'camera_page.dart';
import 'check_distance_provider.dart';
import 'check_presensi_jam_provider.dart';
import 'googleapis_provider.dart';
import 'presensi_selfie_upload_area.dart';
import 'dart:io' as io;
import 'presensi_selfie_upload_area_web.dart';
@@ -55,22 +56,19 @@ class PresensiSelfieScreen extends HookConsumerWidget {
final positionLatitude = useState<String>("");
final positionLongitude = useState<String>("");
// final tTransactionCurrentDistance = useState<String>("NULL");
final fileData = useState<XFile?>(null);
final fileDataBase64 = useState<String>("");
final filePath = useState<String>("");
final isImage = useState(false);
final fileEkstension = useState("");
final fileSize = useState(0);
final fileName = useState("");
List<CameraDescription> cameras;
Location location = new Location();
LocationData _locationData;
final controller = ref.watch(cameraControllerProvider);
final isInited = useState(false);
final imageUrl = useState<String?>(null);
// final imgPhotoWeb = ref.read(imgPhotoWebProvider);
getBase64() async {
// List<int> imageBytes = await fileData.value?.readAsBytes();
@@ -94,47 +92,23 @@ class PresensiSelfieScreen extends HookConsumerWidget {
pickImage() async {
if (kIsWeb) {
// final shared = await SharedPreferences.getInstance();
// final imageBase64 = shared.getString("base64Image");
// final imagePathNew = shared.getString("imagePath");
try {
final path =
await ref.read(cameraControllerProvider.notifier).takePicture();
imageUrl.value = path;
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => CameraPage(
fileDataBase64Param: fileDataBase64,
filePathParam: filePath,
),
),
);
} catch (e) {
SanckbarWidget(
context, "Failed to take picture", snackbarType.warning);
}
} else {
final XFile? pickedFile = await _picker.pickImage(
source: ImageSource.camera,
// maxWidth set untuk width image
maxWidth: 640,
// maxHeight set untuk width image
maxHeight: 480);
if (pickedFile != null) {
final tmpFile = FilePickerResult([
PlatformFile(
name: pickedFile.name,
size: await pickedFile.length(),
path: pickedFile.path,
)
]);
if (await pickedFile.length() > 10000000) {
SanckbarWidget(context, "File tidak boleh lebih dari 10 MB",
snackbarType.warning);
} else {
fileData.value = pickedFile;
isImage.value = true;
fileEkstension.value = tmpFile.files.single.extension ?? "";
DateTime now = new DateTime.now();
fileName.value =
"IMG-${now.year}${now.month}${now.day}.${tmpFile.files.single.extension ?? ''}";
}
// final Directory appDocumentsDir =
// await getApplicationDocumentsDirectory();
// print(appDocumentsDir);
// await pickedFile!.saveTo(appDocumentsDir.path);
await getBase64();
}
}
}
@@ -782,46 +756,51 @@ class PresensiSelfieScreen extends HookConsumerWidget {
child: CircularProgressIndicator(),
)
: InkWell(
onTap: () async {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) =>
CameraPage(),
),
);
onTap: () {
pickImage();
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Icon(
// Icons.upload_outlined,
// color: Constant.textOrange,
// ),
child: Container(
width: Constant.getActualXPhone(
context: context, x: 390),
height: Constant.getActualYPhone(
context: context,
y: isImage.value ? 200 : 83),
decoration: BoxDecoration(
color: Constant.bgUploadFile,
borderRadius: BorderRadius.circular(8),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Icon(
// Icons.upload_outlined,
// color: Constant.textOrange,
// ),
Image.asset(
'images/camera_selfie.png', // Path gambar untuk "Check In"
width: Constant.getActualXPhone(
context: context, x: 24),
height: Constant.getActualYPhone(
context: context, y: 24),
),
SizedBox(
height: Constant.getActualYPhone(
context: context,
y: 4,
Image.asset(
'images/camera_selfie.png', // Path gambar untuk "Check In"
width: Constant.getActualXPhone(
context: context, x: 24),
height: Constant.getActualYPhone(
context: context, y: 24),
),
),
Text(
'Upload File',
style: Constant.titleH2_400_12(
context: context)
.copyWith(
fontWeight: FontWeight.w600,
color: Constant.textOrange),
)
],
SizedBox(
height: Constant.getActualYPhone(
context: context,
y: 4,
),
),
Text(
'Upload File',
style: Constant.titleH2_400_12(
context: context)
.copyWith(
fontWeight: FontWeight.w600,
color: Constant.textOrange),
)
],
),
),
),
] else ...[
@@ -833,18 +812,75 @@ class PresensiSelfieScreen extends HookConsumerWidget {
? Center(
child: CircularProgressIndicator(),
)
: PresensiSelfieUploadAreaWebWidget(
isLoading: isLoadingAddressUserLocation,
isImage: isImage,
fileData: fileData,
fileDataBase64: fileDataBase64,
pickFile: pickFile,
pickImage: pickImage,
: InkWell(
onTap: () {
pickImage();
},
child: Container(
width: Constant.getActualXPhone(
context: context, x: 390),
height: Constant.getActualYPhone(
context: context,
y: isImage.value ? 200 : 83),
decoration: BoxDecoration(
color: Constant.bgUploadFile,
borderRadius: BorderRadius.circular(8),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.center,
children: [
// Icon(
// Icons.upload_outlined,
// color: Constant.textOrange,
// ),
Image.asset(
'images/camera_selfie.png', // Path gambar untuk "Check In"
width: Constant.getActualXPhone(
context: context, x: 24),
height: Constant.getActualYPhone(
context: context, y: 24),
),
SizedBox(
height: Constant.getActualYPhone(
context: context,
y: 4,
),
),
Text(
'Upload File',
style: Constant.titleH2_400_12(
context: context)
.copyWith(
fontWeight: FontWeight.w600,
color: Constant.textOrange),
)
],
),
),
),
]
],
],
// gambar selfie
if (fileDataBase64.value.isNotEmpty) ...[
SizedBox(
height: Constant.getActualYPhone(context: context, y: 40),
),
SizedBox(
width: double.infinity,
height:
Constant.getActualYPhone(context: context, y: 200),
child: Image.network(
filePath.value,
fit: BoxFit.cover,
),
),
],
Spacer(),
// button clock in dan clock out
@@ -924,9 +960,8 @@ class PresensiSelfieScreen extends HookConsumerWidget {
token,
"Clock In");
// print(
// fileDataBase64.value,
// );
print(
"proses imageX: ${fileDataBase64.value}");
},
style: ButtonStyle(
backgroundColor:

View File

@@ -1,11 +1,16 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import '../../app/constant.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:mime/mime.dart';
class PresensiSelfieUploadAreaWebWidget extends StatelessWidget {
import '../../provider/camera_controller_provider.dart';
class PresensiSelfieUploadAreaWebWidget extends HookConsumerWidget {
const PresensiSelfieUploadAreaWebWidget({
super.key,
required this.isImage,
@@ -24,7 +29,9 @@ class PresensiSelfieUploadAreaWebWidget extends StatelessWidget {
final Function pickFile;
@override
Widget build(BuildContext context) {
Widget build(BuildContext context, WidgetRef ref) {
final imgPhotoWeb = ref.read(imgPhotoWebProvider);
return InkWell(
onTap: !isLoading.value
? () {