added other

This commit is contained in:
kicap 2023-05-18 16:04:03 +08:00
parent b82edc2db9
commit 949474b8ef
20 changed files with 913 additions and 171 deletions

4
.env
View File

@ -1,2 +1,2 @@
url = 'http://20.20.20.25/perumahan/'
api_url = 'http://20.20.20.25/perumahan/api/'
url = 'http://20.20.20.25/panti_asuhan2/'
api_url = 'http://20.20.20.25/panti_asuhan2/api/'

View File

@ -2,11 +2,13 @@ import 'package:panti_asuhan/ui/views/admin_index_tracking/admin_index/admin_ind
import 'package:panti_asuhan/ui/views/admin_index_tracking/dana_sosial_admin/dana_sosial_admin_view.dart';
import 'package:panti_asuhan/ui/views/admin_index_tracking/data_siswa/data_siswa_view.dart';
import 'package:panti_asuhan/ui/views/admin_index_tracking/profil/profil_view.dart';
import 'package:panti_asuhan/ui/views/tambah_dana_sosial/tambah_dana_sosial_view.dart';
import 'package:stacked_services/stacked_services.dart';
import 'package:stacked/stacked_annotations.dart';
import '../services/http_services.dart';
import '../services/my_easyloading.dart';
import '../ui/views/admin_index_tracking/add_siswa_dialog/add_siswa_dialog/add_siswa_dialog_view.dart';
import '../ui/views/admin_index_tracking/admin_index_tracking_view.dart';
import '../ui/views/login_screen/login_screen_view.dart';
import '../ui/views/splash_screen/splash_screen_view.dart';
@ -24,6 +26,10 @@ import '../ui/views/splash_screen/splash_screen_view.dart';
MaterialRoute(page: ProfilView),
],
),
MaterialRoute(page: TambahDanaSosialView),
],
dialogs: [
StackedDialog(classType: AddSiswaDialogView),
],
dependencies: [
LazySingleton(classType: NavigationService),

25
lib/app/app.dialogs.dart Normal file
View File

@ -0,0 +1,25 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// **************************************************************************
// StackedDialogGenerator
// **************************************************************************
import 'package:stacked_services/stacked_services.dart';
import 'app.locator.dart';
import '../ui/views/admin_index_tracking/add_siswa_dialog/add_siswa_dialog/add_siswa_dialog_view.dart';
enum DialogType {
addSiswaDialogView,
}
void setupDialogUi() {
final dialogService = locator<DialogService>();
final Map<DialogType, DialogBuilder> builders = {
DialogType.addSiswaDialogView: (context, request, completer) =>
AddSiswaDialogView(request: request, completer: completer),
};
dialogService.registerCustomDialogBuilders(builders);
}

View File

@ -5,24 +5,26 @@
// **************************************************************************
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:flutter/material.dart' as _i5;
import 'package:flutter/material.dart' as _i6;
import 'package:flutter/material.dart';
import 'package:panti_asuhan/ui/views/admin_index_tracking/admin_index/admin_index_view.dart'
as _i6;
as _i7;
import 'package:panti_asuhan/ui/views/admin_index_tracking/admin_index_tracking_view.dart'
as _i4;
import 'package:panti_asuhan/ui/views/admin_index_tracking/dana_sosial_admin/dana_sosial_admin_view.dart'
as _i7;
import 'package:panti_asuhan/ui/views/admin_index_tracking/data_siswa/data_siswa_view.dart'
as _i8;
import 'package:panti_asuhan/ui/views/admin_index_tracking/profil/profil_view.dart'
import 'package:panti_asuhan/ui/views/admin_index_tracking/data_siswa/data_siswa_view.dart'
as _i9;
import 'package:panti_asuhan/ui/views/admin_index_tracking/profil/profil_view.dart'
as _i10;
import 'package:panti_asuhan/ui/views/login_screen/login_screen_view.dart'
as _i3;
import 'package:panti_asuhan/ui/views/splash_screen/splash_screen_view.dart'
as _i2;
import 'package:panti_asuhan/ui/views/tambah_dana_sosial/tambah_dana_sosial_view.dart'
as _i5;
import 'package:stacked/stacked.dart' as _i1;
import 'package:stacked_services/stacked_services.dart' as _i10;
import 'package:stacked_services/stacked_services.dart' as _i11;
class Routes {
static const splashScreenView = '/';
@ -31,10 +33,13 @@ class Routes {
static const adminIndexTrackingView = '/admin-index-tracking-view';
static const tambahDanaSosialView = '/tambah-dana-sosial-view';
static const all = <String>{
splashScreenView,
loginScreenView,
adminIndexTrackingView,
tambahDanaSosialView,
};
}
@ -52,30 +57,41 @@ class StackedRouter extends _i1.RouterBase {
Routes.adminIndexTrackingView,
page: _i4.AdminIndexTrackingView,
),
_i1.RouteDef(
Routes.tambahDanaSosialView,
page: _i5.TambahDanaSosialView,
),
];
final _pagesMap = <Type, _i1.StackedRouteFactory>{
_i2.SplashScreenView: (data) {
return _i5.MaterialPageRoute<dynamic>(
return _i6.MaterialPageRoute<dynamic>(
builder: (context) => const _i2.SplashScreenView(),
settings: data,
maintainState: false,
);
},
_i3.LoginScreenView: (data) {
return _i5.MaterialPageRoute<dynamic>(
return _i6.MaterialPageRoute<dynamic>(
builder: (context) => const _i3.LoginScreenView(),
settings: data,
maintainState: false,
);
},
_i4.AdminIndexTrackingView: (data) {
return _i5.MaterialPageRoute<dynamic>(
return _i6.MaterialPageRoute<dynamic>(
builder: (context) => const _i4.AdminIndexTrackingView(),
settings: data,
maintainState: false,
);
},
_i5.TambahDanaSosialView: (data) {
return _i6.MaterialPageRoute<dynamic>(
builder: (context) => _i5.TambahDanaSosialView(),
settings: data,
maintainState: false,
);
},
};
@override
@ -105,47 +121,47 @@ class AdminIndexTrackingViewRouter extends _i1.RouterBase {
final _routes = <_i1.RouteDef>[
_i1.RouteDef(
AdminIndexTrackingViewRoutes.adminIndexView,
page: _i6.AdminIndexView,
page: _i7.AdminIndexView,
),
_i1.RouteDef(
AdminIndexTrackingViewRoutes.danaSosialAdminView,
page: _i7.DanaSosialAdminView,
page: _i8.DanaSosialAdminView,
),
_i1.RouteDef(
AdminIndexTrackingViewRoutes.dataSiswaView,
page: _i8.DataSiswaView,
page: _i9.DataSiswaView,
),
_i1.RouteDef(
AdminIndexTrackingViewRoutes.profilView,
page: _i9.ProfilView,
page: _i10.ProfilView,
),
];
final _pagesMap = <Type, _i1.StackedRouteFactory>{
_i6.AdminIndexView: (data) {
return _i5.MaterialPageRoute<dynamic>(
builder: (context) => const _i6.AdminIndexView(),
_i7.AdminIndexView: (data) {
return _i6.MaterialPageRoute<dynamic>(
builder: (context) => const _i7.AdminIndexView(),
settings: data,
maintainState: false,
);
},
_i7.DanaSosialAdminView: (data) {
return _i5.MaterialPageRoute<dynamic>(
builder: (context) => const _i7.DanaSosialAdminView(),
_i8.DanaSosialAdminView: (data) {
return _i6.MaterialPageRoute<dynamic>(
builder: (context) => const _i8.DanaSosialAdminView(),
settings: data,
maintainState: false,
);
},
_i8.DataSiswaView: (data) {
return _i5.MaterialPageRoute<dynamic>(
builder: (context) => const _i8.DataSiswaView(),
_i9.DataSiswaView: (data) {
return _i6.MaterialPageRoute<dynamic>(
builder: (context) => const _i9.DataSiswaView(),
settings: data,
maintainState: false,
);
},
_i9.ProfilView: (data) {
return _i5.MaterialPageRoute<dynamic>(
builder: (context) => const _i9.ProfilView(),
_i10.ProfilView: (data) {
return _i6.MaterialPageRoute<dynamic>(
builder: (context) => const _i10.ProfilView(),
settings: data,
maintainState: false,
);
@ -158,7 +174,7 @@ class AdminIndexTrackingViewRouter extends _i1.RouterBase {
Map<Type, _i1.StackedRouteFactory> get pagesMap => _pagesMap;
}
extension NavigatorStateExtension on _i10.NavigationService {
extension NavigatorStateExtension on _i11.NavigationService {
Future<dynamic> navigateToSplashScreenView([
int? routerId,
bool preventDuplicates = true,
@ -201,6 +217,20 @@ extension NavigatorStateExtension on _i10.NavigationService {
transition: transition);
}
Future<dynamic> navigateToTambahDanaSosialView([
int? routerId,
bool preventDuplicates = true,
Map<String, String>? parameters,
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
transition,
]) async {
return navigateTo<dynamic>(Routes.tambahDanaSosialView,
id: routerId,
preventDuplicates: preventDuplicates,
parameters: parameters,
transition: transition);
}
Future<dynamic> navigateToNestedAdminIndexViewInAdminIndexTrackingViewRouter([
int? routerId,
bool preventDuplicates = true,
@ -300,6 +330,20 @@ extension NavigatorStateExtension on _i10.NavigationService {
transition: transition);
}
Future<dynamic> replaceWithTambahDanaSosialView([
int? routerId,
bool preventDuplicates = true,
Map<String, String>? parameters,
Widget Function(BuildContext, Animation<double>, Animation<double>, Widget)?
transition,
]) async {
return replaceWith<dynamic>(Routes.tambahDanaSosialView,
id: routerId,
preventDuplicates: preventDuplicates,
parameters: parameters,
transition: transition);
}
Future<dynamic>
replaceWithNestedAdminIndexViewInAdminIndexTrackingViewRouter([
int? routerId,

View File

@ -3,6 +3,7 @@ import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:stacked_services/stacked_services.dart';
import 'app/app.dialogs.dart';
import 'app/app.locator.dart';
import 'app/app.router.dart';
import 'app/themes/app_theme.dart';
@ -33,7 +34,7 @@ class MyApp extends StatelessWidget {
Future<void> setupAllLocator() async {
await setupLocator();
// setupDialogUi();
setupDialogUi();
// setupBottomsheetUi();
// setupSnackbarUi();
}

View File

@ -1,7 +1,10 @@
import 'package:dio/dio.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import '../app/app.logger.dart';
class MyHttpServices {
final log = getLogger('MyHttpServices');
final _options = BaseOptions(
baseUrl: dotenv.env['api_url']!,
connectTimeout: const Duration(seconds: 60),

View File

@ -0,0 +1,229 @@
import 'package:flutter/material.dart';
import 'package:panti_asuhan/app/themes/app_text.dart';
import 'package:panti_asuhan/ui/widgets/my_textformfield.dart';
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
import 'package:validatorless/validatorless.dart';
import '../../../../../app/themes/app_colors.dart';
import './add_siswa_dialog_view_model.dart';
class DataSiswa {
final String? nama;
DataSiswa({required this.nama, r});
}
class AddSiswaDialogView extends StatelessWidget {
final DialogRequest<DataSiswa> request;
final Function(DialogResponse) completer;
const AddSiswaDialogView({
Key? key,
required DialogRequest request,
required this.completer,
}) : request = request as DialogRequest<DataSiswa>,
super(key: key);
@override
Widget build(BuildContext context) {
return ViewModelBuilder<AddSiswaDialogViewModel>.reactive(
viewModelBuilder: () => AddSiswaDialogViewModel(),
onViewModelReady: (AddSiswaDialogViewModel model) async {
await model.init();
},
builder: (
BuildContext context,
AddSiswaDialogViewModel model,
Widget? child,
) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
),
padding: const EdgeInsets.all(10),
child: SingleChildScrollView(
child: Form(
key: model.formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'Tambah Siswa',
style: boldTextStyle.copyWith(
fontSize: 16,
),
),
const SizedBox(height: 10),
// create circle avatar
Stack(
children: [
CircleAvatar(
radius: 50,
backgroundColor: fontParagraphColor,
child: model.imageBytes == null
? const Icon(
Icons.person,
size: 50,
color: Colors.white,
)
: ClipRRect(
borderRadius: BorderRadius.circular(50),
child: Image.memory(
model.imageBytes!,
width: 100,
height: 100,
fit: BoxFit.cover,
),
),
),
Positioned(
bottom: 0,
right: 0,
child: CircleAvatar(
radius: 15,
backgroundColor: blueColor,
child: IconButton(
onPressed: () {
model.addImage();
},
icon: const Icon(
Icons.add,
color: lightColor,
size: 15,
)),
),
),
],
),
const SizedBox(height: 10),
MyTextFormField(
labelText: 'NIS',
controller: model.nisController,
keyboardType: TextInputType.number,
validator: Validatorless.multiple([
Validatorless.required('NIS tidak boleh kosong'),
Validatorless.number('NIS harus angka'),
]),
),
const SizedBox(height: 10),
MyTextFormField(
labelText: 'Nama',
controller: model.namaController,
validator:
Validatorless.required('Nama tidak boleh kosong'),
),
const SizedBox(height: 10),
MyTextFormField(
labelText: 'Tanggal Lahir',
controller: model.tanggalLahirController,
readOnly: true,
validator: Validatorless.required(
'Tanggal lahir tidak boleh kosong'),
onTap: () {
model.changeDate(context);
},
),
const SizedBox(height: 10),
// create dropdown button
Container(
width: double.infinity,
height: 60,
padding: const EdgeInsets.symmetric(horizontal: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25),
border: Border.all(
color: mainColor,
),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
value: model.jenisKelamin,
onChanged: (String? newValue) {
// model.setSelectedJenisKelamin(newValue!);
model.log.i(newValue);
model.jenisKelamin = newValue!;
model.notifyListeners();
},
items: model.jenisKelaminList.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value,
style: regularTextStyle.copyWith(
fontSize: 16,
),
),
);
}).toList(),
),
),
),
const SizedBox(height: 10),
MyTextFormField(
labelText: 'Alamat',
controller: model.alamatController,
maxLines: 2,
validator:
Validatorless.required('Alamat tidak boleh kosong'),
),
const SizedBox(height: 10),
MyTextFormField(
labelText: 'Keahlian',
controller: model.keahlianController,
maxLines: 4,
validator: Validatorless.required(
'Keahlian tidak boleh kosong'),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () => completer(
DialogResponse(
confirmed: false,
),
),
child: const Text(
'Batal',
style: TextStyle(
color: dangerColor,
),
),
),
TextButton(
onPressed: () async {
if (model.formKey.currentState!.validate()) {
bool res = await model.postData();
model.log.i("res: $res");
// if (res) {
// completer(
// DialogResponse(
// confirmed: true,
// ),
// );
// }
}
},
child: const Text(
'Simpan',
style: TextStyle(
color: blueColor,
),
),
),
],
),
],
),
),
)),
);
},
);
}
}

View File

@ -0,0 +1,113 @@
import 'dart:typed_data';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_holo_date_picker/flutter_holo_date_picker.dart';
import 'package:image_picker/image_picker.dart';
import 'package:panti_asuhan/app/app.locator.dart';
import 'package:panti_asuhan/app/core/custom_base_view_model.dart';
import '../../../../../app/app.logger.dart';
import '../../../../../services/http_services.dart';
import '../../../../../services/my_easyloading.dart';
class AddSiswaDialogViewModel extends CustomBaseViewModel {
final log = getLogger('AddSiswaDialogViewModel');
final _httpService = locator<MyHttpServices>();
final easyLoading = locator<MyEasyLoading>();
List<String> jenisKelaminList = ['Laki-laki', 'Perempuan'];
String jenisKelamin = 'Laki-laki';
// image picker
String? _imagePath;
final ImagePicker _picker = ImagePicker();
XFile? imageFile;
Uint8List? imageBytes;
// form and text controller
final formKey = GlobalKey<FormState>();
TextEditingController nisController = TextEditingController();
TextEditingController namaController = TextEditingController();
TextEditingController tanggalLahirController = TextEditingController();
TextEditingController alamatController = TextEditingController();
TextEditingController keahlianController = TextEditingController();
Future<void> init() async {}
void changeDate(BuildContext context) async {
// get today's date
var datePicked = await DatePicker.showSimpleDatePicker(
context,
initialDate: DateTime(2010),
firstDate: DateTime(2000),
lastDate: DateTime(2015),
dateFormat: "dd-MMMM-yyyy",
locale: DateTimePickerLocale.id,
looping: true,
);
if (datePicked != null) {
String date = datePicked.toString().split(' ')[0];
tanggalLahirController.text = date;
}
}
void addImage() async {
try {
final XFile? image = await _picker.pickImage(source: ImageSource.gallery);
if (image != null) {
imageFile = image;
_imagePath = image.path;
imageBytes = await image.readAsBytes();
log.i('image path: $_imagePath');
notifyListeners();
}
} catch (e) {
log.e(e);
}
}
Future<bool> postData() async {
if (imageBytes == null) {
easyLoading.showError('Foto belum dipilih');
return false;
}
easyLoading.customLoading('Menambahkan data...');
try {
var formData = FormData.fromMap({
'nis': nisController.text,
'nama': namaController.text,
'jenis_kelamin': jenisKelamin,
'tanggal_lahir': tanggalLahirController.text,
'alamat': alamatController.text,
'keahlian': keahlianController.text,
'foto': await MultipartFile.fromFile(_imagePath!),
});
var response = await _httpService.postWithFormData('siswa', formData);
easyLoading.dismissLoading();
log.i(response.data);
easyLoading.showSuccess('Siswa berhasil ditambahkan');
return true;
} catch (e) {
easyLoading.dismissLoading();
snackbarService.showSnackbar(
message: 'Terjadi kesalahan',
title: 'Gagal',
duration: const Duration(seconds: 4),
);
log.e(e);
return false;
}
// completer(
// DialogResponse(
// confirmed: true,
// ),
// );
}
}

View File

@ -20,10 +20,9 @@ class AdminIndexView extends StatelessWidget {
) {
return const Scaffold(
body: Center(
child: Text(
'AdminIndexView asdas asda aasdsda a',
),
),
child: CircularProgressIndicator(
color: Colors.grey,
)),
);
},
);

View File

@ -36,6 +36,12 @@ class AdminIndexTrackingViewModel extends IndexTrackingViewModel {
Future<void> init() async {
setIndex(1);
// await 2 seconds to make sure the view is loaded
await Future.delayed(const Duration(milliseconds: 500));
_navigationService.navigateTo(
_views[1],
id: 3,
);
}
void handleNavigation(int index) {

View File

@ -21,14 +21,62 @@ class DanaSosialAdminView extends StatelessWidget {
Widget? child,
) {
return Scaffold(
body: Column(
children: [
Container(
padding:
const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
width: double.infinity,
body: Column(
children: [
Container(
padding:
const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
width: double.infinity,
decoration: BoxDecoration(
color: mainColor,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: mainGrey.withOpacity(0.5),
spreadRadius: 5,
blurRadius: 7,
offset: const Offset(0, 3), // changes position of shadow
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Dana Sosial Bulan Ini',
style: boldTextStyle.copyWith(
color: Colors.white,
fontSize: 20,
),
),
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Total Dana Sosial',
style: regularTextStyle.copyWith(
color: Colors.white,
fontSize: 15,
),
),
Text(
'Rp. 1.000.000',
style: regularTextStyle.copyWith(
color: Colors.white,
fontSize: 15,
),
),
],
),
],
),
),
const SizedBox(height: 25),
Expanded(
child: Container(
decoration: BoxDecoration(
color: mainColor,
color: Colors.white,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
@ -40,84 +88,38 @@ class DanaSosialAdminView extends StatelessWidget {
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Dana Sosial Bulan Ini',
style: boldTextStyle.copyWith(
color: Colors.white,
fontSize: 20,
child: ListView.builder(
padding: const EdgeInsets.symmetric(
horizontal: 15, vertical: 10),
itemCount: 20,
itemBuilder: (context, index) {
return Card(
child: GestureDetector(
onTap: () {
model.log.i('Card $index tapped');
},
child: ListTile(
title: Text('1/02/15 - 10.00 am',
style: boldTextStyle.copyWith(
fontSize: 13, color: mainColor)),
subtitle: Text('Progress $index'),
trailing: Text('Pembangunan $index'),
),
),
),
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Total Dana Sosial',
style: regularTextStyle.copyWith(
color: Colors.white,
fontSize: 15,
),
),
Text(
'Rp. 1.000.000',
style: regularTextStyle.copyWith(
color: Colors.white,
fontSize: 15,
),
),
],
),
],
);
},
),
),
const SizedBox(height: 25),
Expanded(
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: mainGrey.withOpacity(0.5),
spreadRadius: 5,
blurRadius: 7,
offset:
const Offset(0, 3), // changes position of shadow
),
],
),
child: ListView.builder(
padding: const EdgeInsets.symmetric(
horizontal: 15, vertical: 10),
itemCount: 20,
itemBuilder: (context, index) {
return Card(
child: GestureDetector(
onTap: () {
model.log.i('Card $index tapped');
},
child: ListTile(
title: Text('1/02/15 - 10.00 am',
style: boldTextStyle.copyWith(
fontSize: 13, color: mainColor)),
subtitle: Text('Progress $index'),
trailing: Text('Pembangunan $index'),
),
),
);
},
),
),
),
],
),
floatingActionButton: const FloatingActionButton(
onPressed: null,
child: Icon(Icons.add),
));
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
model.goToTambahDanaSosial();
},
child: const Icon(Icons.add),
),
);
},
);
}

View File

@ -1,8 +1,12 @@
import 'package:panti_asuhan/app/core/custom_base_view_model.dart';
import '../../../../app/app.logger.dart';
import '../../../../app/app.router.dart';
import '../../../../app/core/custom_base_view_model.dart';
class DanaSosialAdminViewModel extends CustomBaseViewModel {
final log = getLogger('DanaSosialAdminViewModel');
Future<void> init() async {}
goToTambahDanaSosial() {
navigationService.navigateTo(Routes.tambahDanaSosialView);
}
}

View File

@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
import '../../../../app/themes/app_colors.dart';
import '../../../../app/themes/app_text.dart';
import './data_siswa_view_model.dart';
class DataSiswaView extends StatelessWidget {
@ -18,11 +20,102 @@ class DataSiswaView extends StatelessWidget {
DataSiswaViewModel model,
Widget? child,
) {
return const Scaffold(
body: Center(
child: Text(
'DataSiswaView',
),
return Scaffold(
body: Column(
children: [
Container(
padding:
const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
width: double.infinity,
decoration: BoxDecoration(
color: mainColor,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: mainGrey.withOpacity(0.5),
spreadRadius: 5,
blurRadius: 7,
offset: const Offset(0, 3), // changes position of shadow
),
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Total Siswa',
style: regularTextStyle.copyWith(
color: Colors.white,
fontSize: 15,
),
),
Text(
'20 orang',
style: regularTextStyle.copyWith(
color: Colors.white,
fontSize: 15,
),
),
],
),
),
const SizedBox(height: 25),
Expanded(
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: mainGrey.withOpacity(0.5),
spreadRadius: 5,
blurRadius: 7,
offset:
const Offset(0, 3), // changes position of shadow
),
],
),
child: ListView.builder(
padding: const EdgeInsets.symmetric(
horizontal: 15, vertical: 10),
itemCount: 20,
itemBuilder: (context, index) {
return Card(
child: GestureDetector(
onTap: () {
model.log.i('Card $index tapped');
},
child: ListTile(
title: Text('Namanya',
style: boldTextStyle.copyWith(
fontSize: 13, color: mainColor)),
subtitle: Text('Umurnya : $index'),
// circle avatar
trailing: Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: mainColor,
borderRadius: BorderRadius.circular(50),
),
child: const Icon(
Icons.person,
color: Colors.white,
),
)),
),
);
},
),
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
model.addSiswa();
},
child: const Icon(Icons.add),
),
);
},

View File

@ -1,5 +1,19 @@
import 'package:panti_asuhan/app/core/custom_base_view_model.dart';
import '../../../../app/app.dialogs.dart';
import '../../../../app/app.logger.dart';
import '../add_siswa_dialog/add_siswa_dialog/add_siswa_dialog_view.dart';
class DataSiswaViewModel extends CustomBaseViewModel {
final log = getLogger('DataSiswaViewModel');
Future<void> init() async {}
void addSiswa() async {
final res = await dialogService.showCustomDialog(
variant: DialogType.addSiswaDialogView,
data: DataSiswa(nama: null),
);
if (res?.confirmed != true) return;
}
}

View File

@ -24,58 +24,60 @@ class LoginScreenView extends StatelessWidget {
return Scaffold(
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
height: MediaQuery.of(context).size.height * 0.2,
),
// show the logo.png
const Center(
child: Image(
image: AssetImage("assets/logo.png"),
width: 150,
height: 150,
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
height: MediaQuery.of(context).size.height * 0.2,
),
),
const SizedBox(height: 10),
Text(
"SILAHKAN LOGIN",
style: boldTextStyle.copyWith(
fontSize: 18,
// show the logo.png
const Center(
child: Image(
image: AssetImage("assets/logo.png"),
width: 150,
height: 150,
),
),
),
const SizedBox(
height: 10,
),
const MyTextFormField(
hintText: "Username",
prefixIcon: Icon(Icons.person),
// controller: model.usernameController,
),
const SizedBox(
height: 10,
),
const MyTextFormField(
hintText: "Password",
prefixIcon: Icon(Icons.lock),
// controller: model.passwordController,
),
const SizedBox(
height: 10,
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.5,
child: MyButton(
text: "LOGIN",
onPressed: () {
model.goToAdmin();
},
const SizedBox(height: 10),
Text(
"SILAHKAN LOGIN",
style: boldTextStyle.copyWith(
fontSize: 18,
),
),
),
],
const SizedBox(
height: 10,
),
const MyTextFormField(
hintText: "Username",
prefixIcon: Icon(Icons.person),
// controller: model.usernameController,
),
const SizedBox(
height: 10,
),
const MyTextFormField(
hintText: "Password",
prefixIcon: Icon(Icons.lock),
// controller: model.passwordController,
),
const SizedBox(
height: 10,
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.5,
child: MyButton(
text: "LOGIN",
onPressed: () {
model.goToAdmin();
},
),
),
],
),
),
),
);

View File

@ -0,0 +1,134 @@
import 'package:flutter/material.dart';
import 'package:panti_asuhan/app/themes/app_colors.dart';
import 'package:panti_asuhan/app/themes/app_text.dart';
import 'package:panti_asuhan/ui/widgets/my_button.dart';
import 'package:panti_asuhan/ui/widgets/my_textformfield.dart';
import 'package:stacked/stacked.dart';
import 'package:validatorless/validatorless.dart';
import './tambah_dana_sosial_view_model.dart';
class TambahDanaSosialView extends StatelessWidget {
const TambahDanaSosialView({super.key});
@override
Widget build(BuildContext context) {
return ViewModelBuilder<TambahDanaSosialViewModel>.reactive(
viewModelBuilder: () => TambahDanaSosialViewModel(),
onViewModelReady: (TambahDanaSosialViewModel model) async {
await model.init();
},
builder: (
BuildContext context,
TambahDanaSosialViewModel model,
Widget? child,
) {
return Scaffold(
appBar: AppBar(
title: const Text(
"Form Dana Sosial",
style: TextStyle(
color: Colors.white,
fontSize: 20,
),
),
backgroundColor: mainColor,
elevation: 0,
),
body: Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15),
child: Form(
key: model.formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Keterangan",
style: regularTextStyle.copyWith(color: mainColor),
),
MyTextFormField(
hintText: "Keterangan",
controller: model.ketController,
maxLines: 3,
validator: Validatorless.required(
'Keterangan tidak boleh kosong'),
),
const SizedBox(height: 20),
Text(
"Jumlah (Rp. )",
style: regularTextStyle.copyWith(color: mainColor),
),
MyTextFormField(
hintText: "Jumlah (Rp. )",
keyboardType: TextInputType.number,
controller: model.jumlahController,
validator: Validatorless.multiple(
[
Validatorless.required('Jumlah tidak boleh kosong'),
Validatorless.number('Jumlah harus angka'),
],
),
),
const SizedBox(height: 20),
Text(
"Tanggal",
style: regularTextStyle.copyWith(color: mainColor),
),
MyTextFormField(
hintText: 'Tanggal',
readOnly: true,
validator: Validatorless.required(
'Tanggal lahir tidak boleh kosong'),
onTap: () {
model.changeDate(context);
},
),
const SizedBox(height: 20),
Text(
"Jenis Dana",
style: regularTextStyle.copyWith(color: mainColor),
),
Container(
width: double.infinity,
height: 60,
padding: const EdgeInsets.symmetric(horizontal: 20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25),
border: Border.all(
color: mainColor,
),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
value: model.jenisDana,
onChanged: (String? newValue) {
model.jenisDana = newValue!;
},
items: model.jenisDanaList
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value,
style: const TextStyle(fontSize: 16)),
);
}).toList(),
),
),
),
const SizedBox(height: 20),
MyButton(
text: "Simpan",
onPressed: () {},
),
],
),
),
),
),
);
},
);
}
}

View File

@ -0,0 +1,31 @@
import 'package:flutter/material.dart';
import 'package:panti_asuhan/app/core/custom_base_view_model.dart';
class TambahDanaSosialViewModel extends CustomBaseViewModel {
String jenisDana = 'Pemasukan';
List<String> jenisDanaList = ['Pemasukan', 'Pengeluaran'];
final formKey = GlobalKey<FormState>();
TextEditingController ketController = TextEditingController();
TextEditingController jumlahController = TextEditingController();
TextEditingController tanggalController = TextEditingController();
Future<void> init() async {}
void changeDate(BuildContext context) async {
// get today's date
var datePicked = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2000),
// last date is today's date
lastDate: DateTime.now(),
);
if (datePicked != null) {
String date = datePicked.toString().split(' ')[0];
tanggalController.text = date;
}
}
}

View File

@ -15,6 +15,9 @@ class MyTextFormField extends StatelessWidget {
this.controller,
this.maxLines = 1,
this.onEditingComplete,
this.readOnly = false,
this.onTap,
this.keyboardType = TextInputType.text,
}) : super(key: key);
final String? labelText;
@ -27,6 +30,9 @@ class MyTextFormField extends StatelessWidget {
final TextEditingController? controller;
final int maxLines;
final VoidCallback? onEditingComplete;
final bool readOnly;
final VoidCallback? onTap;
final TextInputType keyboardType;
@override
Widget build(BuildContext context) {
@ -36,6 +42,9 @@ class MyTextFormField extends StatelessWidget {
controller: controller,
focusNode: focusNode,
obscureText: obscureText ?? false,
readOnly: readOnly,
onTap: onTap,
keyboardType: keyboardType,
decoration: InputDecoration(
prefixIcon: prefixIcon,
suffixIcon: suffixIcon,

View File

@ -33,6 +33,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.10.0"
auto_size_text:
dependency: transitive
description:
name: auto_size_text
sha256: "3f5261cd3fb5f2a9ab4e2fc3fba84fd9fcaac8821f20a1d4e71f557521b22599"
url: "https://pub.dev"
source: hosted
version: "3.0.0"
boolean_selector:
dependency: transitive
description:
@ -246,6 +254,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.5"
flutter_holo_date_picker:
dependency: "direct main"
description:
name: flutter_holo_date_picker
sha256: "94cf29471ffac123043745b65c690dcf6d481e98c11bda71f78a34dd6f726247"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
flutter_lints:
dependency: "direct dev"
description:
@ -813,6 +829,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.2.0"
validatorless:
dependency: "direct main"
description:
name: validatorless
sha256: ddb46df636114b3322d289489164cac309767b157191ba43c7ad49b28c2b57c7
url: "https://pub.dev"
source: hosted
version: "1.2.3"
vector_graphics:
dependency: transitive
description:

View File

@ -46,6 +46,9 @@ dependencies:
google_fonts:
flutter_svg:
stylish_bottom_bar: ^1.0.0
# calendar_date_picker2: ^0.5.2
flutter_holo_date_picker: ^1.1.0
validatorless: ^1.2.3
dev_dependencies:
flutter_test: