diff --git a/android/gradle.properties b/android/gradle.properties index 2597170..2234d2d 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,3 +1,4 @@ org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError android.useAndroidX=true android.enableJetifier=true +dev.steenbakker.mobile_scanner.useUnbundled=true \ No newline at end of file diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 304dcb9..2c865eb 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -45,5 +45,9 @@ UIApplicationSupportsIndirectInputEvents + io.flutter.embedded_views_preview + + NSCameraUsageDescription + This app needs camera access to scan QR codes diff --git a/lib/app/route.dart b/lib/app/route.dart index f97d12c..fdcca4a 100644 --- a/lib/app/route.dart +++ b/lib/app/route.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import '../screen/home/rekam_screen.dart'; import '../screen/home/home_screen.dart'; import '../screen/login/login_screen.dart'; @@ -7,8 +8,7 @@ import '../screen/splash/splash_screen.dart'; const splashRoute = "/splashRoute"; const loginRoute = "/loginRoute"; const homeRoute = "/homeRoute"; -const scanRoute = "/scanRoute"; -const editScanRoute = "/editScanRoute"; +const rekamRoute = "/rekamRoute"; class AppRoute { static Route generateRoute(RouteSettings settings) { @@ -45,6 +45,17 @@ class AppRoute { }); } + // rekam screen + if (settings.name == rekamRoute) { + return MaterialPageRoute(builder: (context) { + return MediaQuery( + data: MediaQuery.of(context).copyWith( + textScaler: TextScaler.linear(1.0), padding: EdgeInsets.all(0)), + child: RekamScreen(), + ); + }); + } + return MaterialPageRoute(builder: (context) { return MediaQuery( data: MediaQuery.of(context).copyWith( diff --git a/lib/provider/voice_to_text_provider.dart b/lib/provider/voice_to_text_provider.dart index 3b2a34b..101300a 100644 --- a/lib/provider/voice_to_text_provider.dart +++ b/lib/provider/voice_to_text_provider.dart @@ -1,4 +1,5 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:mobile_scanner/mobile_scanner.dart'; import '../../model/edit_voice_to_text_model.dart'; import '../../model/voice_to_text_model.dart'; @@ -19,4 +20,9 @@ final selectedEdit = StateProvider( Voice2text_Updated: "", Voice2text_User_ID: "", ), +); + +// +final barcodeX = StateProvider( + (ref) => Barcode(), ); \ No newline at end of file diff --git a/lib/screen/home/home_screen.dart b/lib/screen/home/home_screen.dart index 8d93372..a5bb747 100644 --- a/lib/screen/home/home_screen.dart +++ b/lib/screen/home/home_screen.dart @@ -134,7 +134,7 @@ class HomeScreen extends HookConsumerWidget { ), child: ElevatedButton( onPressed: () { - // Navigator.of(context).pushNamed(scanRoute); + Navigator.of(context).pushNamed(rekamRoute); }, style: ElevatedButton.styleFrom( backgroundColor: Constant.bgButton, diff --git a/lib/screen/home/rekam_screen.dart b/lib/screen/home/rekam_screen.dart new file mode 100644 index 0000000..b08f7e7 --- /dev/null +++ b/lib/screen/home/rekam_screen.dart @@ -0,0 +1,198 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:fluttervoice2text/provider/voice_to_text_provider.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:mobile_scanner/mobile_scanner.dart'; + +import '../../app/constant.dart'; + +class RekamScreen extends HookConsumerWidget { + const RekamScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + SystemChrome.setEnabledSystemUIMode( + SystemUiMode.manual, + overlays: [SystemUiOverlay.bottom], + ); + + final isRekam = useState(false); + final judulTombol = useState("MULAI REKAM"); + final qrCodeStr = useState(""); + final isSelesaiRekam = useState(false); + + void handleBarcode(BarcodeCapture barcodes) { + if (barcodes.barcodes.isNotEmpty) { + final scannedBarcode = + barcodes.barcodes.firstOrNull?.displayValue ?? ""; + if (scannedBarcode.isNotEmpty) { + ref.read(barcodeX.notifier).state = barcodes.barcodes.first; + qrCodeStr.value = scannedBarcode; + } + } + } + + Widget buildBarcode(Barcode? value) { + final qrCode = value?.displayValue ?? ""; + + if (qrCode.isEmpty) { + return const Text( + 'Scan untuk mendapatkan QR Code', + overflow: TextOverflow.fade, + style: TextStyle(color: Colors.white), + ); + } + + return Text( + "Info: $qrCode", + overflow: TextOverflow.fade, + style: const TextStyle(color: Colors.white), + ); + } + + useEffect(() { + handleBarcode; + return () {}; + }, []); + + return GestureDetector( + onTap: () { + FocusManager.instance.primaryFocus!.unfocus(); + }, + child: Scaffold( + resizeToAvoidBottomInset: true, + backgroundColor: Constant.bgGrey, + bottomNavigationBar: BottomAppBar( + color: Colors.blueGrey.shade100, + height: Constant.getActualYPhone( + context: context, + y: 100, + ), + shape: const CircularNotchedRectangle(), + child: Row( + children: [ + ElevatedButton.icon( + onPressed: () { + ref.read(barcodeX.notifier).state = Barcode(); + isRekam.value = false; + judulTombol.value = "MULAI REKAM"; + qrCodeStr.value = ""; + isSelesaiRekam.value = false; + 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: Colors.green.shade400, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + elevation: 8, + shadowColor: Constant.bgButton.withOpacity(0.24), + ), + ), + const Spacer(), + // button + ElevatedButton( + onPressed: (qrCodeStr.value.isEmpty || isSelesaiRekam.value) + ? null + : () { + judulTombol.value = "SELESAI"; + isSelesaiRekam.value = true; + }, + style: ElevatedButton.styleFrom( + backgroundColor: (qrCodeStr.value.isEmpty || isSelesaiRekam.value) + ? Constant.bgGrey + : Constant.bgButton, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + elevation: 8, + shadowColor: Constant.bgButton.withOpacity(0.24), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.mic, + size: Constant.getActualXPhone( + context: context, + x: 20, + ), + color: Constant.textWhite, + ), + SizedBox( + width: Constant.getActualXPhone( + context: context, + x: 10, + ), + ), + Text( + judulTombol.value, + style: Constant.titleButton500(context: context).copyWith( + color: Constant.textWhite, + ), + ), + ], + ), + ), + ], + ), + ), + appBar: AppBar( + title: Text( + 'Rekam Audio', + style: Constant.titlePosisiHP(context: context), + ), + automaticallyImplyLeading: false, + ), + body: Padding( + padding: EdgeInsets.symmetric( + horizontal: Constant.getActualXPhone(context: context, x: 20), + ), + child: Column( + children: [ + // scanner + if (isRekam.value == false) ...[ + SizedBox( + width: double.infinity, + height: Constant.getActualYPhone( + context: context, + y: 450, + ), + child: MobileScanner( + onDetect: handleBarcode, + ), + ), + SizedBox( + height: Constant.getActualYPhone( + context: context, + y: 15, + ), + ), + ], + + // text + Container( + alignment: Alignment.center, + padding: const EdgeInsets.symmetric(vertical: 10), + color: Constant.inputanGrey, + child: buildBarcode(ref.watch(barcodeX)), + ), + ], + ), + ), + ), + ); + } +} diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 5ea4a51..b6a1bab 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,12 +6,14 @@ import FlutterMacOS import Foundation import audioplayers_darwin +import mobile_scanner import path_provider_foundation import record_darwin import shared_preferences_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin")) + MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) RecordPlugin.register(with: registry.registrar(forPlugin: "RecordPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) diff --git a/pubspec.lock b/pubspec.lock index d081ec0..b76436d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -320,6 +320,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.15.0" + mobile_scanner: + dependency: "direct main" + description: + name: mobile_scanner + sha256: "91d28b825784e15572fdc39165c5733099ce0e69c6f6f0964ebdbf98a62130fd" + url: "https://pub.dev" + source: hosted + version: "6.0.6" path: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 3d18441..7c58398 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -49,6 +49,7 @@ dependencies: audioplayers: ^6.2.0 toastification: ^2.3.0 record: ^5.2.1 + mobile_scanner: ^6.0.6 dev_dependencies: flutter_test: