first commit

This commit is contained in:
kicap
2023-11-04 03:55:38 +08:00
commit 212fb855f9
169 changed files with 8572 additions and 0 deletions

View File

@ -0,0 +1,62 @@
import 'package:cek_suara_app/ui/widgets/my_textformfield.dart';
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
import './bottom_sheet_cari_area_view_model.dart';
class BottomSheetCariAreaView extends StatelessWidget {
final SheetRequest request;
final Function(SheetResponse)? completer;
const BottomSheetCariAreaView({
Key? key,
required this.request,
this.completer,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return ViewModelBuilder<BottomSheetCariAreaViewModel>.reactive(
viewModelBuilder: () => BottomSheetCariAreaViewModel(),
onViewModelReady: (BottomSheetCariAreaViewModel model) async {
await model.init();
},
builder: (
BuildContext context,
BottomSheetCariAreaViewModel model,
Widget? child,
) {
return SafeArea(
child: Container(
alignment: Alignment.topCenter,
width: double.infinity,
height: MediaQuery.of(context).size.height,
margin: const EdgeInsets.all(25),
padding: const EdgeInsets.all(25),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15),
),
child: MyTextFormField(
hintText: 'Cari Area',
labelText: 'Cari Area',
controller: model.searchController,
suffixIcon: IconButton(
onPressed: () {
completer!(
SheetResponse(
confirmed: true,
data: model.searchController.text,
),
);
},
icon: const Icon(Icons.search),
),
),
),
);
},
);
}
}

View File

@ -0,0 +1,12 @@
import 'package:flutter/material.dart';
import '../../../../../app/app.logger.dart';
import '../../../../../app/core/custom_base_view_model.dart';
class BottomSheetCariAreaViewModel extends CustomBaseViewModel {
final log = getLogger('BottomSheetCariAreaViewModel');
TextEditingController searchController = TextEditingController();
Future<void> init() async {}
}

View File

@ -0,0 +1,272 @@
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
import 'package:validatorless/validatorless.dart';
import '../../../../app/themes/app_colors.dart';
import '../../../widgets/my_button.dart';
import '../../../widgets/my_textformfield.dart';
import './halaman_survei_view_model.dart';
class HalamanSurveiView extends StatelessWidget {
const HalamanSurveiView({super.key});
@override
Widget build(BuildContext context) {
return ViewModelBuilder<HalamanSurveiViewModel>.reactive(
viewModelBuilder: () => HalamanSurveiViewModel(),
onViewModelReady: (HalamanSurveiViewModel model) async {
await model.init();
},
builder: (
BuildContext context,
HalamanSurveiViewModel model,
Widget? child,
) {
return Scaffold(
body: WillPopScope(
onWillPop: () async {
// model.log.i('backPressed: ${model.globalVar.backPressed}');
if (model.globalVar.backPressed == 'exitApp') {
// model.back();
model.quitApp(context);
}
return false;
},
child: SafeArea(
child: Container(
height: MediaQuery.of(context).size.height,
padding: const EdgeInsets.all(20),
child: SingleChildScrollView(
child: Form(
key: model.formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Center(child: Text('Foto KTP / Wajah Pemilih')),
// create a rectangle container that later use to show image with child is KTP icon
Center(
child: Stack(
children: [
Container(
height: 100,
width: 150,
decoration: BoxDecoration(
color: mainColor,
borderRadius: BorderRadius.circular(10),
),
child: model.imageBytes != null
? Image.memory(
model.imageBytes!,
fit: BoxFit.fill,
)
: const Icon(
Icons.credit_card_rounded,
color: Colors.white,
size: 50,
),
),
Positioned(
bottom: 0,
right: 0,
child: CircleAvatar(
radius: 15,
backgroundColor: sixthGrey,
child: IconButton(
onPressed: () {
model.addImage();
},
icon: const Icon(
Icons.add,
color: backgroundColor3,
size: 15,
)),
),
),
],
),
),
const SizedBox(height: 20),
MyTextFormField(
hintText: 'Masukkan KTP / No HP Pemilih',
labelText: 'KTP / No HP Pemilih',
maxLength: 16,
suffixIcon: const Icon(Icons.add_card_rounded),
controller: model.ktpController,
keyboardType: TextInputType.number,
validator: Validatorless.multiple(
[
Validatorless.required(
'KTP / No HP Pemilih tidak boleh kosong'),
Validatorless.number(
'KTP / No HP Pemilih harus angka'),
Validatorless.min(
10, 'KTP / No HP Pemilih minimal 10 digit'),
Validatorless.max(
16, 'KTP / No HP Pemilih maksimal 16 digit'),
],
),
),
const SizedBox(height: 20),
MyTextFormField(
hintText: 'Masukkan Nama Pemilih',
labelText: 'Nama Pemilih',
suffixIcon: const Icon(Icons.person),
controller: model.namaController,
validator: Validatorless.required(
'Nama Pemilih tidak boleh kosong'),
),
const SizedBox(height: 20),
const Text(
' Pilih Area',
),
if (model.isBusy)
const Center(child: CircularProgressIndicator()),
if (!model.isBusy && model.selectedArea != null)
Container(
width: double.infinity,
height: 60,
padding: const EdgeInsets.symmetric(horizontal: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25),
border: Border.all(
color: sixthGrey,
),
),
child: Row(
children: [
// icon search
IconButton(
onPressed: () async =>
await model.searchArea(),
icon: const Icon(
Icons.search,
color: sixthGrey,
),
),
const SizedBox(width: 10),
Expanded(
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
isExpanded: true,
value: model.selectedArea!,
icon: const Icon(Icons.arrow_drop_down),
iconSize: 24,
elevation: 16,
style: const TextStyle(
color: Colors.black),
onChanged: (String? newValue) {
model.log.i(newValue);
model.selectedArea = newValue!;
// model.changeArea(newValue);
model.notifyListeners();
},
items: model.listAreaString
.map<DropdownMenuItem<String>>(
(String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value,
overflow:
TextOverflow.ellipsis),
);
}).toList()),
),
),
],
),
),
// const SizedBox(height: 20),
// const Text(
// ' Pilih Caleg',
// ),
// if (model.isBusy)
// const Center(child: CircularProgressIndicator()),
// if (!model.isBusy)
// Container(
// width: double.infinity,
// height: 60,
// padding: const EdgeInsets.symmetric(horizontal: 10),
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(25),
// border: Border.all(
// color: sixthGrey,
// ),
// ),
// child: Expanded(
// child: DropdownButtonHideUnderline(
// child: DropdownButton<String>(
// isExpanded: true,
// value: model.selectedCaleg!,
// icon: const Icon(Icons.arrow_drop_down),
// iconSize: 24,
// elevation: 16,
// style: const TextStyle(color: Colors.black),
// onChanged: (String? newValue) {
// model.log.i(newValue);
// model.selectedCaleg = newValue!;
// model.notifyListeners();
// },
// items: model.listCalegString
// .map<DropdownMenuItem<String>>(
// (String value) {
// return DropdownMenuItem<String>(
// value: value,
// child: Text(value,
// overflow: TextOverflow.ellipsis),
// );
// }).toList()),
// ),
// ),
// ),
const SizedBox(height: 20),
Center(
child: SizedBox(
width: 200,
child: MyButton(
text: 'Upload Data',
onPressed: () {
if (model.imageBytes == null) {
model.snackbarService.showSnackbar(
message:
'Foto KTP / Wajah Pemilih tidak boleh kosong',
);
model.addImage();
return;
}
if (model.formKey.currentState!.validate()) {
// hide keyboard
FocusScope.of(context).unfocus();
model.dialogService
.showDialog(
title: 'Upload Data',
description:
'Apakah anda yakin ingin mengupload data?',
buttonTitle: 'Ya',
cancelTitle: 'Tidak',
)
.then((value) async {
if (value!.confirmed) {
await model.uploadData();
}
});
// model.uploadData();
}
},
),
),
),
],
),
),
),
),
),
),
);
},
);
}
}

View File

@ -0,0 +1,212 @@
import 'dart:typed_data';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:http_parser/http_parser.dart';
import 'package:image_picker/image_picker.dart';
import '../../../../app/app.bottomsheets.dart';
import '../../../../app/app.logger.dart';
import '../../../../app/core/custom_base_view_model.dart';
import '../../../../model/area_model.dart';
import '../../../../model/my_response.model.dart';
class HalamanSurveiViewModel extends CustomBaseViewModel {
final log = getLogger('HalamanSurveiViewModel');
// form
final formKey = GlobalKey<FormState>();
TextEditingController ktpController = TextEditingController();
TextEditingController namaController = TextEditingController();
// image picker
String? _imagePath;
final ImagePicker _picker = ImagePicker();
XFile? imageFile;
Uint8List? imageBytes;
// area
List<AreaModel> listAreaModel = [];
List<String> listAreaString = [];
List<AreaModel> allListAreaModel = [];
String? selectedArea;
int areaIndex = 0;
// // caleg
// List<CalegModel> listCalegModel = [];
// List<String> listCalegString = [];
// String? selectedCaleg;
// ini baru
String? idCaleg;
String? nik;
Future<void> init() async {
globalVar.backPressed = 'exitApp';
String idCaleg = await mySharedPrefs.getString('id_caleg') ?? '';
this.idCaleg = idCaleg;
nik = await mySharedPrefs.getString('nik') ?? '';
await getData();
}
getData() async {
log.i('getData');
setBusy(true);
globalVar.backPressed = 'cantBack';
try {
// this one is before
// var response = await httpService.get('area');
// String? nik = await mySharedPrefs.getString('nik');
var response = await httpService.get('area/cek_area/$nik');
log.i(response.data);
MyResponseModel myResponseModel = MyResponseModel.fromJson(response.data);
AreaListModel areaListModel =
AreaListModel.fromJson(myResponseModel.data);
listAreaModel = areaListModel.area!;
allListAreaModel = areaListModel.area!;
for (var element in listAreaModel) {
listAreaString.add(element.namaArea!);
}
selectedArea = listAreaString[0];
// int idArea = listAreaModel[0].idArea!;
// await getCaleg(idArea);
// getCaleg()
} catch (e) {
log.e(e);
} finally {
globalVar.backPressed = 'exitApp';
setBusy(false);
}
}
// getCaleg(int idArea) async {
// log.i('getCaleg');
// log.i('idArea: $idArea');
// selectedCaleg = null;
// listCalegModel = [];
// listCalegString = [];
// setBusy(true);
// try {
// var response = await httpService.get('caleg/area/$idArea');
// log.i(response.data);
// MyResponseModel myResponseModel = MyResponseModel.fromJson(response.data);
// // log.i(myResponseModel.data);
// CalegListModel calegListModel =
// CalegListModel.fromJson(myResponseModel.data);
// listCalegModel = calegListModel.caleg!;
// for (var element in listCalegModel) {
// listCalegString.add(element.namaCaleg!);
// }
// selectedCaleg = listCalegString[0];
// // log.i('listCalegModel: $listCalegModel');
// // log.i('listCalegString: $listCalegString');
// // log.i('selectedCaleg: $selectedCaleg');
// } catch (e) {
// log.e(e);
// } finally {
// setBusy(false);
// }
// }
// changeArea(String? value) async {
// int idArea = listAreaModel[listAreaString.indexOf(value!)].idArea!;
// // log.i('idArea: $idArea');
// await getCaleg(idArea);
// }
void addImage() async {
try {
final XFile? image = await _picker.pickImage(source: ImageSource.camera);
if (image != null) {
imageFile = image;
_imagePath = image.path;
imageBytes = await image.readAsBytes();
log.i('image path: $_imagePath');
notifyListeners();
}
} catch (e) {
log.e(e);
}
}
searchArea() async {
var res = await bottomSheetService.showCustomSheet(
variant: BottomSheetType.bottomSheetCariAreaView,
ignoreSafeArea: false,
isScrollControlled: true,
);
if (res!.confirmed) {
log.i('res.data: ${res.data}');
String area = res.data;
if (area == '') {
listAreaModel = allListAreaModel;
} else {
listAreaModel = [];
for (var element in allListAreaModel) {
if (element.namaArea!.toLowerCase().contains(area.toLowerCase())) {
listAreaModel.add(element);
}
}
}
listAreaString = [];
for (var element in listAreaModel) {
listAreaString.add(element.namaArea!);
}
selectedArea = listAreaString[0];
// int idArea = listAreaModel[0].idArea!;
// await getCaleg(idArea);
notifyListeners();
}
}
uploadData() async {
log.i('uploadData');
setBusy(true);
easyLoading.customLoading('Uploading data...');
globalVar.backPressed = 'cantBack';
try {
String idArea = listAreaModel[listAreaString.indexOf(selectedArea!)]
.idArea
.toString();
var fomData = FormData.fromMap(
{
'ktp': ktpController.text,
'nama': namaController.text,
'idCaleg': idCaleg,
'foto': await MultipartFile.fromFile(
_imagePath!,
filename: imageFile!.name,
contentType: MediaType('image', 'jpg'),
),
'idArea': idArea,
'nik': nik,
},
);
await httpService.postWithFormData('tim_survei', fomData);
snackbarService.showSnackbar(
message: 'Data berhasil diupload',
title: 'Berhasil',
duration: const Duration(milliseconds: 2500),
);
// clear data
ktpController.clear();
namaController.clear();
_imagePath = null;
imageFile = null;
imageBytes = null;
notifyListeners();
} catch (e) {
log.e(e);
} finally {
easyLoading.dismissLoading();
globalVar.backPressed = 'exitApp';
setBusy(false);
}
}
}