Merge branch with resolved conflicts - restructured features and added new modules

This commit is contained in:
2025-12-17 10:26:39 +03:30
484 changed files with 55236 additions and 4255 deletions

View File

@@ -0,0 +1,102 @@
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;
bool frontCamera = true;
bool isCameraLoading = false;
bool hasTwoCameras = false;
List<XFile> capturedImages = <XFile>[];
Future<void> getAvailableCameras() async {
_cameras = await availableCameras();
if (_cameras.length > 1) {
hasTwoCameras = true;
}
}
Future<void> openCamera() async {
try {
isCameraLoading = true;
await disposeCameraController();
await getAvailableCameras();
if (_cameras.isNotEmpty) {
if (hasTwoCameras && frontCamera) {
cameraController = CameraController(
_cameras[0],
ResolutionPreset.high,
enableAudio: false,
);
}
if (hasTwoCameras && !frontCamera) {
cameraController = CameraController(
_cameras[1],
ResolutionPreset.high,
enableAudio: false,
);
}
await cameraController?.initialize();
isCameraReady = true;
isCameraLoading = false;
notifyListeners();
} else {
isCameraReady = false;
isCameraLoading = false;
notifyListeners();
}
} catch (e) {
isCameraReady = false;
isCameraLoading = 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.insert(0, 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 {
isCameraReady = false;
notifyListeners();
await cameraController?.dispose();
cameraController = null;
}
}

View File

@@ -0,0 +1,211 @@
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/cupertino.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(
top: 50,
right: 10,
child: GestureDetector(
onTap: () => Get.back(),
child: Container(
width: 40.w,
height: 40.h,
decoration: BoxDecoration(
color: Colors.white.withAlpha(50),
borderRadius: BorderRadius.circular(10.r),
),
child: Icon(
CupertinoIcons.clear,
color: Colors.white,
size: 20,
),
),
),
),
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),
),
),
),
],
Positioned(
bottom: 40,
left: 10,
child: Center(
child: FloatingActionButton(
onPressed: widget.controller.isCameraLoading
? null
: () async {
widget.controller.frontCamera =
!widget.controller.frontCamera;
await widget.controller.openCamera();
},
backgroundColor: Colors.white,
child: widget.controller.isCameraLoading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(
Colors.black,
),
),
)
: const Icon(Icons.cameraswitch, 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

@@ -23,6 +23,7 @@ class RTextField extends StatefulWidget {
final BoxConstraints? boxConstraints;
final RTextFieldVariant variant;
final bool filled;
final bool isFullHeight;
final Color? filledColor;
final Color? borderColor;
final bool showCounter;
@@ -92,6 +93,7 @@ class RTextField extends StatefulWidget {
this.prefixIcon,
this.suffix,
this.boxConstraints,
this.isFullHeight = false,
// 📐 Layout & appearance
this.borderColor,
@@ -201,8 +203,8 @@ class _RTextFieldState extends State<RTextField> {
child: Padding(
padding: widget.padding ?? EdgeInsets.zero,
child: TextFormField(
textAlignVertical: TextAlignVertical.center,
controller: widget.controller,
focusNode: widget.focusNode,
textAlign: widget.textAlign ?? TextAlign.start,
readOnly: widget.readonly,
@@ -229,7 +231,11 @@ class _RTextFieldState extends State<RTextField> {
textInputAction: widget.textInputAction,
autofillHints: widget.autofillHints,
decoration: InputDecoration(
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
contentPadding: EdgeInsets.symmetric(
horizontal: 16,
vertical: widget.isFullHeight ? widget.height / 3 : 0,
),
errorStyle: widget.errorStyle,
errorMaxLines: 1,
isDense: widget.isDense,

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';