feat: integrate camera package and update related dependencies in pubspec.lock for enhanced image handling capabilities

This commit is contained in:
2025-12-15 16:36:23 +03:30
parent 24431b3514
commit 98c900f408
11 changed files with 518 additions and 153 deletions

View File

@@ -0,0 +1,78 @@
import 'package:camera/camera.dart';
import 'package:flutter/foundation.dart';
import 'package:rasadyar_core/utils/logger_utils.dart';
class RImagePickerController extends ChangeNotifier {
List<CameraDescription> _cameras = [];
CameraController? cameraController;
bool isLoading = false;
bool isCameraReady = false;
List<XFile> capturedImages = <XFile>[];
Future<void> getAvailableCameras() async {
_cameras = await availableCameras();
}
Future<void> openCamera() async {
try {
isLoading = true;
await disposeCameraController();
await getAvailableCameras();
if (_cameras.isNotEmpty) {
cameraController = CameraController(
_cameras[0],
ResolutionPreset.high,
enableAudio: false,
);
await cameraController?.initialize();
notifyListeners();
isCameraReady = true;
isLoading = false;
} else {
isCameraReady = false;
isLoading = false;
notifyListeners();
}
} catch (e) {
isCameraReady = false;
isLoading = false;
notifyListeners();
eLog(e);
}
}
Future<void> takePicture() async {
if (cameraController == null || !cameraController!.value.isInitialized)
return;
if (isLoading) return;
try {
isLoading = true;
notifyListeners();
final image = await cameraController!.takePicture();
capturedImages.add(image);
isLoading = false;
} catch (e) {
eLog(e);
} finally {
isLoading = false;
notifyListeners();
}
}
void removeImage(int index) {
if (index < capturedImages.length) {
capturedImages.removeAt(index);
notifyListeners();
}
}
Future<void> disposeCameraController() async {
await cameraController?.dispose();
}
}

View File

@@ -0,0 +1,158 @@
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:rasadyar_core/core.dart';
class RImagePicker extends StatefulWidget {
const RImagePicker({super.key, this.maxImages, required this.controller});
final int? maxImages;
final RImagePickerController controller;
@override
State<RImagePicker> createState() => _RImagePickerState();
}
class _RImagePickerState extends State<RImagePicker> {
@override
void initState() {
super.initState();
widget.controller.openCamera();
}
@override
void dispose() {
widget.controller.disposeCameraController();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: AnimatedBuilder(
animation: widget.controller,
builder: (context, child) {
if (!widget.controller.isCameraReady) {
return const Center(
child: CoreLoadingIndicator(
color: Colors.white,
variant: CoreLoadingVariant.cupertino,
size: CoreLoadingSize.medium,
),
);
}
return Stack(
alignment: Alignment.center,
fit: StackFit.expand,
children: [
CameraPreview(widget.controller.cameraController!),
Positioned(
bottom: 40,
right: 10,
child: RFab(
onPressed: () => Get.back(),
icon: Assets.vec.checkSvg.svg(),
backgroundColor: AppColor.greenNormal,
),
),
if (widget.maxImages == null ||
widget.controller.capturedImages.length <
(widget.maxImages ?? 1000)) ...[
Positioned(
bottom: 40,
left: 0,
right: 0,
child: Center(
child: FloatingActionButton(
onPressed: widget.controller.isLoading
? null
: () async {
await widget.controller.takePicture();
},
backgroundColor: Colors.white,
child: widget.controller.isLoading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(
Colors.black,
),
),
)
: const Icon(Icons.camera_alt, color: Colors.black),
),
),
),
],
if (widget.controller.capturedImages.isNotEmpty) ...[
Positioned(
bottom: 120,
left: 0,
right: 0,
height: 100.h,
child: ListView.separated(
itemCount: widget.controller.capturedImages.length,
padding: EdgeInsets.symmetric(
horizontal: 20.w,
vertical: 10.h,
),
scrollDirection: Axis.horizontal,
separatorBuilder: (context, index) => SizedBox(width: 10.w),
itemBuilder: (context, index) {
final image = widget.controller.capturedImages[index];
return Container(
width: 100.w,
height: 100.h,
decoration: BoxDecoration(
border: Border.all(color: Colors.white, width: 2),
borderRadius: BorderRadius.circular(8.r),
image: DecorationImage(
image: FileImage(File(image.path)),
fit: BoxFit.cover,
),
),
child: Stack(
fit: StackFit.expand,
children: [
Positioned(
top: 2,
right: 2,
child: GestureDetector(
onTap: () =>
widget.controller.removeImage(index),
child: Container(
padding: const EdgeInsets.all(4),
decoration: const BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
child: const Icon(
Icons.close,
size: 16,
color: Colors.white,
),
),
),
),
],
),
);
},
),
),
],
],
);
},
),
);
}
}

View File

@@ -50,3 +50,7 @@ export 'tabs/new_tab.dart';
export 'tabs/r_segment.dart';
export 'tabs/tab.dart';
export 'vec_widget.dart';
//image picker
export 'image_picker/image_picker_controller.dart';
export 'image_picker/image_picker_widget.dart';