step 7 : ui login, transaksi, user sesuai wireframe

This commit is contained in:
sindhu
2024-01-15 12:10:15 +07:00
parent 71085788cd
commit 95df7d4c5b
13 changed files with 1151 additions and 303 deletions

View File

@@ -10,7 +10,7 @@ class Constant {
static double designWidthPhone = 390;
static String bearerName = "petty-cash";
static String version = "2.00";
static String version = "1.00";
// static String baseUrl = "https://devregonline.pramita.co.id/one-api/xdoc/";
@@ -125,7 +125,7 @@ class Constant {
// static String baseUrl_appdoctor =
// "http://balikpapan.bisone2.pramitalab.id/one-api/app_doctor/";
// // * padang
// static String baseUrl =
// "http://padang.bisone4.pramitalab.id/one-api-doctor/doctor_mitra/";
@@ -154,12 +154,12 @@ class Constant {
// static String baseUrl_appdoctor =
// "http://manado.bisone2.pramitalab.id/one-api/app_doctor/";
// * bekasi
// * bekasi
// static String baseUrl =
// "http://bekasiraya.bisone2.pramitalab.id/one-api-doctor/doctor_mitra/";
// static String baseUrl_appdoctor =
// "http://bekasiraya.bisone2.pramitalab.id/one-api/app_doctor/";
// "http://bekasiraya.bisone2.pramitalab.id/one-api/app_doctor/";
//color Theme
// login marketing
@@ -255,6 +255,9 @@ class Constant {
static Color textWhite = Color(0xffFDFDFD);
static Color textGreen = Color(0xff0BAF48);
// background upload file
static Color bgUploadFile = Color(0xffF5F5F5);
//typoGraphy
static TextStyle titleH1Login({required BuildContext context}) {
return TextStyle(
@@ -362,6 +365,13 @@ class Constant {
);
}
static TextStyle titleH1Login_SelamatDatang({required BuildContext context}) {
return TextStyle(
fontSize: Constant.getActualYPhone(context: context, y: 32),
fontWeight: FontWeight.w500,
);
}
static TextStyle titleH1_600({required BuildContext context}) {
return TextStyle(
fontSize: Constant.getActualYPhone(context: context, y: 36),

View File

@@ -1,7 +1,8 @@
import 'package:app_petty_cash/screen/home/home_screen.dart';
import 'package:app_petty_cash/screen/transaksi/transaksi_screen.dart';
import 'package:app_petty_cash/screen/user/user_screen.dart';
import 'package:flutter/material.dart';
import '../screen/home/home_screen.dart';
import '../screen/transaksi/transaksi_screen.dart';
import '../screen/login/login_screen.dart';
import '../screen/splash/splash_screen.dart';
import '../screen/test_file_picker/test_file_picker.dart';
@@ -11,6 +12,9 @@ const menuRoute = "/menuRoute";
const splashScreen = "/splashScreen";
const homeRoute = "/homeRoute";
const transaksiRoute = "/transaksiRoute";
const userRoute = "/userRoute";
// test screen
const testFilePickerRoute = "/testFilePickerRoute";
class AppRoute {
@@ -28,6 +32,19 @@ class AppRoute {
});
}
// user screen
if (settings.name == userRoute) {
return MaterialPageRoute(builder: (context) {
return MediaQuery(
data: MediaQuery.of(context).copyWith(
textScaleFactor: 1.0,
padding: EdgeInsets.all(0),
),
child: UserScreen(),
);
});
}
// home screen
if (settings.name == homeRoute) {
return MaterialPageRoute(builder: (context) {
@@ -41,7 +58,7 @@ class AppRoute {
});
}
// tansaksi screen
// transaksi screen
if (settings.name == transaksiRoute) {
return MaterialPageRoute(builder: (context) {
return MediaQuery(

View File

@@ -1,12 +1,20 @@
import 'package:flutter/material.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'app/route.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarIconBrightness:
Brightness.dark, // this will change the brightness of the icons
statusBarColor: Colors.white, // or any color you want
));
runApp(
const ProviderScope(child: MyApp()),
const ProviderScope(
child: MyApp(),
),
);
}
@@ -18,7 +26,8 @@ class MyApp extends StatelessWidget {
return MaterialApp(
title: 'App Petty Cash',
theme: ThemeData(
primarySwatch: Colors.red,
// primarySwatch: Colors.red,
primarySwatch: Colors.orange,
),
scrollBehavior: MaterialScrollBehavior().copyWith(
dragDevices: {
@@ -31,8 +40,8 @@ class MyApp extends StatelessWidget {
debugShowCheckedModeBanner: false,
// initialRoute: loginRoute,
// initialRoute: splashScreen,
// initialRoute: transaksiRoute,
initialRoute: testFilePickerRoute,
initialRoute: transaksiRoute,
// initialRoute: testFilePickerRoute,
onGenerateRoute: AppRoute.generateRoute,
);
}

View File

@@ -1,65 +1,28 @@
import 'package:app_petty_cash/app/route.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
class HomeScreen extends StatelessWidget {
import '../../app/constant.dart';
import '../../widget/custom_drawer.dart';
class HomeScreen extends HookConsumerWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home Screen'),
Widget build(BuildContext context, WidgetRef ref) {
return Padding(
padding: EdgeInsets.only(
top: Constant.getActualYPhone(context: context, y: 30),
),
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: [
DrawerHeader(
decoration: BoxDecoration(
color: Colors.blue,
),
child: Text(
'Drawer Header',
style: TextStyle(
color: Colors.white,
fontSize: 24,
),
),
),
ListTile(
title: Text('Home'),
onTap: () {
// Handle navigation to Home screen
Navigator.pop(context);
Navigator.pushNamed(context, homeRoute);
},
),
ListTile(
title: Text('Transaksi'),
onTap: () {
// Handle navigation to Transaksi screen
Navigator.pop(context);
},
),
ListTile(
title: Text('User'),
onTap: () {
// Handle navigation to User screen
Navigator.pop(context);
},
),
ListTile(
title: Text('Logout'),
onTap: () {
// Handle logout logic
Navigator.pop(context);
},
),
],
child: Scaffold(
appBar: AppBar(
title: Text('Home Screen'),
),
drawer: CustomDrawer(),
body: SafeArea(
child: Center(
child: Text('Home Screen Content'),
),
),
),
body: Center(
child: Text('Home Screen Content'),
),
);
}

View File

@@ -97,24 +97,24 @@ class BlockBodyV2 extends HookConsumerWidget {
SizedBox(
height: Constant.getActualYPhone(context: context, y: 30),
),
Padding(
padding: EdgeInsets.only(
left: Constant.getActualXPhone(context: context, x: 40),
right: Constant.getActualXPhone(context: context, x: 40),
),
child: Container(
width: Constant.getActualXPhone(context: context, x: 283),
height: Constant.getActualYPhone(context: context, y: 100),
decoration: BoxDecoration(
// color: Colors.green,
image: DecorationImage(
fit: BoxFit.fitWidth,
// image: AssetImage('images/logo_pramita.png'),
image: AssetImage('images/logo_gabung_bg_png.png'),
),
),
),
),
// Padding(
// padding: EdgeInsets.only(
// left: Constant.getActualXPhone(context: context, x: 40),
// right: Constant.getActualXPhone(context: context, x: 40),
// ),
// child: Container(
// width: Constant.getActualXPhone(context: context, x: 283),
// height: Constant.getActualYPhone(context: context, y: 100),
// decoration: BoxDecoration(
// // color: Colors.green,
// image: DecorationImage(
// fit: BoxFit.fitWidth,
// // image: AssetImage('images/logo_pramita.png'),
// image: AssetImage('images/logo_gabung_bg_png.png'),
// ),
// ),
// ),
// ),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 60),
),
@@ -135,7 +135,7 @@ class BlockBodyV2 extends HookConsumerWidget {
.copyWith(color: Constant.subTitleBlack),
),
Text(
" DOKTER",
" PETTYCASH",
style: Constant.titleH3_700V2(context: context).copyWith(
color: Color(0xffd70302),
),

View File

@@ -0,0 +1,371 @@
// ignore_for_file: prefer_const_constructors, use_full_hex_values_for_flutter_colors
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import '../../app/constant.dart';
import '../../app/route.dart';
import '../../provider/current_menu_provider.dart';
import '../../provider/current_user_provider.dart';
import 'custom_text_field.dart';
import 'login_provider.dart';
class LoginFormScreen extends HookConsumerWidget {
const LoginFormScreen({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final isPasswordObscured = useState<bool>(true);
final ctrlEmail = useTextEditingController(text: "");
final ctrlDokterId = useTextEditingController(text: "");
final ctrlPassword = useTextEditingController(text: "");
final isLoading = useState(false);
final errorMessage = useState("");
final isSuccess = useState(false);
final focusNodeDokterID = useFocusNode();
final focusNodeDokterUsername = useFocusNode();
final focusNodeDokterPassword = useFocusNode();
final dokterIDhasFocus = useState(false);
final usernamehasFocus = useState(false);
final passwordhasFocus = useState(false);
focusNodeDokterID.addListener(() {
if (focusNodeDokterID.hasPrimaryFocus) {
dokterIDhasFocus.value = true;
usernamehasFocus.value = false;
passwordhasFocus.value = false;
} else {
dokterIDhasFocus.value = false;
}
});
focusNodeDokterUsername.addListener(() {
if (focusNodeDokterUsername.hasFocus) {
dokterIDhasFocus.value = false;
usernamehasFocus.value = true;
passwordhasFocus.value = false;
} else {
usernamehasFocus.value = false;
}
});
focusNodeDokterPassword.addListener(() {
if (focusNodeDokterPassword.hasFocus) {
dokterIDhasFocus.value = false;
usernamehasFocus.value = false;
passwordhasFocus.value = true;
} else {
passwordhasFocus.value = false;
}
});
ref.listen(loginProvider, (prev, next) {
if (next is LoginStateLoading) {
isLoading.value = true;
} else if (next is LoginStateError) {
isLoading.value = false;
errorMessage.value = next.message;
Timer(const Duration(seconds: 3), () {
errorMessage.value = "";
});
} else if (next is LoginStateDone) {
isLoading.value = false;
isSuccess.value = true;
ref.read(currentPageProvider.state).update((state) => 0);
ref.read(currentUserProvider.notifier).state = next.model;
// print(next.model.model.doctorName);
// Navigator.of(context).pop();
// Navigator.of(context).pushNamedAndRemoveUntil(menuRoute,(route) => true,);
// Navigator.popAndPushNamed(context,menuRoute);
Navigator.of(context)
.pushNamedAndRemoveUntil(menuRoute, (route) => false);
}
});
return Scaffold(
body: SafeArea(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
width: Constant.getActualXPhone(context: context, x: 390),
height: Constant.getActualYPhone(context: context, y: 359),
decoration: BoxDecoration(color: Colors.grey),
),
Container(
width: Constant.getActualXPhone(context: context, x: 390),
height: Constant.getActualYPhone(context: context, y: 485),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(
top: Constant.getActualYPhone(context: context, y: 63),
left: Constant.getActualXPhone(context: context, x: 20),
right:
Constant.getActualXPhone(context: context, x: 113),
),
child: Text(
'Selamat Datang',
style: Constant.titleH1Login_SelamatDatang(
context: context)
.copyWith(
fontWeight: FontWeight.w600,
color: Constant.textBlack),
),
),
Padding(
padding: EdgeInsets.only(
// top: Constant.getActualYPhone(context: context, y: 63),
left: Constant.getActualXPhone(context: context, x: 20),
right:
Constant.getActualXPhone(context: context, x: 48),
),
child: Text(
'Silahkan masuk untuk mengakses akun Anda',
style: Constant.body1(context: context).copyWith(
fontWeight: FontWeight.w600,
color: Constant.textBlack),
),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 39),
),
// inputan email
Padding(
padding: EdgeInsets.only(
// top: Constant.getActualYPhone(context: context, y: 63),
left: Constant.getActualXPhone(context: context, x: 20),
right:
Constant.getActualXPhone(context: context, x: 20),
),
child: Text(
'Email',
style: Constant.body1_400_dibulan(context: context)
.copyWith(
fontWeight: FontWeight.w600,
color: Constant.textBlack),
),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 4),
),
Padding(
padding: EdgeInsets.only(
// top: Constant.getActualYPhone(context: context, y: 63),
left: Constant.getActualXPhone(context: context, x: 20),
right:
Constant.getActualXPhone(context: context, x: 20),
),
child: TextField(
controller: ctrlEmail,
decoration: InputDecoration(
hintStyle:
Constant.body2_400(context: context).copyWith(
color: Colors.orange,
),
labelStyle:
Constant.body2_400(context: context).copyWith(
color: Colors.orange,
),
border: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.orange,
width: 1,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.orange,
width: 1,
),
),
// labelText: "Nama Pengirim",
// hintText: 'Nama Pengirim',
),
),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 21),
),
// password
Padding(
padding: EdgeInsets.only(
// top: Constant.getActualYPhone(context: context, y: 63),
left: Constant.getActualXPhone(context: context, x: 20),
right:
Constant.getActualXPhone(context: context, x: 20),
),
child: Text(
'Password',
style: Constant.body1_400_dibulan(context: context)
.copyWith(
fontWeight: FontWeight.w600,
color: Constant.textBlack),
),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 4),
),
Padding(
padding: EdgeInsets.only(
// top: Constant.getActualYPhone(context: context, y: 63),
left: Constant.getActualXPhone(context: context, x: 20),
right:
Constant.getActualXPhone(context: context, x: 20),
),
child: TextField(
controller: ctrlPassword,
decoration: InputDecoration(
hintStyle:
Constant.body2_400(context: context).copyWith(
color: Colors.orange,
),
labelStyle:
Constant.body2_400(context: context).copyWith(
color: Colors.orange,
),
border: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.orange,
width: 1,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.orange,
width: 1,
),
),
// labelText: "Nama Pengirim",
// hintText: 'Nama Pengirim',
),
),
),
if (isLoading.value)
SizedBox(
width:
Constant.getActualXPhone(context: context, x: 20),
height:
Constant.getActualYPhone(context: context, y: 20),
child: Center(child: CircularProgressIndicator()),
),
if (errorMessage.value != "") ...[
SizedBox(
height:
Constant.getActualYPhone(context: context, y: 10),
),
Center(
child: Text(
"Peringatan : ${errorMessage.value}",
style: Constant.titleH7_700(context: context)
.copyWith(color: Constant.textRedProblemMaketing),
),
),
],
SizedBox(
height: Constant.getActualYPhone(context: context, y: 40),
),
Padding(
padding: EdgeInsets.only(
left: Constant.getActualXPhone(context: context, x: 20),
right:
Constant.getActualXPhone(context: context, x: 20),
),
child: SizedBox(
width:
Constant.getActualXPhone(context: context, x: 390),
height:
Constant.getActualYPhone(context: context, y: 50),
child: ElevatedButton(
onPressed: () {
Navigator.of(context).pushNamed(homeRoute);
},
// onPressed: (isSuccess.value == true)
// ? () {}
// : () {
// if (ctrlEmail.text.isEmpty ||
// ctrlDokterId.text.isEmpty ||
// ctrlPassword.text.isEmpty) {
// isLoading.value = true;
// errorMessage.value = 'Inputan harus diisi';
// Timer(const Duration(seconds: 3), () {
// isLoading.value = false;
// errorMessage.value = "";
// });
// } else {
// ref.read(loginProvider.notifier).login(
// username: ctrlEmail.text,
// dokterId: ctrlDokterId.text,
// password: ctrlPassword.text);
// }
// },
style: ButtonStyle(
backgroundColor: MaterialStateColor.resolveWith(
(st) => (isSuccess.value == true)
? Constant.textGrey
: Colors.orange),
shape: MaterialStateProperty.all<
RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(color: Colors.orange),
),
),
shadowColor:
MaterialStateProperty.all(Color(0xffff48423d)),
elevation: MaterialStateProperty.all(4.0),
),
child: Align(
alignment: Alignment.center,
child: Text(
'Login',
style: Constant.titleH3_700(context: context)
.copyWith(color: Constant.textLoginColor),
),
),
),
),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 10),
),
Align(
alignment: Alignment.center,
child: Text(
'Versi ${Constant.version}',
style: Constant.titleH4Login(context: context)
.copyWith(color: Constant.textGrey),
),
),
],
),
),
],
),
),
),
);
}
}

View File

@@ -14,6 +14,7 @@ import '../../provider/current_menu_provider.dart';
import '../../provider/current_user_provider.dart';
import 'block_body.dart';
import 'block_bodyv2.dart';
import 'login_form.dart';
class LoginScreen extends HookConsumerWidget {
const LoginScreen({Key? key}) : super(key: key);
@@ -68,8 +69,8 @@ class LoginScreen extends HookConsumerWidget {
end: Alignment.centerRight,
),
),
// child: BlockBody(),
child: BlockBodyV2(),
// child: BlockBodyV2(),
child: LoginFormScreen(),
),
),
),

View File

@@ -1,9 +1,13 @@
import 'package:app_petty_cash/app/app_extension.dart';
import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:intl/intl.dart';
import '../../app/constant.dart';
import '../../widget/custom_drawer.dart';
import '../login/custom_text_field.dart';
class DummyDropdownTipe {
@@ -30,6 +34,18 @@ class TransaksiScreen extends HookConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final ctrlJumlah = useTextEditingController(text: "");
final ctrlCatatan = useTextEditingController(text: "");
final ctrlNamaPengirim = useTextEditingController(text: "");
String formattedDate = DateFormat('dd-MM-yyyy').format(DateTime.now());
// final ctrlTglAwal = useTextEditingController(
// text: DateTime.now().toString().split(' ')[0],
// );
final ctrlTglAwal = useTextEditingController(text: formattedDate);
final tglAwal = useState<DateTime>(DateTime.now());
final tglAwalTmp = useState<String>("");
final selectedDropdownKategori = useState<DummyDropdownTipe>(
DummyDropdownTipe(
@@ -58,244 +74,509 @@ class TransaksiScreen extends HookConsumerWidget {
selectedDropdownKategori.value = dropdownItemsKategori.value[0];
radioButtonItems.value = [
DummyRadioTipe(
"Debit",
"Income",
1,
),
DummyRadioTipe(
"Kredit",
"Expenses",
2,
),
];
selectedRadio.value = radioButtonItems.value[0];
return () {};
}, []);
final isMounted = useIsMounted();
return Scaffold(
appBar: AppBar(
title: Text('Transaksi'),
var sbHeight = MediaQuery.of(context).padding.top;
print('Height : ${sbHeight}');
return Padding(
padding: EdgeInsets.only(
top: Constant.getActualYPhone(context: context, y: 30),
),
body: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Kategori',
style: Constant.body1(context: context).copyWith(
fontWeight: FontWeight.w600, color: Constant.textBlack),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 10),
),
// Dropdown kategori
SizedBox(
width: Constant.getActualXPhone(context: context, x: 390),
child: DropdownButtonHideUnderline(
child: DropdownButton2<DummyDropdownTipe>(
isExpanded: true,
hint: Row(
children: [
Expanded(
child: Text(
'Select Item',
style: Constant.body1(context: context).copyWith(
fontWeight: FontWeight.w600,
color: Constant.textBlack),
overflow: TextOverflow.ellipsis,
),
),
],
),
items: dropdownItemsKategori.value
.map((DummyDropdownTipe option) {
return DropdownMenuItem<DummyDropdownTipe>(
value: option,
child: Text(
option.options,
style: Constant.body1(context: context).copyWith(
color: Constant.textBlack,
fontWeight: FontWeight.w600),
),
);
}).toList(),
value: selectedDropdownKategori.value,
onChanged: (DummyDropdownTipe? newValue) {
// if (newValue) {
selectedDropdownKategori.value = newValue!;
print(selectedDropdownKategori.value.id);
// }
},
buttonStyleData: ButtonStyleData(
height: Constant.getActualY(context: context, y: 56),
width: Constant.getActualX(context: context, x: 320),
padding: EdgeInsets.only(
left: Constant.getActualX(context: context, x: 10),
right: Constant.getActualX(context: context, x: 10),
),
decoration: BoxDecoration(
color: Constant.white,
border: Border.all(color: Constant.textBlack, width: 1),
borderRadius: BorderRadius.circular(8),
),
elevation: 2,
),
iconStyleData: IconStyleData(
icon: Icon(
Icons.keyboard_arrow_down_outlined,
),
iconSize: 24,
iconEnabledColor: Constant.textBlack,
iconDisabledColor: Colors.grey,
),
dropdownStyleData: DropdownStyleData(
maxHeight: Constant.getActualY(context: context, y: 200),
// width: Constant.getActualX(context: context, x: 320),
padding: EdgeInsets.only(
top: Constant.getActualY(context: context, y: 10),
left: Constant.getActualX(context: context, x: 10),
right: Constant.getActualX(context: context, x: 10),
bottom: Constant.getActualY(context: context, y: 10),
),
decoration: BoxDecoration(
color: Constant.white,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.grey,
blurRadius: 20.0,
spreadRadius: 2.0,
offset: Offset(0.0, 0.0),
),
],
),
elevation: 8,
offset: const Offset(0, -10),
scrollbarTheme: ScrollbarThemeData(
radius: const Radius.circular(40),
thickness: MaterialStateProperty.all<double>(6),
thumbVisibility: MaterialStateProperty.all<bool>(true),
),
),
menuItemStyleData: MenuItemStyleData(
height: Constant.getActualY(context: context, y: 56),
padding: EdgeInsets.only(
top: Constant.getActualY(context: context, y: 10),
left: Constant.getActualX(context: context, x: 10),
right: Constant.getActualX(context: context, x: 10),
),
),
child: Scaffold(
appBar: AppBar(
// centerTitle: true,
title: Text('Transaksi'),
),
drawer: CustomDrawer(),
body: SafeArea(
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Tanggal Transaksi',
style: Constant.body1(context: context).copyWith(
fontWeight: FontWeight.w600, color: Constant.textBlack),
),
),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 20),
),
// jenis
Text(
'Jenis',
style: Constant.body1(context: context).copyWith(
fontWeight: FontWeight.w600, color: Constant.textBlack),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 10),
),
SizedBox(
width: Constant.getActualXPhone(context: context, x: 340),
height: Constant.getActualYPhone(context: context, y: 36),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
if (radioButtonItems.value.isEmpty)
Text('Radio Button Empty')
else
for (var i = 0; i < radioButtonItems.value.length; i++)
Padding(
padding: const EdgeInsets.only(right: 20),
child: Row(
children: [
Radio<DummyRadioTipe>(
activeColor: Constant.confirmed,
value: radioButtonItems.value[i],
groupValue: selectedRadio.value,
onChanged: (DummyRadioTipe? index) {
if (isMounted()) {
selectedRadio.value = index!;
} else {
return;
}
},
SizedBox(
height: Constant.getActualYPhone(context: context, y: 10),
),
// Tanggal Transaksi
Row(
children: [
Expanded(
child: TextField(
controller: ctrlTglAwal,
decoration: InputDecoration(
hintStyle:
Constant.body2_400(context: context).copyWith(
color: Colors.orange,
),
labelStyle:
Constant.body2_400(context: context).copyWith(
color: Colors.orange,
),
border: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.orange,
width: 1,
),
Text(
radioButtonItems.value[i].text,
style:
Constant.body1(context: context).copyWith(
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.orange,
width: 1,
),
),
// labelText: "Tanggal Awal",
hintText: 'Tanggal Awal',
// suffixIcon: isLoadingFilterScope.value
// ? SizedBox(
// width: Constant.getActualXPhone(
// context: context,
// x: 4,
// ),
// height: Constant.getActualYPhone(
// context: context,
// y: 4,
// ),
// child: CircularProgressIndicator(
// color: Constant.textRed,
// ),
// )
// : Icon(
// Icons.calendar_month_sharp,
// color: Constant.colorIconDate,
// ),
),
onTap: () async {
final selectedDateAwal = await showDatePicker(
// locale: const Locale("en-CA"),
// locale: ,
context: context,
initialEntryMode:
DatePickerEntryMode.calendarOnly,
firstDate: DateTime(2000),
lastDate: DateTime(2100),
initialDate: (ctrlTglAwal.text.isEmpty)
? DateTime.now()
: tglAwal.value,
);
if (selectedDateAwal != null) {
String formattedDate = DateFormat('dd-MM-yyyy')
.format(selectedDateAwal);
// ctrlTglAwal.text =
// selectedDateAwal.toString().split(' ')[0];
ctrlTglAwal.text = formattedDate;
tglAwal.value = selectedDateAwal;
tglAwalTmp.value = selectedDateAwal.toString();
}
if (selectedDateAwal == null) {
print('cancel button');
return;
}
},
),
),
],
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 20),
),
// Kategori
Text(
'Kategori',
style: Constant.body1(context: context).copyWith(
fontWeight: FontWeight.w600, color: Constant.textBlack),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 10),
),
// Dropdown kategori
SizedBox(
width: Constant.getActualXPhone(context: context, x: 390),
child: DropdownButtonHideUnderline(
child: DropdownButton2<DummyDropdownTipe>(
isExpanded: true,
hint: Row(
children: [
Expanded(
child: Text(
'Select Item',
style: Constant.body1(context: context)
.copyWith(
fontWeight: FontWeight.w600,
color: Constant.textBlack),
overflow: TextOverflow.ellipsis,
),
),
],
),
items: dropdownItemsKategori.value
.map((DummyDropdownTipe option) {
return DropdownMenuItem<DummyDropdownTipe>(
value: option,
child: Text(
option.options,
style: Constant.body1(context: context).copyWith(
color: Constant.textBlack,
fontWeight: FontWeight.w400,
),
fontWeight: FontWeight.w600),
),
);
}).toList(),
value: selectedDropdownKategori.value,
onChanged: (DummyDropdownTipe? newValue) {
// if (newValue) {
selectedDropdownKategori.value = newValue!;
print(selectedDropdownKategori.value.id);
// }
},
buttonStyleData: ButtonStyleData(
height: Constant.getActualY(context: context, y: 56),
width: Constant.getActualX(context: context, x: 320),
padding: EdgeInsets.only(
left: Constant.getActualX(context: context, x: 10),
right: Constant.getActualX(context: context, x: 10),
),
decoration: BoxDecoration(
color: Constant.white,
border:
Border.all(color: Constant.textBlack, width: 1),
borderRadius: BorderRadius.circular(8),
),
elevation: 2,
),
iconStyleData: IconStyleData(
icon: Icon(
Icons.keyboard_arrow_down_outlined,
),
iconSize: 24,
iconEnabledColor: Constant.textBlack,
iconDisabledColor: Colors.grey,
),
dropdownStyleData: DropdownStyleData(
maxHeight:
Constant.getActualY(context: context, y: 200),
// width: Constant.getActualX(context: context, x: 320),
padding: EdgeInsets.only(
top: Constant.getActualY(context: context, y: 10),
left: Constant.getActualX(context: context, x: 10),
right: Constant.getActualX(context: context, x: 10),
bottom:
Constant.getActualY(context: context, y: 10),
),
decoration: BoxDecoration(
color: Constant.white,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.grey,
blurRadius: 20.0,
spreadRadius: 2.0,
offset: Offset(0.0, 0.0),
),
],
),
elevation: 8,
offset: const Offset(0, -10),
scrollbarTheme: ScrollbarThemeData(
radius: const Radius.circular(40),
thickness: MaterialStateProperty.all<double>(6),
thumbVisibility:
MaterialStateProperty.all<bool>(true),
),
),
],
menuItemStyleData: MenuItemStyleData(
height: Constant.getActualY(context: context, y: 56),
padding: EdgeInsets.only(
top: Constant.getActualY(context: context, y: 10),
left: Constant.getActualX(context: context, x: 10),
right: Constant.getActualX(context: context, x: 10),
),
),
),
),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 20),
),
Text(
'Nama Pengirim',
style: Constant.body1(context: context).copyWith(
fontWeight: FontWeight.w600, color: Constant.textBlack),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 10),
),
TextField(
controller: ctrlNamaPengirim,
decoration: InputDecoration(
hintStyle: Constant.body2_400(context: context).copyWith(
color: Colors.orange,
),
labelStyle: Constant.body2_400(context: context).copyWith(
color: Colors.orange,
),
border: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.orange,
width: 1,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.orange,
width: 1,
),
),
// labelText: "Nama Pengirim",
// hintText: 'Nama Pengirim',
),
),
// CustomTextField(
// isNumber: false,
// isReadOnly: false,
// ctrl: ctrlNamaPengirim,
// isPassword: false,
// isMaxLine: false,
// hintText: "Nama Pengirim",
// labelText: "Nama Pengirim",
// // onChange: (String searchResult) {},
// isPrefix: false,
// ),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 20),
),
// jenis
Text(
'Jenis',
style: Constant.body1(context: context).copyWith(
fontWeight: FontWeight.w600, color: Constant.textBlack),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 10),
),
SizedBox(
width: Constant.getActualXPhone(context: context, x: 340),
height: Constant.getActualYPhone(context: context, y: 36),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
if (radioButtonItems.value.isEmpty)
Text('Radio Button Empty')
else
for (var i = 0;
i < radioButtonItems.value.length;
i++)
Padding(
padding: const EdgeInsets.only(right: 20),
child: Row(
children: [
Radio<DummyRadioTipe>(
activeColor: Constant.confirmed,
value: radioButtonItems.value[i],
groupValue: selectedRadio.value,
onChanged: (DummyRadioTipe? index) {
if (isMounted()) {
selectedRadio.value = index!;
} else {
return;
}
},
),
Text(
radioButtonItems.value[i].text,
style: Constant.body1(context: context)
.copyWith(
color: Constant.textBlack,
fontWeight: FontWeight.w400,
),
),
],
),
),
],
),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 20),
),
// jumlah
Text(
'Jumlah',
style: Constant.body1(context: context).copyWith(
fontWeight: FontWeight.w600, color: Constant.textBlack),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 20),
),
TextField(
controller: ctrlJumlah,
keyboardType: TextInputType.number,
decoration: InputDecoration(
hintStyle: Constant.body2_400(context: context).copyWith(
color: Colors.orange,
),
labelStyle: Constant.body2_400(context: context).copyWith(
color: Colors.orange,
),
border: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.orange,
width: 1,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.orange,
width: 1,
),
),
// labelText: "Jumlah",
// hintText: 'Jumlah',
),
),
// CustomTextField(
// isNumber: true,
// isReadOnly: false,
// ctrl: ctrlJumlah,
// isPassword: false,
// isMaxLine: false,
// hintText: "Jumlah",
// labelText: "Jumlah",
// // onChange: (String searchResult) {},
// isPrefix: false,
// ),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 20),
),
// catatan
Text(
'Catatan',
style: Constant.body1(context: context).copyWith(
fontWeight: FontWeight.w600, color: Constant.textBlack),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 20),
),
TextField(
controller: ctrlCatatan,
maxLines: 4,
decoration: InputDecoration(
hintStyle: Constant.body2_400(context: context).copyWith(
color: Colors.orange,
),
labelStyle: Constant.body2_400(context: context).copyWith(
color: Colors.orange,
),
border: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.orange,
width: 1,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.orange,
width: 1,
),
),
// labelText: "Catatan",
// hintText: 'Catatan',
),
),
// CustomTextField(
// isNumber: false,
// isReadOnly: false,
// ctrl: ctrlCatatan,
// isPassword: false,
// isMaxLine: true,
// isTextArea: true,
// hintText: "Catatan",
// labelText: "Catatan",
// // onChange: (String searchResult) {},
// isPrefix: false,
// ),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 20),
),
// Upload File
Container(
width: Constant.getActualXPhone(context: context, x: 336),
height: Constant.getActualYPhone(context: context, y: 83),
decoration: BoxDecoration(color: Constant.bgUploadFile),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(
Icons.upload_outlined,
),
Text(
'Upload File',
style: Constant.body1(context: context).copyWith(
fontWeight: FontWeight.w600,
color: Constant.textBlack),
)
],
),
),
],
),
),
),
),
bottomNavigationBar: Padding(
padding: EdgeInsets.only(
right: Constant.getActualXPhone(context: context, x: 27),
left: Constant.getActualXPhone(context: context, x: 27),
bottom: Constant.getActualYPhone(context: context, y: 32),
top: Constant.getActualYPhone(context: context, y: 10),
),
child: BottomAppBar(
child: Container(
width: Constant.getActualXPhone(context: context, x: 336),
height: Constant.getActualYPhone(context: context, y: 42),
child: ElevatedButton(
child: Text(
'Simpan',
style: Constant.body1(context: context).copyWith(
fontWeight: FontWeight.w600, color: Constant.textBlack),
),
onPressed: () {},
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 20),
),
// jumlah
Text(
'Jumlah',
style: Constant.body1(context: context).copyWith(
fontWeight: FontWeight.w600, color: Constant.textBlack),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 20),
),
CustomTextField(
isNumber: true,
isReadOnly: false,
ctrl: ctrlJumlah,
isPassword: false,
isMaxLine: false,
hintText: "Jumlah",
labelText: "Jumlah",
// onChange: (String searchResult) {},
isPrefix: false,
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 20),
),
// catatan
Text(
'Catatan',
style: Constant.body1(context: context).copyWith(
fontWeight: FontWeight.w600, color: Constant.textBlack),
),
SizedBox(
height: Constant.getActualYPhone(context: context, y: 20),
),
CustomTextField(
isNumber: false,
isReadOnly: false,
ctrl: ctrlCatatan,
isPassword: false,
isMaxLine: true,
isTextArea: true,
hintText: "Catatan",
labelText: "Catatan",
// onChange: (String searchResult) {},
isPrefix: false,
),
],
),
),
),
),

View File

@@ -0,0 +1,29 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import '../../app/constant.dart';
import '../../widget/custom_drawer.dart';
class UserScreen extends HookConsumerWidget {
const UserScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return Padding(
padding: EdgeInsets.only(
top: Constant.getActualYPhone(context: context, y: 30),
),
child: Scaffold(
appBar: AppBar(
title: Text('User'),
),
drawer: CustomDrawer(),
body: SafeArea(
child: Center(
child: Text('Under Construction'),
),
),
),
);
}
}

View File

@@ -0,0 +1,67 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import '../app/constant.dart';
import '../app/route.dart';
class CustomDrawer extends HookConsumerWidget {
const CustomDrawer({
super.key,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
return Drawer(
child: ListView(
padding: EdgeInsets.only(
top: Constant.getActualYPhone(context: context, y: 10),
),
children: [
// DrawerHeader(
// decoration: BoxDecoration(
// color: Colors.blue,
// ),
// child: Text(
// 'Drawer Header',
// style: TextStyle(
// color: Colors.white,
// fontSize: 24,
// ),
// ),
// ),
ListTile(
title: Text('Home'),
onTap: () {
// Handle navigation to Home screen
Navigator.pop(context);
Navigator.pushNamed(context, homeRoute);
},
),
ListTile(
title: Text('Transaksi'),
onTap: () {
// Handle navigation to Transaksi screen
Navigator.pop(context);
Navigator.pushNamed(context, transaksiRoute);
},
),
ListTile(
title: Text('User'),
onTap: () {
// Handle navigation to User screen
Navigator.pop(context);
Navigator.pushNamed(context, userRoute);
},
),
ListTile(
title: Text('Logout'),
onTap: () {
// Handle logout logic
Navigator.pop(context);
},
),
],
),
);
}
}

View File

@@ -0,0 +1,50 @@
import 'package:flutter/material.dart';
import 'package:top_snackbar_flutter/custom_snack_bar.dart';
import 'package:top_snackbar_flutter/top_snack_bar.dart';
enum snackbarType { error, info, success, warning }
SanckbarWidget(BuildContext context, String msg, snackbarType tipe) {
switch (tipe) {
case snackbarType.error:
return showTopSnackBar(
Overlay.of(context),
CustomSnackBar.error(
message: msg,
),
);
break;
case snackbarType.success:
return showTopSnackBar(
Overlay.of(context),
CustomSnackBar.success(
message: msg,
),
);
break;
case snackbarType.info:
return showTopSnackBar(
Overlay.of(context),
CustomSnackBar.info(
message: msg,
),
);
break;
case snackbarType.warning:
return showTopSnackBar(
Overlay.of(context),
CustomSnackBar.info(
backgroundColor: Colors.orangeAccent,
message: msg,
),
);
break;
default:
return showTopSnackBar(
Overlay.of(context),
CustomSnackBar.info(
message: msg,
),
);
}
}

View File

@@ -9,6 +9,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.11.0"
base58check:
dependency: transitive
description:
name: base58check
sha256: "6c300dfc33e598d2fe26319e13f6243fea81eaf8204cb4c6b69ef20a625319a5"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
bech32:
dependency: transitive
description:
name: bech32
sha256: "156cbace936f7720c79a79d16a03efad343b1ef17106716e04b8b8e39f99f7f7"
url: "https://pub.dev"
source: hosted
version: "0.2.2"
boolean_selector:
dependency: transitive
description:
@@ -41,6 +57,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.17.1"
convert:
dependency: transitive
description:
name: convert
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
crypto:
dependency: transitive
description:
name: crypto
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
url: "https://pub.dev"
source: hosted
version: "3.0.3"
cupertino_icons:
dependency: "direct main"
description:
@@ -134,6 +166,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.3"
flutter_multi_formatter:
dependency: "direct main"
description:
name: flutter_multi_formatter
sha256: "4798b3ee8a3c00271565e77dc8886f02b52354c3ac0d845e07d1715d159959f6"
url: "https://pub.dev"
source: hosted
version: "2.12.4"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
@@ -445,6 +485,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.5.1"
top_snackbar_flutter:
dependency: "direct main"
description:
name: top_snackbar_flutter
sha256: "22d14664a13db6ac714934c3382bd8d4daa57fb888a672f922df71981c5a5cb2"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
typed_data:
dependency: transitive
description:

View File

@@ -47,6 +47,8 @@ dependencies:
dropdown_button2: ^2.1.3
file_picker: ^6.1.1
open_file: ^3.3.2
flutter_multi_formatter: ^2.12.4
top_snackbar_flutter: ^3.1.0
dev_dependencies:
flutter_test: