first commit
This commit is contained in:
129
lib/ui/views/login_screen/login_screen_view.dart
Normal file
129
lib/ui/views/login_screen/login_screen_view.dart
Normal file
@ -0,0 +1,129 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:validatorless/validatorless.dart';
|
||||
|
||||
import '../../../app/themes/app_colors.dart';
|
||||
import '../../../app/themes/app_text.dart';
|
||||
import '../../widgets/my_button.dart';
|
||||
import '../../widgets/my_textformfield.dart';
|
||||
import './login_screen_view_model.dart';
|
||||
|
||||
class LoginScreenView extends StatelessWidget {
|
||||
const LoginScreenView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ViewModelBuilder<LoginScreenViewModel>.reactive(
|
||||
viewModelBuilder: () => LoginScreenViewModel(),
|
||||
onViewModelReady: (LoginScreenViewModel model) async {
|
||||
await model.init();
|
||||
},
|
||||
builder: (
|
||||
BuildContext context,
|
||||
LoginScreenViewModel model,
|
||||
Widget? child,
|
||||
) {
|
||||
return Scaffold(
|
||||
backgroundColor: warningColor,
|
||||
body: WillPopScope(
|
||||
onWillPop: () async {
|
||||
model.log.i('backPressed: ${model.globalVar.backPressed}');
|
||||
if (model.globalVar.backPressed == 'backNormal') {
|
||||
// model.back();
|
||||
model.quitApp(context);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
child: SafeArea(
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
|
||||
child: SingleChildScrollView(
|
||||
child: Form(
|
||||
key: model.formKey,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 50,
|
||||
),
|
||||
Center(
|
||||
child: Image.asset(
|
||||
'assets/logo.png',
|
||||
width: 200,
|
||||
height: 200,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
const Text(
|
||||
'Halaman Login',
|
||||
),
|
||||
const Text(
|
||||
'(Survei App)',
|
||||
style: italicTextStyle,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
MyTextFormField(
|
||||
controller: model.usernameController,
|
||||
labelText: 'Username',
|
||||
hintText: 'Masukkan Username',
|
||||
suffixIcon: const Icon(Icons.person),
|
||||
validator: Validatorless.required(
|
||||
'Username tidak boleh kosong'),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
MyTextFormField(
|
||||
controller: model.passwordController,
|
||||
hintText: 'Masukkan Password',
|
||||
labelText: 'Password',
|
||||
suffixIcon: GestureDetector(
|
||||
child: model.isPasswordVisible
|
||||
? const Icon(Icons.visibility)
|
||||
: const Icon(Icons.visibility_off),
|
||||
onTap: () {
|
||||
model.isPasswordVisible =
|
||||
!model.isPasswordVisible;
|
||||
model.log.i(
|
||||
'isPasswordVisible: ${model.isPasswordVisible}');
|
||||
model.notifyListeners();
|
||||
},
|
||||
),
|
||||
obscureText: !model.isPasswordVisible,
|
||||
validator: Validatorless.required(
|
||||
'Password tidak boleh kosong'),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
SizedBox(
|
||||
width: 250,
|
||||
child: MyButton(
|
||||
// theBackgroundColor: lightColor,
|
||||
textColor: fontColor,
|
||||
text: 'Login',
|
||||
onPressed: () {
|
||||
if (model.formKey.currentState!.validate()) {
|
||||
model.login();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
61
lib/ui/views/login_screen/login_screen_view_model.dart
Normal file
61
lib/ui/views/login_screen/login_screen_view_model.dart
Normal file
@ -0,0 +1,61 @@
|
||||
// import '../../../app/app.router.dart';
|
||||
|
||||
import 'package:cek_suara_app/app/app.router.dart';
|
||||
import 'package:cek_suara_app/model/my_response.model.dart';
|
||||
import 'package:cek_suara_app/model/tim_survei_model.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../app/app.logger.dart';
|
||||
import '../../../app/core/custom_base_view_model.dart';
|
||||
|
||||
class LoginScreenViewModel extends CustomBaseViewModel {
|
||||
final log = getLogger('LoginScreenViewModel');
|
||||
|
||||
// variable
|
||||
final formKey = GlobalKey<FormState>();
|
||||
TextEditingController usernameController = TextEditingController();
|
||||
TextEditingController passwordController = TextEditingController();
|
||||
bool isPasswordVisible = false;
|
||||
|
||||
Future<void> init() async {
|
||||
globalVar.backPressed = 'backNormal';
|
||||
}
|
||||
|
||||
login() async {
|
||||
easyLoading.customLoading('Login..');
|
||||
globalVar.backPressed = 'cantBack';
|
||||
|
||||
try {
|
||||
var formData = FormData.fromMap({
|
||||
'username': usernameController.text,
|
||||
'password': passwordController.text,
|
||||
});
|
||||
var response =
|
||||
await httpService.postWithFormData('login/tim_survei', formData);
|
||||
|
||||
MyResponseModel myResponseModel = MyResponseModel.fromJson(response.data);
|
||||
var data = myResponseModel.data;
|
||||
TimSurveiModel timSurveiModel = TimSurveiModel.fromJson(data);
|
||||
|
||||
mySharedPrefs.setString('nik', timSurveiModel.nik!);
|
||||
mySharedPrefs.setString('nama', timSurveiModel.nama!);
|
||||
mySharedPrefs.setString('id_caleg', timSurveiModel.idCaleg.toString());
|
||||
mySharedPrefs.setString('level', 'tim_survei');
|
||||
snackbarService.showSnackbar(
|
||||
message: 'Selamat datang kembali ${timSurveiModel.nama}',
|
||||
title: 'Login Berhasil',
|
||||
duration: const Duration(milliseconds: 2500),
|
||||
);
|
||||
navigationService.navigateToTimSurveiIndexTrackingView();
|
||||
} catch (e) {
|
||||
log.e('error: $e');
|
||||
} finally {
|
||||
easyLoading.dismissLoading();
|
||||
globalVar.backPressed = 'backNormal';
|
||||
}
|
||||
|
||||
// navigationService.pushNamedAndRemoveUntil('/home-screen');
|
||||
// await navigationService.navigateToAdminIndexTrackingView();
|
||||
}
|
||||
}
|
90
lib/ui/views/splash_screen/splash_screen_view.dart
Normal file
90
lib/ui/views/splash_screen/splash_screen_view.dart
Normal file
@ -0,0 +1,90 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
|
||||
import '../../../app/themes/app_colors.dart';
|
||||
import '../../../app/themes/app_text.dart';
|
||||
import './splash_screen_view_model.dart';
|
||||
|
||||
class SplashScreenView extends StatelessWidget {
|
||||
const SplashScreenView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ViewModelBuilder<SplashScreenViewModel>.reactive(
|
||||
viewModelBuilder: () => SplashScreenViewModel(),
|
||||
onViewModelReady: (SplashScreenViewModel model) async {
|
||||
await model.init();
|
||||
},
|
||||
builder: (
|
||||
BuildContext context,
|
||||
SplashScreenViewModel model,
|
||||
Widget? child,
|
||||
) {
|
||||
return Scaffold(
|
||||
backgroundColor: warningColor,
|
||||
body: Center(
|
||||
child: Column(
|
||||
children: [
|
||||
const Expanded(
|
||||
flex: 1,
|
||||
child: SizedBox(
|
||||
height: 100,
|
||||
),
|
||||
),
|
||||
Image.asset(
|
||||
'assets/logo.png',
|
||||
width: 200,
|
||||
height: 200,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
Text(
|
||||
'SISTEM CEK SUARA',
|
||||
style: boldTextStyle.copyWith(
|
||||
// color: backgroundColor,
|
||||
fontSize: 20,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Text(
|
||||
'(Survei App)',
|
||||
style: boldTextStyle.copyWith(
|
||||
// color: backgroundColor,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const Expanded(child: SizedBox()),
|
||||
Text(
|
||||
'Created By',
|
||||
style: regularTextStyle.copyWith(
|
||||
fontSize: 12,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Text(
|
||||
'Kicap Karan',
|
||||
style: boldTextStyle.copyWith(
|
||||
fontSize: 13,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Text(
|
||||
'www.kicap-karan.com',
|
||||
style: boldTextStyle.copyWith(
|
||||
fontSize: 13,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
13
lib/ui/views/splash_screen/splash_screen_view_model.dart
Normal file
13
lib/ui/views/splash_screen/splash_screen_view_model.dart
Normal file
@ -0,0 +1,13 @@
|
||||
import '../../../app/app.logger.dart';
|
||||
import '../../../app/app.router.dart';
|
||||
import '../../../app/core/custom_base_view_model.dart';
|
||||
|
||||
class SplashScreenViewModel extends CustomBaseViewModel {
|
||||
final log = getLogger('SplashScreenViewModel');
|
||||
Future<void> init() async {
|
||||
// await 3 seconds then go to login
|
||||
await mySharedPrefs.clear();
|
||||
await Future.delayed(const Duration(seconds: 3));
|
||||
await navigationService.navigateTo(Routes.loginScreenView);
|
||||
}
|
||||
}
|
@ -0,0 +1,136 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
|
||||
import '../../../../app/themes/app_colors.dart';
|
||||
import '../../../../app/themes/app_text.dart';
|
||||
import '../../../widgets/top_container.dart';
|
||||
import './first_page_view_model.dart';
|
||||
|
||||
class FirstPageView extends StatelessWidget {
|
||||
const FirstPageView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ViewModelBuilder<FirstPageViewModel>.reactive(
|
||||
viewModelBuilder: () => FirstPageViewModel(),
|
||||
onViewModelReady: (FirstPageViewModel model) async {
|
||||
await model.init();
|
||||
},
|
||||
builder: (
|
||||
BuildContext context,
|
||||
FirstPageViewModel 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: Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TopContainer(
|
||||
title: 'Jumlah Pemilih',
|
||||
value: '${model.counter} Orang',
|
||||
icon: Icons.people_alt_outlined,
|
||||
background: blueColor,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
RichText(
|
||||
text: TextSpan(
|
||||
text: 'Selamat Datang, ',
|
||||
style: regularTextStyle,
|
||||
children: [
|
||||
TextSpan(
|
||||
text: '${model.nama}\n\n',
|
||||
style: boldTextStyle,
|
||||
),
|
||||
const TextSpan(
|
||||
text: 'Silahkan tambahkan data ',
|
||||
style: regularTextStyle,
|
||||
),
|
||||
TextSpan(
|
||||
text: 'Pemilih ',
|
||||
style: boldTextStyle.copyWith(
|
||||
color: greenColor,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
const TextSpan(
|
||||
text: 'dengan menekan menu ',
|
||||
style: regularTextStyle,
|
||||
),
|
||||
TextSpan(
|
||||
text: 'Survei ',
|
||||
style: boldTextStyle.copyWith(
|
||||
color: greenColor,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
const TextSpan(
|
||||
text: 'di bawah sebelah kanan.\n\n',
|
||||
style: regularTextStyle,
|
||||
),
|
||||
const TextSpan(
|
||||
text: 'Menu ',
|
||||
style: regularTextStyle,
|
||||
),
|
||||
TextSpan(
|
||||
text: 'History ',
|
||||
style: boldTextStyle.copyWith(
|
||||
color: greenColor,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
const TextSpan(
|
||||
text:
|
||||
'pada bawah tengah digunakan untuk melihat data yang sudah diinputkan.\n\n',
|
||||
style: regularTextStyle,
|
||||
),
|
||||
const TextSpan(
|
||||
text: 'Menu ',
|
||||
style: regularTextStyle,
|
||||
),
|
||||
TextSpan(
|
||||
text: 'Pengaturan ',
|
||||
style: boldTextStyle.copyWith(
|
||||
color: greenColor,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
const TextSpan(
|
||||
text:
|
||||
'pada bawah debelah kanan digunakan untuk mengubah data diri anda.\n\nSekian dari Developer',
|
||||
style: regularTextStyle,
|
||||
),
|
||||
TextSpan(
|
||||
text: '\nKicap Karan ',
|
||||
style: boldTextStyle.copyWith(
|
||||
color: dangerColor,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
import '../../../../app/app.logger.dart';
|
||||
import '../../../../app/core/custom_base_view_model.dart';
|
||||
import '../../../../model/my_response.model.dart';
|
||||
|
||||
class FirstPageViewModel extends CustomBaseViewModel {
|
||||
final log = getLogger('FirstPageViewModel');
|
||||
int counter = 0;
|
||||
String? nik;
|
||||
String? nama;
|
||||
Future<void> init() async {
|
||||
globalVar.backPressed = 'exitApp';
|
||||
nik = await mySharedPrefs.getString('nik');
|
||||
nama = await mySharedPrefs.getString('nama');
|
||||
await getData();
|
||||
}
|
||||
|
||||
getData() async {
|
||||
setBusy(true);
|
||||
|
||||
try {
|
||||
var response = await httpService.get('tim_survei/counter/$nik');
|
||||
MyResponseModel myResponseModel = MyResponseModel.fromJson(response.data);
|
||||
counter = myResponseModel.data;
|
||||
} catch (e) {
|
||||
log.e(e.toString());
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,151 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
|
||||
import '../../../../app/themes/app_colors.dart';
|
||||
import '../../../../app/themes/app_text.dart';
|
||||
import './halaman_history_view_model.dart';
|
||||
|
||||
class HalamanHistoryView extends StatelessWidget {
|
||||
const HalamanHistoryView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ViewModelBuilder<HalamanHistoryViewModel>.reactive(
|
||||
viewModelBuilder: () => HalamanHistoryViewModel(),
|
||||
onViewModelReady: (HalamanHistoryViewModel model) async {
|
||||
await model.init();
|
||||
},
|
||||
builder: (
|
||||
BuildContext context,
|
||||
HalamanHistoryViewModel 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: Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(15),
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
color: warningColor,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
"Caleg : ",
|
||||
style: italicTextStyle.copyWith(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Text(
|
||||
model.namaCaleg,
|
||||
style: boldTextStyle.copyWith(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Expanded(
|
||||
child: Container(
|
||||
alignment: model.isBusy
|
||||
? Alignment.center
|
||||
: model.status == true
|
||||
? model.counter > 0
|
||||
? Alignment.topCenter
|
||||
: Alignment.center
|
||||
: Alignment.center,
|
||||
height: double.infinity,
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
color: warningColor,
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (model.isBusy)
|
||||
const Center(
|
||||
child: CircularProgressIndicator()),
|
||||
if (!model.isBusy &&
|
||||
model.status == true &&
|
||||
model.counter > 0)
|
||||
for (var i = 0; i < model.counter; i++)
|
||||
Card(
|
||||
child: ListTile(
|
||||
// leading is datetime dummy
|
||||
leading:
|
||||
Text(model.listPemilih[i].createdAt!),
|
||||
title: Text(
|
||||
model.listPemilih[i].namaPemilih!,
|
||||
style: boldTextStyle,
|
||||
),
|
||||
subtitle: Text(
|
||||
model.listPemilih[i].namaArea!,
|
||||
),
|
||||
trailing: IconButton(
|
||||
icon: const Icon(
|
||||
Icons.info_outline,
|
||||
color: mainColor,
|
||||
),
|
||||
onPressed: () {
|
||||
// model.showDetailCaleg(model.listCalegModel[i]);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
if (!model.isBusy &&
|
||||
model.status == true &&
|
||||
model.counter == 0)
|
||||
const Center(
|
||||
child: Text(
|
||||
'Belum ada data',
|
||||
style: boldTextStyle,
|
||||
),
|
||||
),
|
||||
if (!model.isBusy && model.status == false)
|
||||
const Center(
|
||||
child: Text(
|
||||
'Error: Gagal mengambil data dari server',
|
||||
style: boldTextStyle,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
import 'package:cek_suara_app/model/my_response.model.dart';
|
||||
import 'package:cek_suara_app/model/pemilih_model.dart';
|
||||
|
||||
import '../../../../app/app.logger.dart';
|
||||
import '../../../../app/core/custom_base_view_model.dart';
|
||||
|
||||
class HalamanHistoryViewModel extends CustomBaseViewModel {
|
||||
final log = getLogger('HalamanHistoryViewModel');
|
||||
int counter = 0;
|
||||
List<PemilihModel> listPemilih = [];
|
||||
bool status = false;
|
||||
|
||||
String namaCaleg = '...';
|
||||
Future<void> init() async {
|
||||
await getData();
|
||||
}
|
||||
|
||||
getData() async {
|
||||
setBusy(true);
|
||||
|
||||
try {
|
||||
String? nik = await mySharedPrefs.getString('nik');
|
||||
var response = await httpService.get('tim_survei/$nik');
|
||||
MyResponseModel myResponseModel = MyResponseModel.fromJson(response.data);
|
||||
PemilihDetailModel pemilihDetailModel =
|
||||
PemilihDetailModel.fromJson(myResponseModel.data);
|
||||
listPemilih = pemilihDetailModel.pemilihModel!;
|
||||
log.i('listPemilih: $listPemilih');
|
||||
namaCaleg = listPemilih[0].namaCaleg!;
|
||||
counter = pemilihDetailModel.jumlah!;
|
||||
log.i('counter: $counter');
|
||||
status = true;
|
||||
} catch (e) {
|
||||
log.e(e.toString());
|
||||
status = false;
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
import 'package:cek_suara_app/ui/views/tim_survei_index_tracking/first_page/first_page_view.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
|
||||
import './halaman_pengaturan_view_model.dart';
|
||||
|
||||
class HalamanPengaturanView extends StatelessWidget {
|
||||
const HalamanPengaturanView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ViewModelBuilder<HalamanPengaturanViewModel>.reactive(
|
||||
viewModelBuilder: () => HalamanPengaturanViewModel(),
|
||||
onViewModelReady: (HalamanPengaturanViewModel model) async {
|
||||
await model.init();
|
||||
},
|
||||
builder: (
|
||||
BuildContext context,
|
||||
HalamanPengaturanViewModel model,
|
||||
Widget? child,
|
||||
) {
|
||||
return WillPopScope(
|
||||
onWillPop: () async {
|
||||
// model.log.i('backPressed: ${model.globalVar.backPressed}');
|
||||
if (model.globalVar.backPressed == 'exitApp') {
|
||||
// model.back();
|
||||
model.quitApp(context);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
child: const FirstPageView(),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
import '../../../../app/core/custom_base_view_model.dart';
|
||||
|
||||
class HalamanPengaturanViewModel extends CustomBaseViewModel {
|
||||
Future<void> init() async {
|
||||
globalVar.backPressed = 'exitApp';
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:stacked_services/stacked_services.dart';
|
||||
import 'package:stylish_bottom_bar/model/bar_items.dart';
|
||||
import 'package:stylish_bottom_bar/stylish_bottom_bar.dart';
|
||||
|
||||
import '../../../app/app.router.dart';
|
||||
import '../../../app/themes/app_colors.dart';
|
||||
import '../../../app/themes/app_text.dart';
|
||||
import './tim_survei_index_tracking_view_model.dart';
|
||||
|
||||
class TimSurveiIndexTrackingView extends StatelessWidget {
|
||||
const TimSurveiIndexTrackingView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ViewModelBuilder<TimSurveiIndexTrackingViewModel>.reactive(
|
||||
viewModelBuilder: () => TimSurveiIndexTrackingViewModel(),
|
||||
onViewModelReady: (TimSurveiIndexTrackingViewModel model) async {
|
||||
await model.init();
|
||||
},
|
||||
builder: (
|
||||
BuildContext context,
|
||||
TimSurveiIndexTrackingViewModel model,
|
||||
Widget? child,
|
||||
) {
|
||||
return Scaffold(
|
||||
extendBody: false,
|
||||
body: ExtendedNavigator(
|
||||
router: TimSurveiIndexTrackingViewRouter(),
|
||||
navigatorKey: StackedService.nestedNavigationKey(5),
|
||||
),
|
||||
bottomNavigationBar: StylishBottomBar(
|
||||
items: [
|
||||
for (var item in model.bottomNavBarList)
|
||||
BottomBarItem(
|
||||
icon: Icon(item['icon'],
|
||||
color: model.currentIndex ==
|
||||
model.bottomNavBarList.indexOf(item)
|
||||
? warningColor
|
||||
: fontColor),
|
||||
title: Text(
|
||||
item['name'],
|
||||
style: regularTextStyle.copyWith(
|
||||
color: model.currentIndex ==
|
||||
model.bottomNavBarList.indexOf(item)
|
||||
? warningColor
|
||||
: fontColor,
|
||||
),
|
||||
// textAlign: TextAlign.l,
|
||||
),
|
||||
backgroundColor:
|
||||
model.currentIndex == model.bottomNavBarList.indexOf(item)
|
||||
? fontColor
|
||||
: fontColor,
|
||||
),
|
||||
],
|
||||
currentIndex: model.currentIndex,
|
||||
option: BubbleBarOptions(),
|
||||
hasNotch: true,
|
||||
backgroundColor: warningColor,
|
||||
onTap: (value) {
|
||||
model.handleNavigation(value);
|
||||
},
|
||||
// fabLocation: StylishBarFabLocation.center,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:stacked_services/stacked_services.dart';
|
||||
|
||||
import '../../../app/app.locator.dart';
|
||||
import '../../../app/app.logger.dart';
|
||||
import '../../../app/app.router.dart';
|
||||
import '../../../services/global_var.dart';
|
||||
import '../../../services/shared_prefs.dart';
|
||||
|
||||
class TimSurveiIndexTrackingViewModel extends IndexTrackingViewModel {
|
||||
final log = getLogger('TimSurveiIndexTrackingViewModel');
|
||||
final mySharedPrefs = locator<MySharedPrefs>();
|
||||
final navigationService = locator<NavigationService>();
|
||||
final snackbarService = locator<SnackbarService>();
|
||||
final globalVar = locator<GlobalVar>();
|
||||
|
||||
final _bottomNavBarList = [
|
||||
{
|
||||
'name': 'Survei',
|
||||
'icon': Icons.supervised_user_circle_outlined,
|
||||
'header': 'Halaman Survei',
|
||||
},
|
||||
{
|
||||
'name': 'History',
|
||||
'icon': Icons.history_edu_outlined,
|
||||
'header': 'History Survei',
|
||||
},
|
||||
{
|
||||
'name': 'Pengaturan',
|
||||
'icon': Icons.settings_outlined,
|
||||
'header': 'Pengaturan',
|
||||
},
|
||||
];
|
||||
|
||||
String header = 'Pengaturan';
|
||||
|
||||
List<Map<String, dynamic>> get bottomNavBarList => _bottomNavBarList;
|
||||
|
||||
final List<String> _views = [
|
||||
TimSurveiIndexTrackingViewRoutes.halamanSurveiView,
|
||||
TimSurveiIndexTrackingViewRoutes.halamanHistoryView,
|
||||
TimSurveiIndexTrackingViewRoutes.halamanPengaturanView,
|
||||
];
|
||||
|
||||
Future<void> init() async {
|
||||
if (await mySharedPrefs.getString('level') != 'tim_survei') {
|
||||
snackbarService.showSnackbar(
|
||||
message: 'Anda tidak memiliki akses',
|
||||
title: 'Akses Ditolak',
|
||||
duration: const Duration(milliseconds: 2500),
|
||||
);
|
||||
navigationService.clearStackAndShow(Routes.loginScreenView);
|
||||
}
|
||||
setIndex(2);
|
||||
}
|
||||
|
||||
void handleNavigation(int index) {
|
||||
// log.d("handleNavigation: $index");
|
||||
// log.d("currentIndex: $currentIndex");
|
||||
|
||||
// if (currentIndex == index) return;
|
||||
|
||||
setIndex(index);
|
||||
header = _bottomNavBarList[index]['header'] as String;
|
||||
navigationService.navigateTo(
|
||||
_views[index],
|
||||
id: 5,
|
||||
);
|
||||
}
|
||||
}
|
38
lib/ui/widgets/my_button.dart
Normal file
38
lib/ui/widgets/my_button.dart
Normal file
@ -0,0 +1,38 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../app/themes/app_colors.dart';
|
||||
|
||||
class MyButton extends StatelessWidget {
|
||||
const MyButton({
|
||||
Key? key,
|
||||
required this.text,
|
||||
this.theBackgroundColor = mainColor,
|
||||
this.textColor = backgroundColor,
|
||||
this.onPressed,
|
||||
}) : super(key: key);
|
||||
|
||||
final String text;
|
||||
final VoidCallback? onPressed;
|
||||
final Color theBackgroundColor;
|
||||
final Color textColor;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: theBackgroundColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
),
|
||||
),
|
||||
onPressed: onPressed,
|
||||
child: Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
color: textColor,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
85
lib/ui/widgets/my_textformfield.dart
Normal file
85
lib/ui/widgets/my_textformfield.dart
Normal file
@ -0,0 +1,85 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../app/themes/app_colors.dart';
|
||||
|
||||
class MyTextFormField extends StatelessWidget {
|
||||
const MyTextFormField({
|
||||
Key? key,
|
||||
this.labelText,
|
||||
this.hintText,
|
||||
this.obscureText,
|
||||
this.validator,
|
||||
this.suffixIcon,
|
||||
this.prefixIcon,
|
||||
this.focusNode,
|
||||
this.controller,
|
||||
this.maxLines = 1,
|
||||
this.onEditingComplete,
|
||||
this.keyboardType = TextInputType.text,
|
||||
this.initialValue,
|
||||
this.readOnly = false,
|
||||
this.maxLength,
|
||||
}) : super(key: key);
|
||||
|
||||
final String? labelText;
|
||||
final String? hintText;
|
||||
final bool? obscureText;
|
||||
final FormFieldValidator<String>? validator;
|
||||
final Widget? suffixIcon;
|
||||
final Widget? prefixIcon;
|
||||
final FocusNode? focusNode;
|
||||
final TextEditingController? controller;
|
||||
final int maxLines;
|
||||
final VoidCallback? onEditingComplete;
|
||||
final TextInputType keyboardType;
|
||||
final String? initialValue;
|
||||
final bool readOnly;
|
||||
final int? maxLength;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextFormField(
|
||||
maxLength: maxLength,
|
||||
readOnly: readOnly,
|
||||
initialValue: initialValue,
|
||||
onEditingComplete: onEditingComplete,
|
||||
maxLines: maxLines,
|
||||
controller: controller,
|
||||
focusNode: focusNode,
|
||||
obscureText: obscureText ?? false,
|
||||
keyboardType: keyboardType,
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: prefixIcon,
|
||||
suffixIcon: suffixIcon,
|
||||
enabledBorder: const OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(25)),
|
||||
borderSide: BorderSide(
|
||||
// color: mainColor,
|
||||
),
|
||||
),
|
||||
focusedBorder: const OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(25)),
|
||||
borderSide: BorderSide(
|
||||
// color: mainColor,
|
||||
),
|
||||
),
|
||||
focusedErrorBorder: const OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(25)),
|
||||
borderSide: BorderSide(
|
||||
color: dangerColor,
|
||||
),
|
||||
),
|
||||
errorBorder: const OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(25)),
|
||||
borderSide: BorderSide(
|
||||
color: dangerColor,
|
||||
),
|
||||
),
|
||||
labelText: labelText,
|
||||
hintText: hintText,
|
||||
labelStyle: const TextStyle(color: fontColor),
|
||||
),
|
||||
validator: validator,
|
||||
);
|
||||
}
|
||||
}
|
73
lib/ui/widgets/top_container.dart
Normal file
73
lib/ui/widgets/top_container.dart
Normal file
@ -0,0 +1,73 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../app/themes/app_colors.dart';
|
||||
|
||||
class TopContainer extends StatelessWidget {
|
||||
const TopContainer({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.value,
|
||||
required this.icon,
|
||||
required this.background,
|
||||
});
|
||||
|
||||
final String title;
|
||||
final String value;
|
||||
final IconData icon;
|
||||
final Color background;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: background,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
color: fontGrey,
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
textAlign: TextAlign.justify,
|
||||
),
|
||||
),
|
||||
const Expanded(
|
||||
flex: 1,
|
||||
child: Text(
|
||||
':',
|
||||
style: TextStyle(
|
||||
color: fontGrey,
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 5,
|
||||
child: Text(
|
||||
value,
|
||||
style: const TextStyle(
|
||||
color: fontGrey,
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
icon,
|
||||
color: fontGrey,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user