From d6bdf1a76cd44efd05e697c3b0b1a1bfb6e84cd0 Mon Sep 17 00:00:00 2001 From: sindhu Date: Sun, 16 Feb 2025 01:22:50 +0700 Subject: [PATCH] step 10 : slicing scan screen, add pubdev, fungsi camera, crop, rotate angle --- android/app/build.gradle | 7 +- android/app/src/main/AndroidManifest.xml | 6 + lib/app/route.dart | 15 +- lib/screen/home/home_screen.dart | 4 +- lib/screen/scan/scan_screen.dart | 354 ++++++++++++++++++ lib/screen/scan/scan_screen1.txt | 277 ++++++++++++++ macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 184 +++++++++ pubspec.yaml | 4 + .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 1 + 11 files changed, 852 insertions(+), 5 deletions(-) create mode 100644 lib/screen/scan/scan_screen.dart create mode 100644 lib/screen/scan/scan_screen1.txt diff --git a/android/app/build.gradle b/android/app/build.gradle index 9420fe0..e399c97 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -7,9 +7,10 @@ plugins { android { namespace = "com.example.scanktpflutter" - compileSdk = flutter.compileSdkVersion - ndkVersion = flutter.ndkVersion - + // compileSdk = flutter.compileSdkVersion + compileSdk = 34 + // ndkVersion = flutter.ndkVersion + ndkVersion = "21.1.6352462" compileOptions { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 13afb52..fbcd549 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,4 +1,10 @@ + + + + + + generateRoute(RouteSettings settings) { @@ -43,6 +45,17 @@ class AppRoute { }); } + // scan screen + if (settings.name == scanRoute) { + return MaterialPageRoute(builder: (context) { + return MediaQuery( + data: MediaQuery.of(context).copyWith( + textScaler: TextScaler.linear(1.0), padding: EdgeInsets.all(0)), + child: ScanScreen(), + ); + }); + } + return MaterialPageRoute(builder: (context) { return MediaQuery( data: MediaQuery.of(context).copyWith( diff --git a/lib/screen/home/home_screen.dart b/lib/screen/home/home_screen.dart index a772084..eee916b 100644 --- a/lib/screen/home/home_screen.dart +++ b/lib/screen/home/home_screen.dart @@ -109,7 +109,9 @@ class HomeScreen extends HookConsumerWidget { y: 48, ), child: ElevatedButton( - onPressed: () {}, + onPressed: () { + Navigator.of(context).pushNamed(scanRoute); + }, style: ElevatedButton.styleFrom( backgroundColor: Constant.bgButton, shape: RoundedRectangleBorder( diff --git a/lib/screen/scan/scan_screen.dart b/lib/screen/scan/scan_screen.dart new file mode 100644 index 0000000..7aaa5c0 --- /dev/null +++ b/lib/screen/scan/scan_screen.dart @@ -0,0 +1,354 @@ +import 'dart:io'; +import 'dart:typed_data'; +import 'package:camera/camera.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:image/image.dart' as img; + +import '../../app/constant.dart'; + +class ScanScreen extends HookConsumerWidget { + const ScanScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: [ + SystemUiOverlay.bottom, + ]); + + final cameraController = useState(null); + final initializeControllerFuture = useState?>(null); + final capturedImage = useState(null); + final croppedImage = useState(null); + final isLoading = useState(false); + final isModalOpen = useState(false); + + useEffect(() { + Future initializeCamera() async { + final cameras = await availableCameras(); + cameraController.value = + CameraController(cameras[0], ResolutionPreset.max); + initializeControllerFuture.value = cameraController.value!.initialize(); + await initializeControllerFuture.value; + await cameraController.value!.setFlashMode(FlashMode.off); + } + + initializeCamera(); + return null; + }, []); + + Future rotateAndCropImage(File imageFile) async { + Uint8List imageBytes = await imageFile.readAsBytes(); + img.Image? original = img.decodeImage(imageBytes); + if (original == null) return imageFile; + + img.Image rotated = img.bakeOrientation(original); + + int width = rotated.width; + int height = rotated.height; + bool isPortrait = height > width; + + int cropWidth, cropHeight; + + if (isPortrait) { + cropHeight = (height * 0.7).toInt(); + cropWidth = (cropHeight ~/ 1.59).toInt(); + } else { + cropWidth = (width * 0.7).toInt(); + cropHeight = (cropWidth ~/ 1.59).toInt(); + } + + int left = ((width - cropWidth) ~/ 2).toInt(); + int top = ((height - cropHeight) ~/ 2).toInt(); + + img.Image cropped = img.copyCrop(rotated, + x: left, y: top, width: cropWidth, height: cropHeight); + + File croppedFile = File('${imageFile.path}_cropped.jpg'); + await croppedFile.writeAsBytes(img.encodeJpg(cropped)); + + return croppedFile; + } + + Future rotateImage(File imageFile) async { + Uint8List bytes = await imageFile.readAsBytes(); + img.Image? image = img.decodeImage(bytes); + + if (image == null) return imageFile; + + img.Image rotated = img.copyRotate(image, angle: -90); + + File rotatedFile = File('${imageFile.path}_rotated.jpg'); + await rotatedFile.writeAsBytes(img.encodeJpg(rotated)); + + 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 captureAndCropImage() async { + try { + isLoading.value = true; + await initializeControllerFuture.value; + + // Ambil gambar dari kamera + final image = await cameraController.value!.takePicture(); + File cropped = await rotateAndCropImage(File(image.path)); + + // Rotate gambar setelah cropping + File rotatedImage = await rotateImage(cropped); + + // Simpan hasil yang sudah di-crop dan di-rotate + capturedImage.value = image; + croppedImage.value = rotatedImage; + + showResultModal(context); + } catch (e) { + print("Error capturing image: $e"); + } finally { + isLoading.value = false; + } + } + + return Scaffold( + backgroundColor: Colors.black.withOpacity(0.5), + body: Stack( + children: [ + if (cameraController.value != null) + FutureBuilder( + future: initializeControllerFuture.value, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + return CameraPreview(cameraController.value!); + } else { + return const Center(child: CircularProgressIndicator()); + } + }, + ), + // bingkai foto + Positioned.fill( + child: CustomPaint( + painter: OverlayPainter(), + ), + ), + // loading + if (isLoading.value) + Positioned.fill( + child: Container( + color: Colors.black.withOpacity(0.5), + child: const Center( + child: CircularProgressIndicator(color: Colors.white), + ), + ), + ), + ], + ), + bottomNavigationBar: BottomAppBar( + height: Constant.getActualYPhone( + context: context, + y: 100, + ), + shape: const CircularNotchedRectangle(), + child: Row( + children: [ + ElevatedButton.icon( + onPressed: () { + Navigator.of(context).pop(); + }, + icon: Icon( + Icons.arrow_back, + size: 17, + color: Constant.textWhite, + ), + label: Text( + 'Kembali', + style: Constant.cardText(context: context).copyWith( + color: Constant.textWhite, + ), + ), + style: ElevatedButton.styleFrom( + backgroundColor: Constant.textCardGrey, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + elevation: 8, + shadowColor: Constant.bgButton.withOpacity(0.24), + ), + ), + const Spacer(), + // scan + ElevatedButton.icon( + onPressed: captureAndCropImage, + icon: Icon( + Icons.camera, + size: 17, + color: Constant.textWhite, + ), + label: Text( + 'Foto', + style: Constant.cardText(context: context).copyWith( + color: Constant.textWhite, + ), + ), + style: ElevatedButton.styleFrom( + backgroundColor: Constant.bgButton, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + elevation: 8, + shadowColor: Constant.bgButton.withOpacity(0.24), + ), + ), + ], + ), + ), + ); + } +} + +class OverlayPainter extends CustomPainter { + @override + void paint(Canvas canvas, Size size) { + final paint = Paint() + ..color = Colors.black.withOpacity(0.6) + ..style = PaintingStyle.fill; + + final borderPaint = Paint() + ..color = Colors.yellow + ..strokeWidth = 4 + ..style = PaintingStyle.stroke; + + double boxHeight = size.height * 0.7; + double boxWidth = boxHeight / 1.59; + + double left = (size.width - boxWidth) / 2; + double top = (size.height - boxHeight) / 2; + + Path path = Path() + ..addRect(Rect.fromLTWH(0, 0, size.width, size.height)) + ..addRect(Rect.fromLTWH(left, top, boxWidth, boxHeight)) + ..fillType = PathFillType.evenOdd; + + canvas.drawPath(path, paint); + canvas.drawRect(Rect.fromLTWH(left, top, boxWidth, boxHeight), borderPaint); + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) => false; +} diff --git a/lib/screen/scan/scan_screen1.txt b/lib/screen/scan/scan_screen1.txt new file mode 100644 index 0000000..1e650c6 --- /dev/null +++ b/lib/screen/scan/scan_screen1.txt @@ -0,0 +1,277 @@ +import 'dart:io'; +import 'dart:typed_data'; +import 'package:camera/camera.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:image/image.dart' as img; + +import '../../app/constant.dart'; + +class ScanScreen extends HookConsumerWidget { + const ScanScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: [ + SystemUiOverlay.bottom, + ]); + + final cameraController = useState(null); + final capturedImage = useState(null); + final croppedImage = useState(null); + final isLoading = useState(false); + final initializeControllerFuture = useState?>(null); + + useEffect(() { + Future initializeCamera() async { + final cameras = await availableCameras(); + cameraController.value = + CameraController(cameras[0], ResolutionPreset.max); + initializeControllerFuture.value = cameraController.value!.initialize(); + await initializeControllerFuture.value; + await cameraController.value!.setFlashMode(FlashMode.off); + } + + initializeCamera(); + return null; + }, []); + + Future rotateAndCropImage(File imageFile) async { + Uint8List imageBytes = await imageFile.readAsBytes(); + img.Image? original = img.decodeImage(imageBytes); + if (original == null) return imageFile; + + img.Image rotated = img.bakeOrientation(original); + + int width = rotated.width; + int height = rotated.height; + bool isPortrait = height > width; + + int cropWidth, cropHeight; + + if (isPortrait) { + cropHeight = (height * 0.7).toInt(); + cropWidth = (cropHeight ~/ 1.59).toInt(); + } else { + cropWidth = (width * 0.7).toInt(); + cropHeight = (cropWidth ~/ 1.59).toInt(); + } + + int left = ((width - cropWidth) ~/ 2).toInt(); + int top = ((height - cropHeight) ~/ 2).toInt(); + + img.Image cropped = img.copyCrop(rotated, + x: left, y: top, width: cropWidth, height: cropHeight); + + File croppedFile = File('${imageFile.path}_cropped.jpg'); + await croppedFile.writeAsBytes(img.encodeJpg(cropped)); + + return croppedFile; + } + + Future rotateImage(File imageFile) async { + Uint8List bytes = await imageFile.readAsBytes(); + img.Image? image = img.decodeImage(bytes); + + if (image == null) return imageFile; + + img.Image rotated = img.copyRotate(image, angle: -90); + + File rotatedFile = File('${imageFile.path}_rotated.jpg'); + await rotatedFile.writeAsBytes(img.encodeJpg(rotated)); + + return rotatedFile; + } + + Future captureAndCropImage() async { + try { + isLoading.value = true; + await initializeControllerFuture.value; + + // Ambil gambar dari kamera + final image = await cameraController.value!.takePicture(); + File cropped = await rotateAndCropImage(File(image.path)); + + // Rotate gambar setelah cropping + File rotatedImage = await rotateImage(cropped); + + // Simpan hasil yang sudah di-crop dan di-rotate + capturedImage.value = image; + croppedImage.value = rotatedImage; + } catch (e) { + print("Error capturing image: $e"); + } finally { + isLoading.value = false; + } + } + + return GestureDetector( + onTap: () { + FocusManager.instance.primaryFocus!.unfocus(); + }, + child: Scaffold( + resizeToAvoidBottomInset: true, + backgroundColor: Constant.textBlack, + body: Stack( + children: [ + if (cameraController.value != null) + FutureBuilder( + future: initializeControllerFuture.value, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + return CameraPreview(cameraController.value!); + } else { + return const Center(child: CircularProgressIndicator()); + } + }, + ), + // bingkai foto + Positioned.fill( + child: CustomPaint( + painter: OverlayPainter(), + ), + ), + // hasil foto + if (croppedImage.value != null) + Positioned.fill( + child: Image.file( + croppedImage.value!, + fit: BoxFit.contain, + ), + ), + // loading + if (isLoading.value) + Positioned.fill( + child: Container( + color: Colors.black.withOpacity(0.5), + child: const Center( + child: CircularProgressIndicator(color: Colors.white), + ), + ), + ), + ], + ), + bottomNavigationBar: BottomAppBar( + shape: const CircularNotchedRectangle(), + child: Row( + children: [ + ElevatedButton.icon( + onPressed: () { + Navigator.of(context).pop(); + }, + icon: Icon( + Icons.arrow_back, + size: 17, + color: Constant.textWhite, + ), + label: Text( + 'Kembali', + style: Constant.cardText(context: context).copyWith( + color: Constant.textWhite, + ), + ), + style: ElevatedButton.styleFrom( + backgroundColor: Constant.textCardGrey, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + elevation: 8, + shadowColor: Constant.bgButton.withOpacity(0.24), + ), + ), + + Spacer(), + + // clear + ElevatedButton.icon( + onPressed: () { + capturedImage.value = null; + croppedImage.value = null; + }, + icon: Icon( + Icons.clear, + size: 17, + color: Constant.textWhite, + ), + label: Text( + 'Reset', + style: Constant.cardText(context: context).copyWith( + color: Constant.textWhite, + ), + ), + style: ElevatedButton.styleFrom( + backgroundColor: Constant.textRed, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + elevation: 8, + shadowColor: Constant.bgButton.withOpacity(0.24), + ), + ), + + SizedBox( + width: Constant.getActualXPhone(context: context, x: 10), + ), + // scan + ElevatedButton.icon( + onPressed: captureAndCropImage, + icon: Icon( + Icons.camera, + size: 17, + color: Constant.textWhite, + ), + label: Text( + 'Foto', + style: Constant.cardText(context: context).copyWith( + color: Constant.textWhite, + ), + ), + style: ElevatedButton.styleFrom( + backgroundColor: Constant.bgButton, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + elevation: 8, + shadowColor: Constant.bgButton.withOpacity(0.24), + ), + ), + ], + ), + ), + ), + ); + } +} + +class OverlayPainter extends CustomPainter { + @override + void paint(Canvas canvas, Size size) { + final paint = Paint() + ..color = Colors.black.withOpacity(0.6) + ..style = PaintingStyle.fill; + + final borderPaint = Paint() + ..color = Colors.yellow + ..strokeWidth = 4 + ..style = PaintingStyle.stroke; + + double boxHeight = size.height * 0.7; + double boxWidth = boxHeight / 1.59; + + double left = (size.width - boxWidth) / 2; + double top = (size.height - boxHeight) / 2; + + Path path = Path() + ..addRect(Rect.fromLTWH(0, 0, size.width, size.height)) + ..addRect(Rect.fromLTWH(left, top, boxWidth, boxHeight)) + ..fillType = PathFillType.evenOdd; + + canvas.drawPath(path, paint); + canvas.drawRect(Rect.fromLTWH(left, top, boxWidth, boxHeight), borderPaint); + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) => false; +} diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 724bb2a..b8e2b22 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,8 +5,10 @@ import FlutterMacOS import Foundation +import path_provider_foundation import shared_preferences_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index 12572c9..f95fa41 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,14 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + archive: + dependency: transitive + description: + name: archive + sha256: "6199c74e3db4fbfbd04f66d739e72fe11c8a8957d5f219f1f4482dbde6420b5a" + url: "https://pub.dev" + source: hosted + version: "4.0.2" async: dependency: transitive description: @@ -17,6 +25,46 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + camera: + dependency: "direct main" + description: + name: camera + sha256: dfa8fc5a1adaeb95e7a54d86a5bd56f4bb0e035515354c8ac6d262e35cec2ec8 + url: "https://pub.dev" + source: hosted + version: "0.10.6" + camera_android: + dependency: transitive + description: + name: camera_android + sha256: "007c57cdcace4751014071e3d42f2eb8a64a519254abed35b714223d81d66234" + url: "https://pub.dev" + source: hosted + version: "0.10.10" + camera_avfoundation: + dependency: transitive + description: + name: camera_avfoundation + sha256: eff7ed630b1ac3994737c790368fe006388ad9f271d7148e432263721e45dc75 + url: "https://pub.dev" + source: hosted + version: "0.9.18+7" + camera_platform_interface: + dependency: transitive + description: + name: camera_platform_interface + sha256: "953e7baed3a7c8fae92f7200afeb2be503ff1a17c3b4e4ed7b76f008c2810a31" + url: "https://pub.dev" + source: hosted + version: "2.9.0" + camera_web: + dependency: transitive + description: + name: camera_web + sha256: "595f28c89d1fb62d77c73c633193755b781c6d2e0ebcd8dc25b763b514e6ba8f" + url: "https://pub.dev" + source: hosted + version: "0.3.5" characters: dependency: transitive description: @@ -41,6 +89,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.18.0" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670" + url: "https://pub.dev" + source: hosted + version: "0.3.4+2" + crypto: + dependency: transitive + description: + name: crypto + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + url: "https://pub.dev" + source: hosted + version: "3.0.6" cupertino_icons: dependency: "direct main" description: @@ -118,6 +182,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.0" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: "615a505aef59b151b46bbeef55b36ce2b6ed299d160c51d84281946f0aa0ce0e" + url: "https://pub.dev" + source: hosted + version: "2.0.24" flutter_riverpod: dependency: "direct main" description: @@ -152,6 +224,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" + image: + dependency: "direct main" + description: + name: image + sha256: "8346ad4b5173924b5ddddab782fc7d8a6300178c8b1dc427775405a01701c4a6" + url: "https://pub.dev" + source: hosted + version: "4.5.2" intl: dependency: "direct main" description: @@ -232,6 +312,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.0" + path_provider: + dependency: "direct main" + description: + name: path_provider + sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" + url: "https://pub.dev" + source: hosted + version: "2.1.5" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2" + url: "https://pub.dev" + source: hosted + version: "2.2.15" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" + url: "https://pub.dev" + source: hosted + version: "2.4.1" path_provider_linux: dependency: transitive description: @@ -256,6 +360,62 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.0" + permission_handler: + dependency: "direct main" + description: + name: permission_handler + sha256: "18bf33f7fefbd812f37e72091a15575e72d5318854877e0e4035a24ac1113ecb" + url: "https://pub.dev" + source: hosted + version: "11.3.1" + permission_handler_android: + dependency: transitive + description: + name: permission_handler_android + sha256: "71bbecfee799e65aff7c744761a57e817e73b738fedf62ab7afd5593da21f9f1" + url: "https://pub.dev" + source: hosted + version: "12.0.13" + permission_handler_apple: + dependency: transitive + description: + name: permission_handler_apple + sha256: e6f6d73b12438ef13e648c4ae56bd106ec60d17e90a59c4545db6781229082a0 + url: "https://pub.dev" + source: hosted + version: "9.4.5" + permission_handler_html: + dependency: transitive + description: + name: permission_handler_html + sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24" + url: "https://pub.dev" + source: hosted + version: "0.1.3+5" + permission_handler_platform_interface: + dependency: transitive + description: + name: permission_handler_platform_interface + sha256: e9c8eadee926c4532d0305dff94b85bf961f16759c3af791486613152af4b4f9 + url: "https://pub.dev" + source: hosted + version: "4.2.3" + permission_handler_windows: + dependency: transitive + description: + name: permission_handler_windows + sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" + url: "https://pub.dev" + source: hosted + version: "0.2.1" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 + url: "https://pub.dev" + source: hosted + version: "6.0.2" platform: dependency: transitive description: @@ -272,6 +432,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" + posix: + dependency: transitive + description: + name: posix + sha256: a0117dc2167805aa9125b82eee515cc891819bac2f538c83646d355b16f58b9a + url: "https://pub.dev" + source: hosted + version: "6.0.1" riverpod: dependency: transitive description: @@ -373,6 +541,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 + url: "https://pub.dev" + source: hosted + version: "2.1.1" string_scanner: dependency: transitive description: @@ -445,6 +621,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + xml: + dependency: transitive + description: + name: xml + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 + url: "https://pub.dev" + source: hosted + version: "6.5.0" sdks: dart: ">=3.5.4 <4.0.0" flutter: ">=3.24.0" diff --git a/pubspec.yaml b/pubspec.yaml index 9ae446f..229018b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -44,6 +44,10 @@ dependencies: equatable: ^2.0.7 top_snackbar_flutter: ^3.2.0 jiffy: ^6.3.1 + path_provider: ^2.1.5 + camera: ^0.10.6 + image: ^4.1.3 + permission_handler: ^11.3.0 dev_dependencies: flutter_test: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 8b6d468..48de52b 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,6 +6,9 @@ #include "generated_plugin_registrant.h" +#include void RegisterPlugins(flutter::PluginRegistry* registry) { + PermissionHandlerWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index b93c4c3..0e69e40 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + permission_handler_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST