diff --git a/app_petty_cash/android/app/build.gradle b/app_petty_cash/android/app/build.gradle index ed29a90..a97d4f8 100644 --- a/app_petty_cash/android/app/build.gradle +++ b/app_petty_cash/android/app/build.gradle @@ -52,6 +52,7 @@ android { targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName + multiDexEnabled true } buildTypes { @@ -69,4 +70,5 @@ flutter { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation 'com.android.support:multidex:1.0.3' } diff --git a/app_petty_cash/android/app/src/main/AndroidManifest.xml b/app_petty_cash/android/app/src/main/AndroidManifest.xml index b05f645..9404999 100644 --- a/app_petty_cash/android/app/src/main/AndroidManifest.xml +++ b/app_petty_cash/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ + prm = {}; String M_CompanyID = await getCompanyID(); if (tipe == "DEBIT") { sender = ""; - paramPostInUrl = - "?tanggal=$tanggal&tipe=$tipe&kategoriid=$kategoriid&jumlah=$jumlah&catatan=$catatan&url=$url&userid=$userid&sender=$sender"; + prm = { + 'tanggal': tanggal, + 'tipe': tipe, + 'kategoriid': kategoriid, + 'jumlah': jumlah, + 'catatan': catatan, + 'url': url, + 'userid': userid, + 'sender': sender, + 'companyid': M_CompanyID, + 'base64File': base64file, + 'fileName': fileName, + 'fileSize': fileSize, + 'fileEkstension': fileExtension + }; + + // paramPostInUrl = + // "?tanggal=$tanggal&tipe=$tipe&kategoriid=$kategoriid&jumlah=$jumlah&catatan=$catatan&url=$url&userid=$userid&sender=$sender"; } else { if (tipe == "KREDIT") { kategoriid = "0"; - paramPostInUrl = - "?tanggal=$tanggal&tipe=$tipe&kategoriid=$kategoriid&jumlah=$jumlah&catatan=$catatan&url=$url&userid=$userid&sender=$sender"; + prm = { + 'tanggal': tanggal, + 'tipe': tipe, + 'kategoriid': kategoriid, + 'jumlah': jumlah, + 'catatan': catatan, + 'url': url, + 'userid': userid, + 'sender': sender, + 'companyid': M_CompanyID, + 'fileName': fileName, + 'fileSize': fileSize, + 'fileEkstension': fileExtension + }; + // paramPostInUrl = + // "?tanggal=$tanggal&tipe=$tipe&kategoriid=$kategoriid&jumlah=$jumlah&catatan=$catatan&url=$url&userid=$userid&sender=$sender"; } } - paramPostInUrl += "&companyid=$M_CompanyID"; + // paramPostInUrl += "&companyid=$M_CompanyID"; // /?tanggal=2023-12-29&tipe=KREDIT&kategoriid=3&jumlah=5000&catatan=Lakban%20Besar&url=&userid=1&sender= - final service = - "${Constant.baseUrlDevone}transaction/addtransaction/$paramPostInUrl"; - final resp = await get( - // param: { - // "": "", - // }, - service: service, - ); + final service = "${Constant.baseUrlDevone}transaction/addtransaction"; + final resp = await post( + // param: { + // "": "", + // }, + service: service, + param: prm); - print("url insert transaksi : $service"); + print("prm insert transaksi : ${jsonEncode(prm)}"); // final result = List.empty(growable: true); // resp['data'].forEach((e) { @@ -127,7 +163,6 @@ class TransaksiRepository extends BaseRepository { String id, String userid, ) async { - final service = "${Constant.baseUrlDevone}transaction/deletetransaction/?id=$id&userid=$userid"; final resp = await get( @@ -145,7 +180,7 @@ class TransaksiRepository extends BaseRepository { // result.add(model); // }); - if(resp['status'] != "OK"){ + if (resp['status'] != "OK") { return resp['message']; } diff --git a/app_petty_cash/lib/screen/camera/coba_camera.dart b/app_petty_cash/lib/screen/camera/coba_camera.dart new file mode 100644 index 0000000..a90d3d3 --- /dev/null +++ b/app_petty_cash/lib/screen/camera/coba_camera.dart @@ -0,0 +1,128 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:io' as io; + +import 'package:app_petty_cash/app/constant.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:mime/mime.dart'; + +class CobaCamera extends HookConsumerWidget { + const CobaCamera({super.key}); + @override + Widget build(BuildContext context, WidgetRef ref) { + final photo = useState(null); + final photoBase64 = useState(""); + final previewLoading = useState(false); + getBase64() async { + // List imageBytes = await photo.value?.readAsBytes(); + if (photo.value != null) { + final bytes = io.File(photo.value!.path).readAsBytesSync(); + String base64Image = base64Encode(bytes); + photoBase64.value = base64Image; + final file = File(photo.value!.path); + print(file.lengthSync() / 1000000); + print(await photo.value!.length()); + + // await getExternalStorageDirectory(); + // print(await photo.value!.saveTo(path)); + print(base64Image); + } + } + + final ImagePicker _picker = ImagePicker(); + pickImage() async { + previewLoading.value = true; + final XFile? pickedFile = await _picker.pickImage( + source: ImageSource.camera, + ); + photo.value = pickedFile; + // final Directory appDocumentsDir = + // await getApplicationDocumentsDirectory(); + // print(appDocumentsDir); + // await pickedFile!.saveTo(appDocumentsDir.path); + await getBase64(); + previewLoading.value = false; + } + + browseImage() async { + previewLoading.value = true; + + final XFile? pickedFile = await _picker.pickImage( + source: ImageSource.gallery, + ); + photo.value = pickedFile; + getBase64(); + previewLoading.value = false; + } + + return Scaffold( + body: SingleChildScrollView( + child: SafeArea( + minimum: const EdgeInsets.all(20), + child: Column( + children: [ + Container( + height: Constant.getActualYPhone(context: context, y: 500), + width: MediaQuery.of(context).size.width, + color: Colors.red, + child: LayoutBuilder( + builder: (context, constraints) { + final String? mime = + lookupMimeType(photo.value?.path ?? ""); + + return Semantics( + label: 'image_picker_example_picked_image', + child: (mime != null + ? (mime.startsWith('image/')) + ? Image.file( + // image: AssetImage(photo.value!.path), + File(photo.value!.path), + frameBuilder: (context, child, frame, + wasSynchronouslyLoaded) { + return (wasSynchronouslyLoaded) + ? Center( + child: Text("Loadinga"), + ) + : Container( + child: child, + ); + }, + errorBuilder: (BuildContext context, + Object error, StackTrace? stackTrace) { + return const Center( + child: Text( + 'This image type is not supported')); + }, + ) + : null + : null)); + }, + ), + ), + // photo.value != null + // ? PhotoView(imageProvider: AssetImage(photo.value!.path)) + // : Container(), + Row( + children: [ + ElevatedButton( + onPressed: () { + pickImage(); + }, + child: Text("Take a picture")), + ElevatedButton( + onPressed: () { + browseImage(); + }, + child: Text("Browse a picture")) + ], + ), + ], + ), + ), + ), + ); + } +} diff --git a/app_petty_cash/lib/screen/camera/example.dart b/app_petty_cash/lib/screen/camera/example.dart new file mode 100644 index 0000000..e792072 --- /dev/null +++ b/app_petty_cash/lib/screen/camera/example.dart @@ -0,0 +1,542 @@ +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:mime/mime.dart'; +import 'package:video_player/video_player.dart'; + +class CamerExample extends StatefulWidget { + const CamerExample({super.key}); + + @override + State createState() => _CamerExampleState(); +} + +class _CamerExampleState extends State { + List? _mediaFileList; + + void _setImageFileListFromFile(XFile? value) { + _mediaFileList = value == null ? null : [value]; + } + + dynamic _pickImageError; + bool isVideo = false; + + VideoPlayerController? _controller; + VideoPlayerController? _toBeDisposed; + String? _retrieveDataError; + + final ImagePicker _picker = ImagePicker(); + final TextEditingController maxWidthController = TextEditingController(); + final TextEditingController maxHeightController = TextEditingController(); + final TextEditingController qualityController = TextEditingController(); + + Future _playVideo(XFile? file) async { + if (file != null && mounted) { + await _disposeVideoController(); + late VideoPlayerController controller; + if (kIsWeb) { + controller = VideoPlayerController.networkUrl(Uri.parse(file.path)); + } else { + controller = VideoPlayerController.file(File(file.path)); + } + _controller = controller; + // In web, most browsers won't honor a programmatic call to .play + // if the video has a sound track (and is not muted). + // Mute the video so it auto-plays in web! + // This is not needed if the call to .play is the result of user + // interaction (clicking on a "play" button, for example). + const double volume = kIsWeb ? 0.0 : 1.0; + await controller.setVolume(volume); + await controller.initialize(); + await controller.setLooping(true); + await controller.play(); + setState(() {}); + } + } + + Future _onImageButtonPressed( + ImageSource source, { + required BuildContext context, + bool isMultiImage = false, + bool isMedia = false, + }) async { + if (_controller != null) { + await _controller!.setVolume(0.0); + } + if (context.mounted) { + if (isVideo) { + final XFile? file = await _picker.pickVideo( + source: source, maxDuration: const Duration(seconds: 10)); + await _playVideo(file); + } else if (isMultiImage) { + await _displayPickImageDialog(context, + (double? maxWidth, double? maxHeight, int? quality) async { + try { + final List pickedFileList = isMedia + ? await _picker.pickMultipleMedia( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: quality, + ) + : await _picker.pickMultiImage( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: quality, + ); + setState(() { + _mediaFileList = pickedFileList; + }); + } catch (e) { + setState(() { + _pickImageError = e; + }); + } + }); + } else if (isMedia) { + await _displayPickImageDialog(context, + (double? maxWidth, double? maxHeight, int? quality) async { + try { + final List pickedFileList = []; + final XFile? media = await _picker.pickMedia( + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: quality, + ); + if (media != null) { + pickedFileList.add(media); + setState(() { + _mediaFileList = pickedFileList; + }); + } + } catch (e) { + setState(() { + _pickImageError = e; + }); + } + }); + } else { + await _displayPickImageDialog(context, + (double? maxWidth, double? maxHeight, int? quality) async { + try { + final XFile? pickedFile = await _picker.pickImage( + source: source, + maxWidth: maxWidth, + maxHeight: maxHeight, + imageQuality: quality, + ); + setState(() { + _setImageFileListFromFile(pickedFile); + }); + } catch (e) { + setState(() { + _pickImageError = e; + }); + } + }); + } + } + } + + @override + void deactivate() { + if (_controller != null) { + _controller!.setVolume(0.0); + _controller!.pause(); + } + super.deactivate(); + } + + @override + void dispose() { + _disposeVideoController(); + maxWidthController.dispose(); + maxHeightController.dispose(); + qualityController.dispose(); + super.dispose(); + } + + Future _disposeVideoController() async { + if (_toBeDisposed != null) { + await _toBeDisposed!.dispose(); + } + _toBeDisposed = _controller; + _controller = null; + } + + Widget _previewVideo() { + final Text? retrieveError = _getRetrieveErrorWidget(); + if (retrieveError != null) { + return retrieveError; + } + if (_controller == null) { + return const Text( + 'You have not yet picked a video', + textAlign: TextAlign.center, + ); + } + return Padding( + padding: const EdgeInsets.all(10.0), + child: AspectRatioVideo(_controller), + ); + } + + Widget _previewImages() { + final Text? retrieveError = _getRetrieveErrorWidget(); + if (retrieveError != null) { + return retrieveError; + } + if (_mediaFileList != null) { + return Semantics( + label: 'image_picker_example_picked_images', + child: ListView.builder( + key: UniqueKey(), + itemBuilder: (BuildContext context, int index) { + final String? mime = lookupMimeType(_mediaFileList![index].path); + + // Why network for web? + // See https://pub.dev/packages/image_picker_for_web#limitations-on-the-web-platform + return Semantics( + label: 'image_picker_example_picked_image', + child: kIsWeb + ? Image.network(_mediaFileList![index].path) + : (mime == null || mime.startsWith('image/') + ? Image.file( + File(_mediaFileList![index].path), + errorBuilder: (BuildContext context, Object error, + StackTrace? stackTrace) { + return const Center( + child: + Text('This image type is not supported')); + }, + ) + : _buildInlineVideoPlayer(index)), + ); + }, + itemCount: _mediaFileList!.length, + ), + ); + } else if (_pickImageError != null) { + return Text( + 'Pick image error: $_pickImageError', + textAlign: TextAlign.center, + ); + } else { + return const Text( + 'You have not yet picked an image.', + textAlign: TextAlign.center, + ); + } + } + + Widget _buildInlineVideoPlayer(int index) { + final VideoPlayerController controller = + VideoPlayerController.file(File(_mediaFileList![index].path)); + const double volume = kIsWeb ? 0.0 : 1.0; + controller.setVolume(volume); + controller.initialize(); + controller.setLooping(true); + controller.play(); + return Center(child: AspectRatioVideo(controller)); + } + + Widget _handlePreview() { + if (isVideo) { + return _previewVideo(); + } else { + return _previewImages(); + } + } + + Future retrieveLostData() async { + final LostDataResponse response = await _picker.retrieveLostData(); + if (response.isEmpty) { + return; + } + if (response.file != null) { + if (response.type == RetrieveType.video) { + isVideo = true; + await _playVideo(response.file); + } else { + isVideo = false; + setState(() { + if (response.files == null) { + _setImageFileListFromFile(response.file); + } else { + _mediaFileList = response.files; + } + }); + } + } else { + _retrieveDataError = response.exception!.code; + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("Camera example"), + ), + body: Center( + child: !kIsWeb && defaultTargetPlatform == TargetPlatform.android + ? FutureBuilder( + future: retrieveLostData(), + builder: (BuildContext context, AsyncSnapshot snapshot) { + switch (snapshot.connectionState) { + case ConnectionState.none: + case ConnectionState.waiting: + return const Text( + 'You have not yet picked an image.', + textAlign: TextAlign.center, + ); + case ConnectionState.done: + return _handlePreview(); + case ConnectionState.active: + if (snapshot.hasError) { + return Text( + 'Pick image/video error: ${snapshot.error}}', + textAlign: TextAlign.center, + ); + } else { + return const Text( + 'You have not yet picked an image.', + textAlign: TextAlign.center, + ); + } + } + }, + ) + : _handlePreview(), + ), + floatingActionButton: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Semantics( + label: 'image_picker_example_from_gallery', + child: FloatingActionButton( + onPressed: () { + isVideo = false; + _onImageButtonPressed(ImageSource.gallery, context: context); + }, + heroTag: 'image0', + tooltip: 'Pick Image from gallery', + child: const Icon(Icons.photo), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + onPressed: () { + isVideo = false; + _onImageButtonPressed( + ImageSource.gallery, + context: context, + isMultiImage: true, + isMedia: true, + ); + }, + heroTag: 'multipleMedia', + tooltip: 'Pick Multiple Media from gallery', + child: const Icon(Icons.photo_library), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + onPressed: () { + isVideo = false; + _onImageButtonPressed( + ImageSource.gallery, + context: context, + isMedia: true, + ); + }, + heroTag: 'media', + tooltip: 'Pick Single Media from gallery', + child: const Icon(Icons.photo_library), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + onPressed: () { + isVideo = false; + _onImageButtonPressed( + ImageSource.gallery, + context: context, + isMultiImage: true, + ); + }, + heroTag: 'image1', + tooltip: 'Pick Multiple Image from gallery', + child: const Icon(Icons.photo_library), + ), + ), + if (_picker.supportsImageSource(ImageSource.camera)) + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + onPressed: () { + isVideo = false; + _onImageButtonPressed(ImageSource.camera, context: context); + }, + heroTag: 'image2', + tooltip: 'Take a Photo', + child: const Icon(Icons.camera_alt), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + backgroundColor: Colors.red, + onPressed: () { + isVideo = true; + _onImageButtonPressed(ImageSource.gallery, context: context); + }, + heroTag: 'video0', + tooltip: 'Pick Video from gallery', + child: const Icon(Icons.video_library), + ), + ), + if (_picker.supportsImageSource(ImageSource.camera)) + Padding( + padding: const EdgeInsets.only(top: 16.0), + child: FloatingActionButton( + backgroundColor: Colors.red, + onPressed: () { + isVideo = true; + _onImageButtonPressed(ImageSource.camera, context: context); + }, + heroTag: 'video1', + tooltip: 'Take a Video', + child: const Icon(Icons.videocam), + ), + ), + ], + ), + ); + } + + Text? _getRetrieveErrorWidget() { + if (_retrieveDataError != null) { + final Text result = Text(_retrieveDataError!); + _retrieveDataError = null; + return result; + } + return null; + } + + Future _displayPickImageDialog( + BuildContext context, OnPickImageCallback onPick) async { + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Add optional parameters'), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + controller: maxWidthController, + keyboardType: + const TextInputType.numberWithOptions(decimal: true), + decoration: const InputDecoration( + hintText: 'Enter maxWidth if desired'), + ), + TextField( + controller: maxHeightController, + keyboardType: + const TextInputType.numberWithOptions(decimal: true), + decoration: const InputDecoration( + hintText: 'Enter maxHeight if desired'), + ), + TextField( + controller: qualityController, + keyboardType: TextInputType.number, + decoration: const InputDecoration( + hintText: 'Enter quality if desired'), + ), + ], + ), + actions: [ + TextButton( + child: const Text('CANCEL'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + TextButton( + child: const Text('PICK'), + onPressed: () { + final double? width = maxWidthController.text.isNotEmpty + ? double.parse(maxWidthController.text) + : null; + final double? height = maxHeightController.text.isNotEmpty + ? double.parse(maxHeightController.text) + : null; + final int? quality = qualityController.text.isNotEmpty + ? int.parse(qualityController.text) + : null; + onPick(width, height, quality); + Navigator.of(context).pop(); + }), + ], + ); + }); + } +} + +typedef OnPickImageCallback = void Function( + double? maxWidth, double? maxHeight, int? quality); + +class AspectRatioVideo extends StatefulWidget { + const AspectRatioVideo(this.controller, {super.key}); + + final VideoPlayerController? controller; + + @override + AspectRatioVideoState createState() => AspectRatioVideoState(); +} + +class AspectRatioVideoState extends State { + VideoPlayerController? get controller => widget.controller; + bool initialized = false; + + void _onVideoControllerUpdate() { + if (!mounted) { + return; + } + if (initialized != controller!.value.isInitialized) { + initialized = controller!.value.isInitialized; + setState(() {}); + } + } + + @override + void initState() { + super.initState(); + controller!.addListener(_onVideoControllerUpdate); + } + + @override + void dispose() { + controller!.removeListener(_onVideoControllerUpdate); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + if (initialized) { + return Center( + child: AspectRatio( + aspectRatio: controller!.value.aspectRatio, + child: VideoPlayer(controller!), + ), + ); + } else { + return Container(); + } + } +} diff --git a/app_petty_cash/lib/screen/report/report_screen.dart b/app_petty_cash/lib/screen/report/report_screen.dart index 658f3e5..963540b 100644 --- a/app_petty_cash/lib/screen/report/report_screen.dart +++ b/app_petty_cash/lib/screen/report/report_screen.dart @@ -82,373 +82,337 @@ class ReportScreen extends HookConsumerWidget { ), drawer: CustomDrawer(), body: SafeArea( - child: Padding( - padding: EdgeInsets.all(20), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Tanggal Awal', - style: Constant.body1(context: context).copyWith( - fontWeight: FontWeight.w600, color: Constant.textBlack), - ), - SizedBox( - height: Constant.getActualYPhone(context: context, y: 10), - ), - // Tanggal Awal - Row( - children: [ - Expanded( - child: TextField( - controller: ctrlTglAwal, - decoration: InputDecoration( - hintStyle: - Constant.body2_400(context: context).copyWith( - color: Colors.orange, - ), - labelStyle: - Constant.body2_400(context: context).copyWith( - color: Colors.orange, - ), - border: OutlineInputBorder( - borderSide: BorderSide( + child: SingleChildScrollView( + child: Padding( + padding: EdgeInsets.all(20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Tanggal Awal', + style: Constant.body1(context: context).copyWith( + fontWeight: FontWeight.w600, color: Constant.textBlack), + ), + SizedBox( + height: Constant.getActualYPhone(context: context, y: 10), + ), + // Tanggal Awal + Row( + children: [ + Expanded( + child: TextField( + controller: ctrlTglAwal, + decoration: InputDecoration( + hintStyle: + Constant.body2_400(context: context).copyWith( color: Colors.orange, - width: 1, ), - ), - focusedBorder: OutlineInputBorder( - borderSide: BorderSide( + labelStyle: + Constant.body2_400(context: context).copyWith( color: Colors.orange, - width: 1, ), - ), - // labelText: "Tanggal Awal", - hintText: 'Tanggal Awal', - // suffixIcon: isLoadingFilterScope.value - // ? SizedBox( - // width: Constant.getActualXPhone( - // context: context, - // x: 4, - // ), - // height: Constant.getActualYPhone( - // context: context, - // y: 4, - // ), - // child: CircularProgressIndicator( - // color: Constant.textRed, - // ), - // ) - // : Icon( - // Icons.calendar_month_sharp, - // color: Constant.colorIconDate, - // ), - ), - onTap: () async { - final selectedDateAwal = await showDatePicker( - // locale: const Locale("en-CA"), - // locale: , - context: context, - initialEntryMode: DatePickerEntryMode.calendarOnly, - firstDate: DateTime(2000), - lastDate: DateTime(2100), - - initialDate: (ctrlTglAwal.text.isEmpty) - ? DateTime.now() - : tglAwal.value, - ); - - if (selectedDateAwal != null) { - String formattedDate = DateFormat('dd-MM-yyyy') - .format(selectedDateAwal); - // ctrlTglAwal.text = - // selectedDateAwal.toString().split(' ')[0]; - ctrlTglAwal.text = formattedDate; - tglAwal.value = selectedDateAwal; - tglAwalTmp.value = selectedDateAwal.toString(); - } - - if (selectedDateAwal == null) { - print('cancel button'); - return; - } - }, - ), - ), - ], - ), - - SizedBox( - height: Constant.getActualYPhone(context: context, y: 20), - ), - - Text( - 'Tanggal Akhir', - style: Constant.body1(context: context).copyWith( - fontWeight: FontWeight.w600, color: Constant.textBlack), - ), - SizedBox( - height: Constant.getActualYPhone(context: context, y: 10), - ), - // Tanggal Akhir - Row( - children: [ - Expanded( - child: TextField( - controller: ctrlTglAkhir, - decoration: InputDecoration( - hintStyle: - Constant.body2_400(context: context).copyWith( - color: Colors.orange, - ), - labelStyle: - Constant.body2_400(context: context).copyWith( - color: Colors.orange, - ), - border: OutlineInputBorder( - borderSide: BorderSide( - color: Colors.orange, - width: 1, - ), - ), - focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: Colors.orange, - width: 1, - ), - ), - // labelText: "Tanggal Awal", - hintText: 'Tanggal Akhir', - // suffixIcon: isLoadingFilterScope.value - // ? SizedBox( - // width: Constant.getActualXPhone( - // context: context, - // x: 4, - // ), - // height: Constant.getActualYPhone( - // context: context, - // y: 4, - // ), - // child: CircularProgressIndicator( - // color: Constant.textRed, - // ), - // ) - // : Icon( - // Icons.calendar_month_sharp, - // color: Constant.colorIconDate, - // ), - ), - onTap: () async { - final selectedDateAkhir = await showDatePicker( - // locale: const Locale("en-CA"), - // locale: , - context: context, - initialEntryMode: DatePickerEntryMode.calendarOnly, - firstDate: DateTime(2000), - lastDate: DateTime(2100), - - initialDate: (ctrlTglAkhir.text.isEmpty) - ? DateTime.now() - : tglAkhir.value, - ); - - if (selectedDateAkhir != null) { - String formattedDate = DateFormat('dd-MM-yyyy') - .format(selectedDateAkhir); - // ctrlTglAkhir.text = - // selectedDateAkhir.toString().split(' ')[0]; - ctrlTglAkhir.text = formattedDate; - tglAkhir.value = selectedDateAkhir; - tglAkhirTmp.value = selectedDateAkhir.toString(); - } - - if (selectedDateAkhir == null) { - print('cancel button'); - return; - } - }, - ), - ), - ], - ), - - Spacer(), - - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: ElevatedButton( - style: ButtonStyle( - backgroundColor: MaterialStateColor.resolveWith( - (states) => Colors.white), - // side: MaterialStateBorderSide.resolveWith( - // (states) => BorderSide(color: Colors.green), - // ), - - // backgroundColor: MaterialStateColor.resolveWith( - // (st) => Constant.pcBtnBackgroundColor), - shape: - MaterialStateProperty.all( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - side: BorderSide( - color: Colors.green, + border: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.orange, + width: 1, ), ), - ), - shadowColor: - MaterialStateProperty.all(Color(0xffff48423d)), - elevation: MaterialStateProperty.all(4.0), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - width: Constant.getActualXPhone( - context: context, x: 16), - height: Constant.getActualYPhone( - context: context, y: 16), - // decoration: BoxDecoration(color: Colors.grey), - child: Image.asset( - "images/logo_excel.png", - fit: BoxFit.fill, - // scale: 1, + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.orange, + width: 1, ), ), - SizedBox( - width: Constant.getActualXPhone( - context: context, x: 6), + // labelText: "Tanggal Awal", + hintText: 'Tanggal Awal', + // suffixIcon: isLoadingFilterScope.value + // ? SizedBox( + // width: Constant.getActualXPhone( + // context: context, + // x: 4, + // ), + // height: Constant.getActualYPhone( + // context: context, + // y: 4, + // ), + // child: CircularProgressIndicator( + // color: Constant.textRed, + // ), + // ) + // : Icon( + // Icons.calendar_month_sharp, + // color: Constant.colorIconDate, + // ), + ), + onTap: () async { + final selectedDateAwal = await showDatePicker( + // locale: const Locale("en-CA"), + // locale: , + context: context, + initialEntryMode: + DatePickerEntryMode.calendarOnly, + firstDate: DateTime(2000), + lastDate: DateTime(2100), + + initialDate: (ctrlTglAwal.text.isEmpty) + ? DateTime.now() + : tglAwal.value, + ); + + if (selectedDateAwal != null) { + String formattedDate = DateFormat('dd-MM-yyyy') + .format(selectedDateAwal); + // ctrlTglAwal.text = + // selectedDateAwal.toString().split(' ')[0]; + ctrlTglAwal.text = formattedDate; + tglAwal.value = selectedDateAwal; + tglAwalTmp.value = selectedDateAwal.toString(); + } + + if (selectedDateAwal == null) { + print('cancel button'); + return; + } + }, + ), + ), + ], + ), + + SizedBox( + height: Constant.getActualYPhone(context: context, y: 20), + ), + + Text( + 'Tanggal Akhir', + style: Constant.body1(context: context).copyWith( + fontWeight: FontWeight.w600, color: Constant.textBlack), + ), + SizedBox( + height: Constant.getActualYPhone(context: context, y: 10), + ), + // Tanggal Akhir + Row( + children: [ + Expanded( + child: TextField( + controller: ctrlTglAkhir, + decoration: InputDecoration( + hintStyle: + Constant.body2_400(context: context).copyWith( + color: Colors.orange, ), - Text( - 'Excel', - style: Constant.body1(context: context).copyWith( + labelStyle: + Constant.body2_400(context: context).copyWith( + color: Colors.orange, + ), + border: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.orange, + width: 1, + ), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.orange, + width: 1, + ), + ), + // labelText: "Tanggal Awal", + hintText: 'Tanggal Akhir', + // suffixIcon: isLoadingFilterScope.value + // ? SizedBox( + // width: Constant.getActualXPhone( + // context: context, + // x: 4, + // ), + // height: Constant.getActualYPhone( + // context: context, + // y: 4, + // ), + // child: CircularProgressIndicator( + // color: Constant.textRed, + // ), + // ) + // : Icon( + // Icons.calendar_month_sharp, + // color: Constant.colorIconDate, + // ), + ), + onTap: () async { + final selectedDateAkhir = await showDatePicker( + // locale: const Locale("en-CA"), + // locale: , + context: context, + initialEntryMode: + DatePickerEntryMode.calendarOnly, + firstDate: DateTime(2000), + lastDate: DateTime(2100), + + initialDate: (ctrlTglAkhir.text.isEmpty) + ? DateTime.now() + : tglAkhir.value, + ); + + if (selectedDateAkhir != null) { + String formattedDate = DateFormat('dd-MM-yyyy') + .format(selectedDateAkhir); + // ctrlTglAkhir.text = + // selectedDateAkhir.toString().split(' ')[0]; + ctrlTglAkhir.text = formattedDate; + tglAkhir.value = selectedDateAkhir; + tglAkhirTmp.value = selectedDateAkhir.toString(); + } + + if (selectedDateAkhir == null) { + print('cancel button'); + return; + } + }, + ), + ), + ], + ), + ], + ), + ), + ), + ), + bottomNavigationBar: BottomAppBar( + height: 150, + child: Container( + child: Padding( + padding: EdgeInsets.only( + // right: Constant.getActualXPhone(context: context, x: 27), + // left: Constant.getActualXPhone(context: context, x: 27), + // bottom: Constant.getActualYPhone(context: context, y: 32), + // top: Constant.getActualYPhone(context: context, y: 24), + ), + child: Column( + children: [ + // Excel + Container( + width: Constant.getActualXPhone(context: context, x: 336), + height: Constant.getActualYPhone(context: context, y: 42), + child: ElevatedButton( + style: ButtonStyle( + backgroundColor: MaterialStateColor.resolveWith( + (states) => Colors.white), + // side: MaterialStateBorderSide.resolveWith( + // (states) => BorderSide(color: Colors.green), + // ), + + // backgroundColor: MaterialStateColor.resolveWith( + // (st) => Constant.pcBtnBackgroundColor), + shape: + MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide( + color: Colors.green, + ), + ), + ), + shadowColor: + MaterialStateProperty.all(Color(0xffff48423d)), + elevation: MaterialStateProperty.all(4.0), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: Constant.getActualXPhone( + context: context, x: 16), + height: Constant.getActualYPhone( + context: context, y: 16), + // decoration: BoxDecoration(color: Colors.grey), + child: Image.asset( + "images/logo_excel.png", + fit: BoxFit.fill, + // scale: 1, + ), + ), + SizedBox( + height: Constant.getActualXPhone( + context: context, x: 8), + ), + Text( + 'Download Report (xls) ', + style: Constant.body1(context: context).copyWith( + fontWeight: FontWeight.w600, + color: Colors.green, + ), + ), + ], + ), + onPressed: () {}, + ), + ), + + SizedBox( + height: Constant.getActualYPhone(context: context, y: 20), + ), + + // PDF + Container( + width: Constant.getActualXPhone(context: context, x: 336), + height: Constant.getActualYPhone(context: context, y: 42), + child: ElevatedButton( + style: ButtonStyle( + backgroundColor: MaterialStateColor.resolveWith( + (st) => Constant.pcBtnBackgroundColor), + shape: + MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide( + color: Constant.pcBtnBackgroundColor, + ), + ), + ), + shadowColor: + MaterialStateProperty.all(Color(0xffff48423d)), + elevation: MaterialStateProperty.all(4.0), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: Constant.getActualXPhone( + context: context, x: 16), + height: Constant.getActualYPhone( + context: context, y: 16), + // decoration: BoxDecoration(color: Colors.grey), + child: Image.asset( + "images/logo_pdf.png", + fit: BoxFit.fill, + // scale: 1, + ), + ), + SizedBox( + height: Constant.getActualXPhone( + context: context, x: 8), + ), + Text( + 'Download Report (PDF)', + style: Constant.body1(context: context).copyWith( fontWeight: FontWeight.w600, - color: Colors.green, - ), - ), - ], - ), - onPressed: () async { - M_CompanyID = await getCompanyID(); - if (M_CompanyID == "0") { - SanckbarWidget( - context, 'Invalid Company', snackbarType.error); - return; - } - // Awal - DateTime parsedDateAwal = - DateFormat('dd-MM-yyyy').parse( - ctrlTglAwal.value.text.toString(), - ); - String formattedDateAwal = - DateFormat('yyyy-MM-dd').format(parsedDateAwal); - - // Akhir - DateTime parsedDateAkhir = - DateFormat('dd-MM-yyyy').parse( - ctrlTglAwal.value.text.toString(), - ); - String formattedDateAkhir = - DateFormat('yyyy-MM-dd').format(parsedDateAkhir); - - String url = - "https://${Constant.baseUrlDevoneReport}/birt/run?__report=report/one/pettycash/rpt_r_pt_001.rptdesign&__format=xls&PStartDate=$formattedDateAwal&PEndDate=$formattedDateAkhir&PCompanyID=$M_CompanyID&username=adminsas%20"; - if (!await launchUrl(Uri.parse(url))) { - // throw Exception('Could not launch $url'); - SanckbarWidget(context, 'Could not launch $url', - snackbarType.error); - } - // https://devone.aplikasi.web.id/birt/run?__report=report/one/pettycash/rpt_r_pt_001.rptdesign&__format=pdf&PStartDate=2023-11-01&PEndDate=2023-12-30&PCompanyID=0&username=adminsas%20&tm=1701327096267 - }, - ), - ), - - SizedBox( - width: Constant.getActualXPhone(context: context, x: 10), - ), - - // PDF - Expanded( - child: ElevatedButton( - style: ButtonStyle( - backgroundColor: MaterialStateColor.resolveWith( - (st) => Constant.pcBtnBackgroundColor), - shape: - MaterialStateProperty.all( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - side: BorderSide( - color: Constant.pcBtnBackgroundColor, - ), - ), + color: Constant.white), ), - shadowColor: - MaterialStateProperty.all(Color(0xffff48423d)), - elevation: MaterialStateProperty.all(4.0), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - width: Constant.getActualXPhone( - context: context, x: 16), - height: Constant.getActualYPhone( - context: context, y: 16), - // decoration: BoxDecoration(color: Colors.grey), - child: Image.asset( - "images/logo_pdf.png", - fit: BoxFit.fill, - // scale: 1, - ), - ), - SizedBox( - width: Constant.getActualXPhone( - context: context, x: 6), - ), - Text( - 'PDF', - style: Constant.body1(context: context).copyWith( - fontWeight: FontWeight.w600, - color: Constant.white), - ), - ], - ), - onPressed: () async { - M_CompanyID = await getCompanyID(); - if (M_CompanyID == "0") { - SanckbarWidget( - context, 'Invalid Company', snackbarType.error); - return; - } - // Awal - DateTime parsedDateAwal = - DateFormat('dd-MM-yyyy').parse( - ctrlTglAwal.value.text.toString(), - ); - String formattedDateAwal = - DateFormat('yyyy-MM-dd').format(parsedDateAwal); - - // Akhir - DateTime parsedDateAkhir = - DateFormat('dd-MM-yyyy').parse( - ctrlTglAwal.value.text.toString(), - ); - String formattedDateAkhir = - DateFormat('yyyy-MM-dd').format(parsedDateAkhir); - - String url = - "https://${Constant.baseUrlDevoneReport}/birt/run?__report=report/one/pettycash/rpt_r_pt_001.rptdesign&__format=pdf&PStartDate=$formattedDateAwal&PEndDate=$formattedDateAkhir&PCompanyID=$M_CompanyID&username=adminsas%20"; - if (!await launchUrl(Uri.parse(url))) { - // throw Exception('Could not launch $url'); - SanckbarWidget(context, 'Could not launch $url', - snackbarType.error); - } - }, + ], ), + onPressed: () async { + String url = "https://pub.dev/packages?q=url+launcher"; + if (!await launchUrl(Uri.parse(url))) { + // throw Exception('Could not launch $url'); + SanckbarWidget(context, 'Could not launch $url', + snackbarType.error); + } + }, ), - ], - ), - ], + ), + ], + ), ), ), ), diff --git a/app_petty_cash/lib/screen/transaksi/insert_transaksi_provider.dart b/app_petty_cash/lib/screen/transaksi/insert_transaksi_provider.dart index c38e916..4510a1b 100644 --- a/app_petty_cash/lib/screen/transaksi/insert_transaksi_provider.dart +++ b/app_petty_cash/lib/screen/transaksi/insert_transaksi_provider.dart @@ -1,4 +1,3 @@ - import 'package:equatable/equatable.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -44,15 +43,18 @@ class InsertTransaksiNotifier extends StateNotifier { }) : super(InsertTransaksiStateInit()); void insertTransaksi( - String tanggal, - String tipe, - String kategoriid, - String jumlah, - String catatan, - String userid, - String sender, - String url, - ) async { + {required String tanggal, + required String tipe, + required String kategoriid, + required String jumlah, + required String catatan, + required String userid, + required String sender, + required String url, + required String base64file, + required String fileName, + required String fileSize, + required String fileExtension}) async { try { state = InsertTransaksiStateLoading(); final dio = ref.read(dioProvider); @@ -64,6 +66,10 @@ class InsertTransaksiNotifier extends StateNotifier { catatan, userid, sender, + base64file, + fileName, + fileSize, + fileExtension, url, ); state = InsertTransaksiStateDone(resp: resp); diff --git a/app_petty_cash/lib/screen/transaksi/transaksi_pick_date_widget.dart b/app_petty_cash/lib/screen/transaksi/transaksi_pick_date_widget.dart new file mode 100644 index 0000000..1c4e998 --- /dev/null +++ b/app_petty_cash/lib/screen/transaksi/transaksi_pick_date_widget.dart @@ -0,0 +1,104 @@ +import 'package:app_petty_cash/app/constant.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; + +class TransaksiPickDateWidget extends StatelessWidget { + const TransaksiPickDateWidget( + {super.key, + required this.ctrlTglAwal, + required this.tglAwal, + required this.tglAwalTmp, + required this.isLoading}); + + final TextEditingController ctrlTglAwal; + final ValueNotifier tglAwal; + final ValueNotifier tglAwalTmp; + final ValueNotifier isLoading; + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Expanded( + child: TextField( + readOnly: true, + controller: ctrlTglAwal, + decoration: InputDecoration( + hintStyle: Constant.body2_400(context: context).copyWith( + color: Constant.textGreyv2, + ), + labelStyle: Constant.body2_400(context: context).copyWith( + color: Constant.textGreyv2, + ), + border: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.orange, + width: 1, + ), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Constant.textGreyv2, + width: 1, + ), + ), + + // labelText: "Tanggal Awal", + hintText: 'Tanggal Transaksi', + // suffixIcon: isLoadingFilterScope.value + // ? SizedBox( + // width: Constant.getActualXPhone( + // context: context, + // x: 4, + // ), + // height: Constant.getActualYPhone( + // context: context, + // y: 4, + // ), + // child: CircularProgressIndicator( + // color: Constant.textRed, + // ), + // ) + // : Icon( + // Icons.calendar_month_sharp, + // color: Constant.colorIconDate, + // ), + ), + onTap: !isLoading.value + ? () async { + final selectedDateAwal = await showDatePicker( + keyboardType: TextInputType.none, + // locale: const Locale("en-CA"), + // locale: , + context: context, + initialEntryMode: DatePickerEntryMode.calendarOnly, + firstDate: DateTime(2000), + lastDate: DateTime(2100), + + initialDate: (ctrlTglAwal.text.isEmpty) + ? DateTime.now() + : tglAwal.value, + ); + + if (selectedDateAwal != null) { + String formattedDate = + DateFormat('dd-MM-yyyy').format(selectedDateAwal); + // ctrlTglAwal.text = + // selectedDateAwal.toString().split(' ')[0]; + ctrlTglAwal.text = formattedDate; + tglAwal.value = selectedDateAwal; + tglAwalTmp.value = selectedDateAwal.toString(); + } + + if (selectedDateAwal == null) { + print('cancel button'); + return; + } + } + : null, + ), + ), + ], + ); + } +} diff --git a/app_petty_cash/lib/screen/transaksi/transaksi_screen.dart b/app_petty_cash/lib/screen/transaksi/transaksi_screen.dart index be1089b..fca6e7a 100644 --- a/app_petty_cash/lib/screen/transaksi/transaksi_screen.dart +++ b/app_petty_cash/lib/screen/transaksi/transaksi_screen.dart @@ -1,14 +1,21 @@ -import 'package:app_petty_cash/app/app_extension.dart'; +import 'dart:convert'; +import 'dart:io' as io; +import 'dart:io'; + import 'package:app_petty_cash/app/route.dart'; import 'package:app_petty_cash/model/list_type_model.dart'; import 'package:app_petty_cash/screen/transaksi/insert_transaksi_provider.dart'; import 'package:app_petty_cash/screen/transaksi/list_category_provider.dart'; import 'package:app_petty_cash/screen/transaksi/list_type_provider.dart'; -import 'package:dropdown_button2/dropdown_button2.dart'; +import 'package:app_petty_cash/screen/transaksi/transaksi_pick_date_widget.dart'; +import 'package:app_petty_cash/screen/transaksi/transaksi_select_kategori_widget.dart'; +import 'package:app_petty_cash/screen/transaksi/transaksi_upload_area_widget.dart'; +import 'package:file_picker/file_picker.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_picker/image_picker.dart'; import 'package:intl/intl.dart'; import '../../app/constant.dart'; @@ -16,7 +23,6 @@ import '../../model/list_category_model.dart'; import '../../provider/current_user_provider.dart'; import '../../widget/custom_drawer.dart'; import '../../widget/sankbar_widget.dart'; -import '../login/custom_text_field.dart'; class DummyDropdownTipe { final String options; @@ -44,6 +50,12 @@ class TransaksiScreen extends HookConsumerWidget { final ctrlCatatan = useTextEditingController(text: ""); final ctrlNamaPengirim = useTextEditingController(text: ""); final ctrlCompanyName = useTextEditingController(text: ""); + final fileData = useState(null); + final fileDataBase64 = useState(""); + final isImage = useState(false); + final fileEkstension = useState(""); + final fileSize = useState(0); + final fileName = useState(""); String formattedDate = DateFormat('dd-MM-yyyy').format(DateTime.now()); @@ -182,6 +194,172 @@ class TransaksiScreen extends HookConsumerWidget { final userIDLogin = ref.read(currentUserProvider)?.model.M_UserID ?? "0"; + getBase64() async { + // List imageBytes = await fileData.value?.readAsBytes(); + if (fileData.value != null) { + final bytes = io.File(fileData.value!.path).readAsBytesSync(); + String base64Image = base64Encode(await fileData.value!.readAsBytes()); + fileDataBase64.value = base64Image; + final file = File(fileData.value!.path); + print(file.lengthSync() / 1000000); + print(await fileData.value!.length()); + fileSize.value = await fileData.value!.length(); + + // await getExternalStorageDirectory(); + // print(await fileData.value!.saveTo(path)); + print(fileDataBase64.value); + } + } + + final ImagePicker _picker = ImagePicker(); + pickImage() async { + final XFile? pickedFile = await _picker.pickImage( + source: ImageSource.camera, + ); + 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(); + } + } + + browseImage() async { + final XFile? pickedFile = await _picker.pickImage( + source: ImageSource.gallery, + ); + fileData.value = pickedFile; + getBase64(); + } + + pickFile() async { + List imgExt = [ + 'jpg', + 'png', + 'jpeg', + ]; + FilePickerResult? result = await FilePicker.platform.pickFiles( + allowMultiple: false, + type: FileType.custom, + dialogTitle: "Pick a file", + allowedExtensions: [ + ...imgExt, + 'pdf', + 'doc', + 'docx', + 'xls', + 'xlsx', + 'ppt', + 'pptx', + 'txt' + ], + ); + + if (result != null) { + if (result.files.single.size > 10000000) { + SanckbarWidget(context, "File tidak boleh lebih dari 10 MB", + snackbarType.warning); + } else { + // File files = File(result.files.single.path!); + print(result.files.single.extension); + XFile fl = XFile(result.files.single.path!); + fileName.value = result.files.single.name; + + isImage.value = imgExt.contains(result.files.single.extension); + + print(result.files.single.name); + fileData.value = fl; + await getBase64(); + fileEkstension.value = result.files.single.extension ?? ""; + } + } else { + // User canceled the picker + } + } + + insertTransaksi() { + if (selectedListTypeData.value.typeid.toString() == "DEBIT") { + ctrlNamaPengirim.text = ""; + // validasi form + if (ctrlJumlah.text.isEmpty || ctrlCatatan.text.trim().isEmpty) { + SanckbarWidget( + context, "Jumlah dan catatan harus diisi", snackbarType.warning); + return; + } + } else { + if (selectedListTypeData.value.typeid.toString() == "KREDIT") { + selectedListCategory.value.categoryid = "0"; + // validasi form + if (ctrlNamaPengirim.text.trim().isEmpty || + ctrlJumlah.text.isEmpty || + ctrlCatatan.text.trim().isEmpty) { + SanckbarWidget( + context, + "Nama pengirim, jumlah dan catatan harus diisi", + snackbarType.warning); + return; + } + } + } + + DateTime parsedDate = DateFormat('dd-MM-yyyy').parse( + ctrlTglAwal.value.text.toString(), + ); + String formattedDateTransaksi = + DateFormat('yyyy-MM-dd').format(parsedDate); + var param = { + "tgltransaksi": formattedDateTransaksi, + "typeid": selectedListTypeData.value.typeid.toString(), + "categoryid": selectedListCategory.value.categoryid.toString(), + "jumlah": ctrlJumlah.value.text.toString(), + "catatan": ctrlCatatan.value.text.toString(), + // "userid": "1", + "userid": userIDLogin, + "namapengirim": ctrlNamaPengirim.value.text.toString(), + "url": "", + // "base64file": fileDataBase64.value, + "fileName": fileName.value, + "fileExtension": fileEkstension.value, + "fileSize": fileSize.value.toString() + }; + print(param); + // return; + ref.read(insertTransaksiProvider.notifier).insertTransaksi( + tanggal: formattedDateTransaksi, + tipe: selectedListTypeData.value.typeid.toString(), + kategoriid: selectedListCategory.value.categoryid.toString(), + jumlah: ctrlJumlah.value.text.toString(), + catatan: ctrlCatatan.value.text.toString(), + // "1", + userid: userIDLogin, + sender: ctrlNamaPengirim.value.text.toString(), + url: "", + base64file: fileDataBase64.value, + fileName: fileData.value?.name ?? "", + fileExtension: fileEkstension.value, + fileSize: fileSize.value.toString()); + } + return Padding( padding: EdgeInsets.only( top: Constant.getActualYPhone(context: context, y: 30), @@ -204,7 +382,7 @@ class TransaksiScreen extends HookConsumerWidget { child: Padding( padding: EdgeInsets.all(20), child: Container( - height: MediaQuery.of(context).size.height - 10, + // height: MediaQuery.of(context).size.height - 10, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -259,86 +437,11 @@ class TransaksiScreen extends HookConsumerWidget { height: Constant.getActualYPhone(context: context, y: 10), ), // Tanggal Transaksi - Row( - children: [ - Expanded( - child: TextField( - controller: ctrlTglAwal, - decoration: InputDecoration( - hintStyle: - Constant.body2_400(context: context).copyWith( - color: Constant.textGreyv2, - ), - labelStyle: - Constant.body2_400(context: context).copyWith( - color: Constant.textGreyv2, - ), - border: OutlineInputBorder( - borderSide: BorderSide( - color: Colors.orange, - width: 1, - ), - ), - focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: Constant.textGreyv2, - width: 1, - ), - ), - // labelText: "Tanggal Awal", - hintText: 'Tanggal Transaksi', - // suffixIcon: isLoadingFilterScope.value - // ? SizedBox( - // width: Constant.getActualXPhone( - // context: context, - // x: 4, - // ), - // height: Constant.getActualYPhone( - // context: context, - // y: 4, - // ), - // child: CircularProgressIndicator( - // color: Constant.textRed, - // ), - // ) - // : Icon( - // Icons.calendar_month_sharp, - // color: Constant.colorIconDate, - // ), - ), - onTap: () async { - final selectedDateAwal = await showDatePicker( - // locale: const Locale("en-CA"), - // locale: , - context: context, - initialEntryMode: - DatePickerEntryMode.calendarOnly, - firstDate: DateTime(2000), - lastDate: DateTime(2100), - - initialDate: (ctrlTglAwal.text.isEmpty) - ? DateTime.now() - : tglAwal.value, - ); - - if (selectedDateAwal != null) { - String formattedDate = DateFormat('dd-MM-yyyy') - .format(selectedDateAwal); - // ctrlTglAwal.text = - // selectedDateAwal.toString().split(' ')[0]; - ctrlTglAwal.text = formattedDate; - tglAwal.value = selectedDateAwal; - tglAwalTmp.value = selectedDateAwal.toString(); - } - - if (selectedDateAwal == null) { - print('cancel button'); - return; - } - }, - ), - ), - ], + TransaksiPickDateWidget( + ctrlTglAwal: ctrlTglAwal, + tglAwal: tglAwal, + tglAwalTmp: tglAwalTmp, + isLoading: transaksiIsLoading, ), SizedBox( @@ -387,14 +490,16 @@ class TransaksiScreen extends HookConsumerWidget { value: listTypeData.value[i], groupValue: selectedListTypeData.value, - onChanged: (ListType? index) { - if (isMounted()) { - selectedListTypeData.value = - index!; - } else { - return; - } - }, + onChanged: !transaksiIsLoading.value + ? (ListType? index) { + if (isMounted()) { + selectedListTypeData + .value = index!; + } else { + return; + } + } + : null, ), Text( listTypeData.value[i].typename ?? @@ -441,126 +546,10 @@ class TransaksiScreen extends HookConsumerWidget { child: CircularProgressIndicator(), ), ) - : SizedBox( - width: Constant.getActualXPhone( - context: context, x: 390), - child: DropdownButtonHideUnderline( - child: DropdownButton2( - isExpanded: true, - hint: Row( - children: [ - Expanded( - child: Text( - 'Select Item', - style: Constant.body1( - context: context) - .copyWith( - fontWeight: FontWeight.w600, - color: Constant.textBlack), - overflow: TextOverflow.ellipsis, - ), - ), - ], - ), - items: listCategoryData.value - .map((ListCategory option) { - return DropdownMenuItem( - value: option, - child: Text( - option.categoryname ?? "", - style: Constant.body1(context: context) - .copyWith( - color: Constant.textBlack, - fontWeight: FontWeight.w600), - ), - ); - }).toList(), - value: selectedListCategory.value, - onChanged: (ListCategory? newValue) { - // if (newValue) { - selectedListCategory.value = newValue!; - print( - selectedListCategory.value.categoryid); - // } - }, - buttonStyleData: ButtonStyleData( - height: Constant.getActualY( - context: context, y: 56), - width: Constant.getActualX( - context: context, x: 320), - padding: EdgeInsets.only( - left: Constant.getActualX( - context: context, x: 10), - right: Constant.getActualX( - context: context, x: 10), - ), - decoration: BoxDecoration( - color: Constant.white, - border: Border.all( - color: Constant.textBlack, width: 1), - borderRadius: BorderRadius.circular(8), - ), - elevation: 2, - ), - iconStyleData: IconStyleData( - icon: Icon( - Icons.keyboard_arrow_down_outlined, - ), - iconSize: 24, - iconEnabledColor: Constant.textBlack, - iconDisabledColor: Colors.grey, - ), - dropdownStyleData: DropdownStyleData( - maxHeight: Constant.getActualY( - context: context, y: 200), - // width: Constant.getActualX(context: context, x: 320), - padding: EdgeInsets.only( - top: Constant.getActualY( - context: context, y: 10), - left: Constant.getActualX( - context: context, x: 10), - right: Constant.getActualX( - context: context, x: 10), - bottom: Constant.getActualY( - context: context, y: 10), - ), - decoration: BoxDecoration( - color: Constant.white, - borderRadius: BorderRadius.circular(8), - boxShadow: [ - BoxShadow( - color: Colors.grey, - blurRadius: 20.0, - spreadRadius: 2.0, - offset: Offset(0.0, 0.0), - ), - ], - ), - elevation: 8, - offset: const Offset(0, -10), - scrollbarTheme: ScrollbarThemeData( - radius: const Radius.circular(40), - thickness: - MaterialStateProperty.all(6), - thumbVisibility: - MaterialStateProperty.all(true), - ), - ), - menuItemStyleData: MenuItemStyleData( - height: Constant.getActualY( - context: context, y: 56), - padding: EdgeInsets.only( - top: Constant.getActualY( - context: context, y: 10), - left: Constant.getActualX( - context: context, x: 10), - right: Constant.getActualX( - context: context, x: 10), - ), - ), - ), - ), - ), + : TransaksiSelectKategoriWidget( + isLoading: transaksiIsLoading, + listCategoryData: listCategoryData, + selectedListCategory: selectedListCategory), SizedBox( height: @@ -582,6 +571,7 @@ class TransaksiScreen extends HookConsumerWidget { ), TextField( controller: ctrlNamaPengirim, + readOnly: transaksiIsLoading.value, decoration: InputDecoration( hintStyle: Constant.body2_400(context: context).copyWith( @@ -625,6 +615,7 @@ class TransaksiScreen extends HookConsumerWidget { TextField( controller: ctrlJumlah, keyboardType: TextInputType.number, + readOnly: transaksiIsLoading.value, decoration: InputDecoration( hintStyle: Constant.body2_400(context: context).copyWith( @@ -667,6 +658,7 @@ class TransaksiScreen extends HookConsumerWidget { ), TextField( + readOnly: transaksiIsLoading.value, controller: ctrlCatatan, maxLines: 4, decoration: InputDecoration( @@ -700,32 +692,22 @@ class TransaksiScreen extends HookConsumerWidget { ), // Upload File - Container( - width: Constant.getActualXPhone(context: context, x: 390), - height: Constant.getActualYPhone(context: context, y: 83), - decoration: BoxDecoration(color: Constant.bgUploadFile), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Icon( - Icons.upload_outlined, - color: Constant.pcBtnBackgroundColor, - ), - Text( - 'Upload File', - style: Constant.body1(context: context).copyWith( - fontWeight: FontWeight.w600, - color: Constant.pcBtnBackgroundColor), - ) - ], - ), - ), + TransaksiUploadAreaWidget( + isLoading: transaksiIsLoading, + isImage: isImage, + fileData: fileData, + fileDataBase64: fileDataBase64, + pickFile: pickFile, + pickImage: pickImage), - Spacer(), + // Spacer(), Container( + margin: EdgeInsets.only( + top: Constant.getActualYPhone( + context: context, y: 24)), width: Constant.getActualXPhone(context: context, x: 390), + height: Constant.getActualY(context: context, y: 64), child: ElevatedButton( style: ButtonStyle( backgroundColor: MaterialStateColor.resolveWith( @@ -748,7 +730,7 @@ class TransaksiScreen extends HookConsumerWidget { (transaksiIsLoading.value) ? SizedBox( width: Constant.getActualXPhone( - context: context, x: 24), + context: context, x: 32), height: Constant.getActualYPhone( context: context, y: 32), child: CircularProgressIndicator( @@ -764,53 +746,12 @@ class TransaksiScreen extends HookConsumerWidget { ), ], ), - onPressed: () { - if (selectedListTypeData.value.typeid.toString() == - "DEBIT") { - ctrlNamaPengirim.text = ""; - // validasi form - } else { - if (selectedListTypeData.value.typeid.toString() == - "KREDIT") { - selectedListCategory.value.categoryid = "0"; - // validasi form - } - } - - DateTime parsedDate = DateFormat('dd-MM-yyyy').parse( - ctrlTglAwal.value.text.toString(), - ); - String formattedDateTransaksi = - DateFormat('yyyy-MM-dd').format(parsedDate); - var param = { - "tgltransaksi": formattedDateTransaksi, - "typeid": - selectedListTypeData.value.typeid.toString(), - "categoryid": - selectedListCategory.value.categoryid.toString(), - "jumlah": ctrlJumlah.value.text.toString(), - "catatan": ctrlCatatan.value.text.toString(), - // "userid": "1", - "userid": userIDLogin, - "namapengirim": - ctrlNamaPengirim.value.text.toString(), - "url": "", - }; - print(param); - ref - .read(insertTransaksiProvider.notifier) - .insertTransaksi( - formattedDateTransaksi, - selectedListTypeData.value.typeid.toString(), - selectedListCategory.value.categoryid.toString(), - ctrlJumlah.value.text.toString(), - ctrlCatatan.value.text.toString(), - // "1", - userIDLogin, - ctrlNamaPengirim.value.text.toString(), - "", - ); - }, + onPressed: !transaksiIsLoading.value + ? () { + // transaksiIsLoading.value = true; + insertTransaksi(); + } + : null, ), ), ], diff --git a/app_petty_cash/lib/screen/transaksi/transaksi_select_kategori_widget.dart b/app_petty_cash/lib/screen/transaksi/transaksi_select_kategori_widget.dart new file mode 100644 index 0000000..57035e3 --- /dev/null +++ b/app_petty_cash/lib/screen/transaksi/transaksi_select_kategori_widget.dart @@ -0,0 +1,121 @@ +import 'package:app_petty_cash/app/constant.dart'; +import 'package:app_petty_cash/model/list_category_model.dart'; +import 'package:dropdown_button2/dropdown_button2.dart'; +import 'package:flutter/material.dart'; + +class TransaksiSelectKategoriWidget extends StatelessWidget { + const TransaksiSelectKategoriWidget({ + super.key, + required this.listCategoryData, + required this.selectedListCategory, + required this.isLoading, + }); + + final ValueNotifier> listCategoryData; + final ValueNotifier selectedListCategory; + final ValueNotifier isLoading; + + @override + Widget build(BuildContext context) { + return SizedBox( + width: Constant.getActualXPhone(context: context, x: 390), + child: DropdownButtonHideUnderline( + child: DropdownButton2( + isExpanded: true, + hint: Row( + children: [ + Expanded( + child: Text( + 'Select Item', + style: Constant.body1(context: context).copyWith( + fontWeight: FontWeight.w400, color: Constant.textBlack), + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + items: listCategoryData.value.map((ListCategory option) { + return DropdownMenuItem( + value: option, + child: Text( + option.categoryname ?? "", + style: Constant.body1(context: context).copyWith( + color: Constant.textBlack, fontWeight: FontWeight.w400), + ), + ); + }).toList(), + style: Constant.body1(context: context) + .copyWith(color: Constant.textBlack, fontWeight: FontWeight.w400), + value: selectedListCategory.value, + onChanged: !isLoading.value + ? (ListCategory? newValue) { + // if (newValue) { + selectedListCategory.value = newValue!; + print(selectedListCategory.value.categoryid); + // } + } + : null, + buttonStyleData: ButtonStyleData( + height: Constant.getActualY(context: context, y: 80), + width: Constant.getActualX(context: context, x: 320), + padding: EdgeInsets.only( + left: Constant.getActualX(context: context, x: 20), + right: Constant.getActualX(context: context, x: 20), + ), + decoration: BoxDecoration( + color: Constant.white, + border: Border.all(color: Constant.textGrey, width: 1), + borderRadius: BorderRadius.circular(8), + ), + // elevation: 2, + ), + iconStyleData: IconStyleData( + icon: Icon( + Icons.keyboard_arrow_down_outlined, + ), + iconSize: 24, + iconEnabledColor: Constant.textBlack, + iconDisabledColor: Colors.grey, + ), + dropdownStyleData: DropdownStyleData( + maxHeight: Constant.getActualY(context: context, y: 200), + // width: Constant.getActualX(context: context, x: 320), + padding: EdgeInsets.only( + top: Constant.getActualY(context: context, y: 10), + left: Constant.getActualX(context: context, x: 20), + right: Constant.getActualX(context: context, x: 20), + bottom: Constant.getActualY(context: context, y: 10), + ), + decoration: BoxDecoration( + color: Constant.white, + borderRadius: BorderRadius.circular(8), + boxShadow: [ + BoxShadow( + color: Colors.grey, + blurRadius: 20.0, + spreadRadius: 2.0, + offset: Offset(0.0, 0.0), + ), + ], + ), + elevation: 8, + offset: const Offset(0, -10), + scrollbarTheme: ScrollbarThemeData( + radius: const Radius.circular(40), + thickness: MaterialStateProperty.all(6), + thumbVisibility: MaterialStateProperty.all(true), + ), + ), + menuItemStyleData: MenuItemStyleData( + height: Constant.getActualY(context: context, y: 56), + padding: EdgeInsets.only( + top: Constant.getActualY(context: context, y: 10), + left: Constant.getActualX(context: context, x: 20), + right: Constant.getActualX(context: context, x: 20), + ), + ), + ), + ), + ); + } +} diff --git a/app_petty_cash/lib/screen/transaksi/transaksi_upload_area_widget.dart b/app_petty_cash/lib/screen/transaksi/transaksi_upload_area_widget.dart new file mode 100644 index 0000000..33a4a87 --- /dev/null +++ b/app_petty_cash/lib/screen/transaksi/transaksi_upload_area_widget.dart @@ -0,0 +1,167 @@ +import 'dart:io'; + +import 'package:app_petty_cash/app/constant.dart'; +import 'package:flutter/material.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:mime/mime.dart'; + +class TransaksiUploadAreaWidget extends StatelessWidget { + const TransaksiUploadAreaWidget({ + super.key, + required this.isImage, + required this.fileData, + required this.fileDataBase64, + required this.pickFile, + required this.pickImage, + required this.isLoading, + }); + + final ValueNotifier isImage; + final ValueNotifier isLoading; + final ValueNotifier fileData; + final ValueNotifier fileDataBase64; + final Function pickImage; + final Function pickFile; + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: !isLoading.value + ? () { + showModalBottomSheet( + context: context, + builder: (context) { + return Container( + height: Constant.getActualY(context: context, y: 200), + padding: EdgeInsets.all(20), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Column( + children: [ + IconButton( + onPressed: () { + Navigator.pop(context); + pickFile(); + }, + icon: Icon( + Icons.folder_copy_rounded, + size: 50, + color: Constant.pcBtnBackgroundColor, + )), + Text("Browse a file", + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w600, + color: Constant.textBlack)) + ], + ), + Column( + children: [ + IconButton( + onPressed: () { + Navigator.pop(context); + pickImage(); + }, + icon: Icon( + Icons.add_a_photo_rounded, + size: 50, + color: Constant.pcBtnBackgroundColor, + )), + Text("Take a picture", + style: Constant.body1(context: context) + .copyWith( + fontWeight: FontWeight.w600, + color: Constant.textBlack)) + ], + ), + ], + ), + ); + }, + ); + } + : null, + child: Stack( + alignment: AlignmentDirectional.topEnd, + children: [ + Container( + width: Constant.getActualXPhone(context: context, x: 390), + height: Constant.getActualYPhone( + context: context, y: isImage.value ? 200 : 83), + decoration: BoxDecoration(color: Constant.bgUploadFile), + child: Builder(builder: (context) { + final String? mime = lookupMimeType(fileData.value?.path ?? ""); + return Semantics( + label: 'image_picker_example_picked_image', + child: (mime != null + ? (mime.startsWith('image/')) + ? Image.file( + // image: AssetImage(photo.value!.path), + File(fileData.value!.path), + frameBuilder: (context, child, frame, + wasSynchronouslyLoaded) { + return (wasSynchronouslyLoaded) + ? Center( + child: Text("Loadinga"), + ) + : Container( + child: child, + ); + }, + errorBuilder: (BuildContext context, Object error, + StackTrace? stackTrace) { + return const Center( + child: Text( + 'This image type is not supported')); + }, + ) + : Container( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.file_present_rounded, + size: 40, + color: Constant.pcBtnBackgroundColor, + ), + Text( + fileData.value?.name ?? '', + style: Constant.body2_400(context: context), + ) + ], + ), + ) + : Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Icon( + Icons.upload_outlined, + color: Constant.pcBtnBackgroundColor, + ), + Text( + 'Upload File', + style: Constant.body1(context: context).copyWith( + fontWeight: FontWeight.w600, + color: Constant.pcBtnBackgroundColor), + ) + ], + ))); + }), + ), + if (fileData.value != null && !isLoading.value) + IconButton( + onPressed: () { + fileData.value = null; + fileDataBase64.value = ''; + isImage.value = false; + }, + icon: Icon(Icons.cancel_outlined)), + ], + ), + ); + } +} diff --git a/app_petty_cash/linux/flutter/generated_plugin_registrant.cc b/app_petty_cash/linux/flutter/generated_plugin_registrant.cc index f6f23bf..7299b5c 100644 --- a/app_petty_cash/linux/flutter/generated_plugin_registrant.cc +++ b/app_petty_cash/linux/flutter/generated_plugin_registrant.cc @@ -6,9 +6,13 @@ #include "generated_plugin_registrant.h" +#include #include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); + file_selector_plugin_register_with_registrar(file_selector_linux_registrar); g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); diff --git a/app_petty_cash/linux/flutter/generated_plugins.cmake b/app_petty_cash/linux/flutter/generated_plugins.cmake index f16b4c3..786ff5c 100644 --- a/app_petty_cash/linux/flutter/generated_plugins.cmake +++ b/app_petty_cash/linux/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + file_selector_linux url_launcher_linux ) diff --git a/app_petty_cash/macos/Flutter/GeneratedPluginRegistrant.swift b/app_petty_cash/macos/Flutter/GeneratedPluginRegistrant.swift index 997e35d..a7158a1 100644 --- a/app_petty_cash/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/app_petty_cash/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,10 +5,16 @@ import FlutterMacOS import Foundation +import file_selector_macos +import path_provider_foundation import shared_preferences_foundation import url_launcher_macos +import video_player_avfoundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) + FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin")) } diff --git a/app_petty_cash/pubspec.lock b/app_petty_cash/pubspec.lock index 9f45f31..b477586 100644 --- a/app_petty_cash/pubspec.lock +++ b/app_petty_cash/pubspec.lock @@ -85,10 +85,10 @@ packages: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.18.0" convert: dependency: transitive description: @@ -97,6 +97,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.1" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: fedaadfa3a6996f75211d835aaeb8fede285dae94262485698afd832371b9a5e + url: "https://pub.dev" + source: hosted + version: "0.3.3+8" crypto: dependency: transitive description: @@ -105,6 +113,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.3" + csslib: + dependency: transitive + description: + name: csslib + sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb" + url: "https://pub.dev" + source: hosted + version: "1.0.0" cupertino_icons: dependency: "direct main" description: @@ -177,6 +193,38 @@ packages: url: "https://pub.dev" source: hosted version: "6.1.1" + file_selector_linux: + dependency: transitive + description: + name: file_selector_linux + sha256: "045d372bf19b02aeb69cacf8b4009555fb5f6f0b7ad8016e5f46dd1387ddd492" + url: "https://pub.dev" + source: hosted + version: "0.9.2+1" + file_selector_macos: + dependency: transitive + description: + name: file_selector_macos + sha256: b15c3da8bd4908b9918111fa486903f5808e388b8d1c559949f584725a6594d6 + url: "https://pub.dev" + source: hosted + version: "0.9.3+3" + file_selector_platform_interface: + dependency: transitive + description: + name: file_selector_platform_interface + sha256: a3994c26f10378a039faa11de174d7b78eb8f79e4dd0af2a451410c1a5c3f66b + url: "https://pub.dev" + source: hosted + version: "2.6.2" + file_selector_windows: + dependency: transitive + description: + name: file_selector_windows + sha256: d3547240c20cabf205c7c7f01a50ecdbc413755814d6677f3cb366f04abcead0 + url: "https://pub.dev" + source: hosted + version: "0.9.3+1" flutter: dependency: "direct main" description: flutter @@ -256,6 +304,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.4" + html: + dependency: transitive + description: + name: html + sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" + url: "https://pub.dev" + source: hosted + version: "0.15.4" + http: + dependency: transitive + description: + name: http + sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139 + url: "https://pub.dev" + source: hosted + version: "1.1.2" http_parser: dependency: transitive description: @@ -272,6 +336,70 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.4" + image_picker: + dependency: "direct main" + description: + name: image_picker + sha256: "26222b01a0c9a2c8fe02fc90b8208bd3325da5ed1f4a2acabf75939031ac0bdd" + url: "https://pub.dev" + source: hosted + version: "1.0.7" + image_picker_android: + dependency: transitive + description: + name: image_picker_android + sha256: "39f2bfe497e495450c81abcd44b62f56c2a36a37a175da7d137b4454977b51b1" + url: "https://pub.dev" + source: hosted + version: "0.8.9+3" + image_picker_for_web: + dependency: transitive + description: + name: image_picker_for_web + sha256: e2423c53a68b579a7c37a1eda967b8ae536c3d98518e5db95ca1fe5719a730a3 + url: "https://pub.dev" + source: hosted + version: "3.0.2" + image_picker_ios: + dependency: transitive + description: + name: image_picker_ios + sha256: fadafce49e8569257a0cad56d24438a6fa1f0cbd7ee0af9b631f7492818a4ca3 + url: "https://pub.dev" + source: hosted + version: "0.8.9+1" + image_picker_linux: + dependency: transitive + description: + name: image_picker_linux + sha256: "4ed1d9bb36f7cd60aa6e6cd479779cc56a4cb4e4de8f49d487b1aaad831300fa" + url: "https://pub.dev" + source: hosted + version: "0.2.1+1" + image_picker_macos: + dependency: transitive + description: + name: image_picker_macos + sha256: "3f5ad1e8112a9a6111c46d0b57a7be2286a9a07fc6e1976fdf5be2bd31d4ff62" + url: "https://pub.dev" + source: hosted + version: "0.2.1+1" + image_picker_platform_interface: + dependency: transitive + description: + name: image_picker_platform_interface + sha256: fa4e815e6fcada50e35718727d83ba1c92f1edf95c0b4436554cec301b56233b + url: "https://pub.dev" + source: hosted + version: "2.9.3" + image_picker_windows: + dependency: transitive + description: + name: image_picker_windows + sha256: "6ad07afc4eb1bc25f3a01084d28520496c4a3bb0cb13685435838167c9dcedeb" + url: "https://pub.dev" + source: hosted + version: "0.2.1+1" intl: dependency: "direct main" description: @@ -308,26 +436,34 @@ packages: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.15" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.5.0" meta: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" + mime: + dependency: "direct main" + description: + name: mime + sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + url: "https://pub.dev" + source: hosted + version: "1.0.4" open_file: dependency: "direct main" description: @@ -352,6 +488,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + path_provider: + dependency: "direct main" + description: + name: path_provider + sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668" + url: "https://pub.dev" + source: hosted + version: "2.2.2" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f" + url: "https://pub.dev" + source: hosted + version: "2.3.2" path_provider_linux: dependency: transitive description: @@ -424,6 +584,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.4.0" + photo_view: + dependency: "direct main" + description: + name: photo_view + sha256: "8036802a00bae2a78fc197af8a158e3e2f7b500561ed23b4c458107685e645bb" + url: "https://pub.dev" + source: hosted + version: "0.14.0" platform: dependency: transitive description: @@ -521,18 +689,18 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" state_notifier: dependency: transitive description: @@ -545,10 +713,10 @@ packages: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" string_scanner: dependency: transitive description: @@ -569,10 +737,10 @@ packages: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.6.1" top_snackbar_flutter: dependency: "direct main" description: @@ -685,6 +853,54 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + video_player: + dependency: "direct main" + description: + name: video_player + sha256: fbf28ce8bcfe709ad91b5789166c832cb7a684d14f571a81891858fefb5bb1c2 + url: "https://pub.dev" + source: hosted + version: "2.8.2" + video_player_android: + dependency: transitive + description: + name: video_player_android + sha256: "7f8f25d7ad56819a82b2948357f3c3af071f6a678db33833b26ec36bbc221316" + url: "https://pub.dev" + source: hosted + version: "2.4.11" + video_player_avfoundation: + dependency: transitive + description: + name: video_player_avfoundation + sha256: "309e3962795e761be010869bae65c0b0e45b5230c5cee1bec72197ca7db040ed" + url: "https://pub.dev" + source: hosted + version: "2.5.6" + video_player_platform_interface: + dependency: transitive + description: + name: video_player_platform_interface + sha256: "236454725fafcacf98f0f39af0d7c7ab2ce84762e3b63f2cbb3ef9a7e0550bc6" + url: "https://pub.dev" + source: hosted + version: "6.2.2" + video_player_web: + dependency: transitive + description: + name: video_player_web + sha256: "34beb3a07d4331a24f7e7b2f75b8e2b103289038e07e65529699a671b6a6e2cb" + url: "https://pub.dev" + source: hosted + version: "2.1.3" + web: + dependency: transitive + description: + name: web + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + url: "https://pub.dev" + source: hosted + version: "0.3.0" win32: dependency: transitive description: @@ -718,5 +934,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.0.6 <4.0.0" - flutter: ">=3.10.0" + dart: ">=3.2.0 <4.0.0" + flutter: ">=3.16.0" diff --git a/app_petty_cash/pubspec.yaml b/app_petty_cash/pubspec.yaml index 33b0ae6..141e1cb 100644 --- a/app_petty_cash/pubspec.yaml +++ b/app_petty_cash/pubspec.yaml @@ -53,6 +53,11 @@ dependencies: url_launcher: ^6.1.13 flutter_svg: ^2.0.9 flutter_launcher_icons: ^0.13.1 + image_picker: ^1.0.7 + video_player: ^2.7.2 + photo_view: ^0.14.0 + mime: ^1.0.4 + path_provider: ^2.1.2 dev_dependencies: flutter_test: diff --git a/app_petty_cash/windows/flutter/generated_plugin_registrant.cc b/app_petty_cash/windows/flutter/generated_plugin_registrant.cc index a0d0bbe..4e586f9 100644 --- a/app_petty_cash/windows/flutter/generated_plugin_registrant.cc +++ b/app_petty_cash/windows/flutter/generated_plugin_registrant.cc @@ -6,10 +6,13 @@ #include "generated_plugin_registrant.h" +#include #include #include void RegisterPlugins(flutter::PluginRegistry* registry) { + FileSelectorWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FileSelectorWindows")); PermissionHandlerWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); UrlLauncherWindowsRegisterWithRegistrar( diff --git a/app_petty_cash/windows/flutter/generated_plugins.cmake b/app_petty_cash/windows/flutter/generated_plugins.cmake index c20a586..1119879 100644 --- a/app_petty_cash/windows/flutter/generated_plugins.cmake +++ b/app_petty_cash/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + file_selector_windows permission_handler_windows url_launcher_windows )