penggabungan browse file & pick picture dan pemecahan file

This commit is contained in:
Sas Andy
2024-01-17 08:32:11 +07:00
parent 1235fd9e10
commit 578e296097
5 changed files with 501 additions and 228 deletions

View File

@@ -1,4 +1,3 @@
import 'package:equatable/equatable.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

View File

@@ -0,0 +1,100 @@
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,
});
final TextEditingController ctrlTglAwal;
final ValueNotifier<DateTime> tglAwal;
final ValueNotifier<String> tglAwalTmp;
@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: () 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;
}
},
),
),
],
);
}
}

View File

@@ -1,15 +1,25 @@
import 'dart:convert';
import 'dart:io' as io;
import 'dart:io';
import 'package:app_petty_cash/app/app_extension.dart';
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: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:dropdown_button2/dropdown_button2.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 'package:mime/mime.dart';
import '../../app/constant.dart';
import '../../model/list_category_model.dart';
@@ -44,6 +54,10 @@ 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("");
String formattedDate = DateFormat('dd-MM-yyyy').format(DateTime.now());
@@ -182,6 +196,95 @@ 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(bytes);
fileDataBase64.value = base64Image;
final file = File(fileData.value!.path);
print(file.lengthSync() / 1000000);
print(await fileData.value!.length());
// await getExternalStorageDirectory();
// print(await fileData.value!.saveTo(path));
print(base64Image);
}
}
final ImagePicker _picker = ImagePicker();
pickImage() async {
final XFile? pickedFile = await _picker.pickImage(
source: ImageSource.camera,
);
if (pickedFile != null) {
if (await pickedFile.length() > 10000000) {
SanckbarWidget(context, "File tidak boleh lebih dari 10 MB",
snackbarType.warning);
} else {
fileData.value = pickedFile;
isImage.value = true;
}
// 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!);
isImage.value = imgExt.contains(result.files.single.extension);
print("ini xfile");
fileData.value = fl;
await getBase64();
fileEkstension.value = result.files.single.extension ?? "";
}
} else {
// User canceled the picker
}
}
return Padding(
padding: EdgeInsets.only(
top: Constant.getActualYPhone(context: context, y: 30),
@@ -259,87 +362,10 @@ 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),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 20),
@@ -441,126 +467,9 @@ 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(
listCategoryData: listCategoryData,
selectedListCategory: selectedListCategory),
SizedBox(
height:
@@ -700,29 +609,14 @@ 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(
isImage: isImage,
fileData: fileData,
fileDataBase64: fileDataBase64,
pickFile: pickFile,
pickImage: pickImage),
Spacer(),
// Spacer(),
Container(
width: Constant.getActualXPhone(context: context, x: 390),
@@ -776,7 +670,7 @@ class TransaksiScreen extends HookConsumerWidget {
// validasi form
}
}
DateTime parsedDate = DateFormat('dd-MM-yyyy').parse(
ctrlTglAwal.value.text.toString(),
);
@@ -786,8 +680,8 @@ class TransaksiScreen extends HookConsumerWidget {
"tgltransaksi": formattedDateTransaksi,
"typeid":
selectedListTypeData.value.typeid.toString(),
"categoryid":
selectedListCategory.value.categoryid.toString(),
"categoryid": selectedListCategory.value.categoryid
.toString(),
"jumlah": ctrlJumlah.value.text.toString(),
"catatan": ctrlCatatan.value.text.toString(),
// "userid": "1",
@@ -802,7 +696,8 @@ class TransaksiScreen extends HookConsumerWidget {
.insertTransaksi(
formattedDateTransaksi,
selectedListTypeData.value.typeid.toString(),
selectedListCategory.value.categoryid.toString(),
selectedListCategory.value.categoryid
.toString(),
ctrlJumlah.value.text.toString(),
ctrlCatatan.value.text.toString(),
// "1",

View File

@@ -0,0 +1,117 @@
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,
});
final ValueNotifier<List<ListCategory>> listCategoryData;
final ValueNotifier<ListCategory> selectedListCategory;
@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: (ListCategory? newValue) {
// if (newValue) {
selectedListCategory.value = newValue!;
print(selectedListCategory.value.categoryid);
// }
},
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,162 @@
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,
});
final ValueNotifier<bool> isImage;
final ValueNotifier<XFile?> fileData;
final ValueNotifier<String> fileDataBase64;
final Function pickImage;
final Function pickFile;
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
print("tapped");
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))
],
),
],
),
);
},
);
},
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)
IconButton(
onPressed: () {
fileData.value = null;
fileDataBase64.value = '';
isImage.value = false;
},
icon: Icon(Icons.cancel_outlined)),
],
),
);
}
}