commit Merge branch 'andy/merge_camera'

This commit is contained in:
sindhu
2024-01-17 14:00:58 +07:00
20 changed files with 1959 additions and 681 deletions

View File

@@ -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<XFile?>(null);
final photoBase64 = useState<String>("");
final previewLoading = useState(false);
getBase64() async {
// List<int> 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"))
],
),
],
),
),
),
);
}
}

View File

@@ -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<CamerExample> createState() => _CamerExampleState();
}
class _CamerExampleState extends State<CamerExample> {
List<XFile>? _mediaFileList;
void _setImageFileListFromFile(XFile? value) {
_mediaFileList = value == null ? null : <XFile>[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<void> _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<void> _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<XFile> 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<XFile> pickedFileList = <XFile>[];
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<void> _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<void> 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<void>(
future: retrieveLostData(),
builder: (BuildContext context, AsyncSnapshot<void> 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: <Widget>[
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<void> _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: <Widget>[
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: <Widget>[
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<AspectRatioVideo> {
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();
}
}
}

View File

@@ -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>(
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>(
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>(
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>(
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);
}
},
),
],
),
],
),
],
),
),
),
),

View File

@@ -1,4 +1,3 @@
import 'package:equatable/equatable.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@@ -44,15 +43,18 @@ class InsertTransaksiNotifier extends StateNotifier<InsertTransaksiState> {
}) : 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<InsertTransaksiState> {
catatan,
userid,
sender,
base64file,
fileName,
fileSize,
fileExtension,
url,
);
state = InsertTransaksiStateDone(resp: resp);

View File

@@ -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<DateTime> tglAwal;
final ValueNotifier<String> tglAwalTmp;
final ValueNotifier<bool> 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,
),
),
],
);
}
}

View File

@@ -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<XFile?>(null);
final fileDataBase64 = useState<String>("");
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<int> 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<String> 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<ListCategory>(
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<ListCategory>(
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<double>(6),
thumbVisibility:
MaterialStateProperty.all<bool>(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,
),
),
],

View File

@@ -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<List<ListCategory>> listCategoryData;
final ValueNotifier<ListCategory> selectedListCategory;
final ValueNotifier<bool> isLoading;
@override
Widget build(BuildContext context) {
return SizedBox(
width: Constant.getActualXPhone(context: context, x: 390),
child: DropdownButtonHideUnderline(
child: DropdownButton2<ListCategory>(
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<ListCategory>(
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<double>(6),
thumbVisibility: MaterialStateProperty.all<bool>(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),
),
),
),
),
);
}
}

View File

@@ -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<bool> isImage;
final ValueNotifier<bool> isLoading;
final ValueNotifier<XFile?> fileData;
final ValueNotifier<String> 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)),
],
),
);
}
}