first commit
This commit is contained in:
@ -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),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -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 {}
|
||||
}
|
@ -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();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user