Compare commits
114 Commits
Author | SHA1 | Date | |
---|---|---|---|
848891e1f4 | |||
4f2380fcdf | |||
c23075304a | |||
17c7559158 | |||
711e5f3f52 | |||
a99365fb0a | |||
41f0bb7a68 | |||
c97130239d | |||
1b867227c7 | |||
4b07219928 | |||
9cdda42b8b | |||
6f5d3ccca8 | |||
ff515e2621 | |||
f057a346c2 | |||
f84fe1017d | |||
27ba55314b | |||
59e046bd92 | |||
4461b78565 | |||
f2f688f9f3 | |||
d1adfd2ab0 | |||
33b2ab85e3 | |||
6a43a3dcaf | |||
db1280b272 | |||
ae4f9c25c4 | |||
a87afe16cb | |||
1904e9a4a3 | |||
d79959c47f | |||
38837bd4f8 | |||
d966108e9e | |||
506f364b87 | |||
506480d812 | |||
a6ea9a2314 | |||
176261923d | |||
a3148d8210 | |||
6e03fa5fa7 | |||
de1782c2c2 | |||
89a79276a6 | |||
4737a91ab1 | |||
ff135dd47b | |||
2e98c1a234 | |||
c4c0479341 | |||
e510aaefd7 | |||
dfb947dce5 | |||
9761dc369c | |||
9e2304990c | |||
8b1b3e950f | |||
aa987fe320 | |||
ad6195061d | |||
b5a382ce96 | |||
fdca27233b | |||
bd065242e6 | |||
a35110af71 | |||
83211e76f9 | |||
99db140a0c | |||
57a4e828c9 | |||
466d49312d | |||
9ba8b79112 | |||
4f50dc951a | |||
d66a9e3435 | |||
8e04b4e77e | |||
afc2bd3cc9 | |||
0762a8ab0c | |||
66ace5b217 | |||
5cb76fca7f | |||
e86e67b9c9 | |||
a6248520ef | |||
eb1eb83d52 | |||
7706fe4387 | |||
9da1675250 | |||
298d7f46d2 | |||
368f326123 | |||
a574f30424 | |||
3c1f7e210a | |||
b3a68b4436 | |||
ce2bf8a777 | |||
7e9c109fa2 | |||
8537045d74 | |||
23d189c288 | |||
4bad9cd18c | |||
3dca727a5e | |||
4c1cc7422b | |||
219339f577 | |||
0c2441091f | |||
d86820ec98 | |||
ececa5e541 | |||
b0de8c255e | |||
6977c8166d | |||
7a1cddee03 | |||
db6e4d543d | |||
d672a23564 | |||
5e97154100 | |||
59e6e82d13 | |||
477eb5d2b1 | |||
370db229de | |||
9475767021 | |||
0a347f5e6d | |||
4d58a7dced | |||
81231505b1 | |||
6e2516d9c8 | |||
e538fa5927 | |||
7ec266cded | |||
96c676ac4c | |||
e1cabe0a09 | |||
4b4b42beae | |||
dacf5461f3 | |||
5d4bc47adf | |||
80e4657240 | |||
d82d427bcc | |||
e63e5588fb | |||
f407eca735 | |||
ff1886cec1 | |||
1616f22925 | |||
0b754bf939 | |||
0e7ad81345 |
|
@ -12,6 +12,12 @@ if (localPropertiesFile.exists()) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def keystoreProperties = new Properties()
|
||||||
|
def keystorePropertiesFile = rootProject.file('key.properties')
|
||||||
|
if (keystorePropertiesFile.exists()) {
|
||||||
|
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
|
||||||
|
}
|
||||||
|
|
||||||
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||||
if (flutterVersionCode == null) {
|
if (flutterVersionCode == null) {
|
||||||
flutterVersionCode = '1'
|
flutterVersionCode = '1'
|
||||||
|
@ -45,17 +51,27 @@ android {
|
||||||
applicationId "com.example.cims_apps"
|
applicationId "com.example.cims_apps"
|
||||||
// You can update the following values to match your application needs.
|
// You can update the following values to match your application needs.
|
||||||
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
||||||
minSdkVersion flutter.minSdkVersion
|
minSdkVersion 21
|
||||||
targetSdkVersion flutter.targetSdkVersion
|
targetSdkVersion flutter.targetSdkVersion
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signingConfigs {
|
||||||
|
release {
|
||||||
|
keyAlias keystoreProperties['keyAlias']
|
||||||
|
keyPassword keystoreProperties['keyPassword']
|
||||||
|
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
|
||||||
|
storePassword keystoreProperties['storePassword']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
// TODO: Add your own signing config for the release build.
|
// TODO: Add your own signing config for the release build.
|
||||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||||
signingConfig signingConfigs.debug
|
// signingConfig signingConfigs.debug
|
||||||
|
signingConfig signingConfigs.release
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<application
|
<application
|
||||||
android:label="cims_apps"
|
android:label="cims investment"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:enableOnBackInvokedCallback="true"
|
android:enableOnBackInvokedCallback="true"
|
||||||
|
|
BIN
assets/icons/icon-bag.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
assets/icons/icon-balance.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
assets/icons/icon-cake.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/icons/icon-car.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
assets/icons/icon-card.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
assets/icons/icon-cart.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/icons/icon-ceklis-outline.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
assets/icons/icon-chat.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
assets/icons/icon-coins.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
assets/icons/icon-create-plan.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/icons/icon-education.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
assets/icons/icon-flag.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
assets/icons/icon-fund.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
assets/icons/icon-gadget-outline.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
assets/icons/icon-gadget.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/icons/icon-home.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
assets/icons/icon-house.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/icons/icon-ktp1.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
assets/icons/icon-ktp2.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
assets/icons/icon-ktp3.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
assets/icons/icon-ktp4.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
assets/icons/icon-lock.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
assets/icons/icon-logout.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
assets/icons/icon-market.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/icons/icon-money-receive.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
assets/icons/icon-navigation-home.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
assets/icons/icon-navigation-plan.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/icons/icon-navigation-portfolio.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
assets/icons/icon-navigation-profile.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
assets/icons/icon-navigation-transaction.png
Normal file
After Width: | Height: | Size: 899 B |
BIN
assets/icons/icon-plane.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
assets/icons/icon-portofolio-bonds.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
assets/icons/icon-portofolio-moneymarket.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
assets/icons/icon-portofolio-shares.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
assets/icons/icon-portofolio-sharia.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
assets/icons/icon-portofolio.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/icons/icon-profile.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
assets/icons/icon-question.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
assets/icons/icon-remove.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
assets/icons/icon-selfie1.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
assets/icons/icon-selfie2.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
assets/icons/icon-selfie3.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
assets/icons/icon-selfie4.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
assets/icons/icon-setting.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
assets/icons/icon-shield.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/icons/icon-shop.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
assets/icons/icon-strongbox.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
assets/icons/icon-thumb.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/icons/icon-ticket.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/icons/icon-toga.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
assets/images/bg-profile.png
Normal file
After Width: | Height: | Size: 4.0 MiB |
BIN
assets/images/frame-signature.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
assets/images/img-articles.png
Normal file
After Width: | Height: | Size: 154 KiB |
BIN
assets/images/img-bg-photo-ktp.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
assets/images/img-bg-photo-selfie.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
assets/images/img-business-failure.png
Normal file
After Width: | Height: | Size: 122 KiB |
BIN
assets/images/img-carousel.png
Normal file
After Width: | Height: | Size: 636 KiB |
BIN
assets/images/img-cat-outlined.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
assets/images/img-cat.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
assets/images/img-dashboard-account.png
Normal file
After Width: | Height: | Size: 1.3 MiB |
BIN
assets/images/img-data-analysis.png
Normal file
After Width: | Height: | Size: 66 KiB |
BIN
assets/images/img-data-report.png
Normal file
After Width: | Height: | Size: 60 KiB |
BIN
assets/images/img-deer.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
assets/images/img-empty-transaction.png
Normal file
After Width: | Height: | Size: 70 KiB |
BIN
assets/images/img-expand-purchase.png
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
assets/images/img-finish.png
Normal file
After Width: | Height: | Size: 105 KiB |
BIN
assets/images/img-growing.png
Normal file
After Width: | Height: | Size: 74 KiB |
BIN
assets/images/img-guide-bank.png
Normal file
After Width: | Height: | Size: 53 KiB |
BIN
assets/images/img-guide1.png
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
assets/images/img-guide2.png
Normal file
After Width: | Height: | Size: 119 KiB |
BIN
assets/images/img-leader.png
Normal file
After Width: | Height: | Size: 93 KiB |
BIN
assets/images/img-lion.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
assets/images/img-money-income.png
Normal file
After Width: | Height: | Size: 60 KiB |
BIN
assets/images/img-open-shopping.png
Normal file
After Width: | Height: | Size: 101 KiB |
BIN
assets/images/img-payment-success.png
Normal file
After Width: | Height: | Size: 74 KiB |
BIN
assets/images/img-product.png
Normal file
After Width: | Height: | Size: 4.5 KiB |
BIN
assets/images/img-success-signup.png
Normal file
After Width: | Height: | Size: 88 KiB |
|
@ -1,7 +1,7 @@
|
||||||
class PathAssets {
|
class PathAssets {
|
||||||
PathAssets._();
|
PathAssets._();
|
||||||
|
|
||||||
/// LOGO
|
/// ICON
|
||||||
static const String iconSplashRight = 'assets/icons/splash-right.png';
|
static const String iconSplashRight = 'assets/icons/splash-right.png';
|
||||||
static const String iconSplashLeft = 'assets/icons/splash-left.png';
|
static const String iconSplashLeft = 'assets/icons/splash-left.png';
|
||||||
static const String iconReksadana = 'assets/icons/icon-reksadana.png';
|
static const String iconReksadana = 'assets/icons/icon-reksadana.png';
|
||||||
|
@ -10,6 +10,67 @@ class PathAssets {
|
||||||
static const String iconGoogle = 'assets/icons/icon-google.png';
|
static const String iconGoogle = 'assets/icons/icon-google.png';
|
||||||
static const String icon1 = 'assets/icons/icon-1.png';
|
static const String icon1 = 'assets/icons/icon-1.png';
|
||||||
static const String iconConnect = 'assets/icons/icon-connect.png';
|
static const String iconConnect = 'assets/icons/icon-connect.png';
|
||||||
|
static const String iconPortofolioBonds =
|
||||||
|
'assets/icons/icon-portofolio-bonds.png';
|
||||||
|
static const String iconPortofolioShares =
|
||||||
|
'assets/icons/icon-portofolio-shares.png';
|
||||||
|
static const String iconPortofolioSharia =
|
||||||
|
'assets/icons/icon-portofolio-sharia.png';
|
||||||
|
static const String iconPortofolioMoneyMarket =
|
||||||
|
'assets/icons/icon-portofolio-moneymarket.png';
|
||||||
|
static const String iconShield = 'assets/icons/icon-shield.png';
|
||||||
|
static const String iconFlag = 'assets/icons/icon-flag.png';
|
||||||
|
static const String iconKtp1 = 'assets/icons/icon-ktp1.png';
|
||||||
|
static const String iconKtp2 = 'assets/icons/icon-ktp2.png';
|
||||||
|
static const String iconKtp3 = 'assets/icons/icon-ktp3.png';
|
||||||
|
static const String iconKtp4 = 'assets/icons/icon-ktp4.png';
|
||||||
|
static const String iconSelfie1 = 'assets/icons/icon-selfie1.png';
|
||||||
|
static const String iconSelfie2 = 'assets/icons/icon-selfie2.png';
|
||||||
|
static const String iconSelfie3 = 'assets/icons/icon-selfie3.png';
|
||||||
|
static const String iconSelfie4 = 'assets/icons/icon-selfie4.png';
|
||||||
|
static const String iconStrongBox = 'assets/icons/icon-strongbox.png';
|
||||||
|
static const String iconBalance = 'assets/icons/icon-balance.png';
|
||||||
|
static const String iconMoneyReceive = 'assets/icons/icon-money-receive.png';
|
||||||
|
static const String iconCoins = 'assets/icons/icon-coins.png';
|
||||||
|
static const String iconQuestion = 'assets/icons/icon-question.png';
|
||||||
|
static const String iconCake = 'assets/icons/icon-cake.png';
|
||||||
|
static const String iconHouse = 'assets/icons/icon-house.png';
|
||||||
|
static const String iconToga = 'assets/icons/icon-toga.png';
|
||||||
|
static const String iconCreatePlan = 'assets/icons/icon-create-plan.png';
|
||||||
|
static const String iconChecklistOutlined =
|
||||||
|
'assets/icons/icon-ceklis-outline.png';
|
||||||
|
static const String iconLock = 'assets/icons/icon-lock.png';
|
||||||
|
static const String iconThumb = 'assets/icons/icon-thumb.png';
|
||||||
|
static const String iconPortofolio = 'assets/icons/icon-portofolio.png';
|
||||||
|
static const String iconPlane = 'assets/icons/icon-plane.png';
|
||||||
|
static const String iconCart = 'assets/icons/icon-cart.png';
|
||||||
|
static const String iconBag = 'assets/icons/icon-bag.png';
|
||||||
|
static const String iconMarket = 'assets/icons/icon-market.png';
|
||||||
|
static const String iconTicket = 'assets/icons/icon-ticket.png';
|
||||||
|
static const String iconGadget = 'assets/icons/icon-gadget.png';
|
||||||
|
static const String iconCar = 'assets/icons/icon-car.png';
|
||||||
|
static const String iconNavigationHome =
|
||||||
|
'assets/icons/icon-navigation-home.png';
|
||||||
|
static const String iconNavigationPlan =
|
||||||
|
'assets/icons/icon-navigation-plan.png';
|
||||||
|
static const String iconNavigationTransaction =
|
||||||
|
'assets/icons/icon-navigation-transaction.png';
|
||||||
|
static const String iconNavigationPortfolio =
|
||||||
|
'assets/icons/icon-navigation-portfolio.png';
|
||||||
|
static const String iconNavigationProfile =
|
||||||
|
'assets/icons/icon-navigation-profile.png';
|
||||||
|
static const String iconRemove = 'assets/icons/icon-remove.png';
|
||||||
|
static const String iconEducation = 'assets/icons/icon-education.png';
|
||||||
|
static const String iconFund = 'assets/icons/icon-fund.png';
|
||||||
|
static const String iconHome = 'assets/icons/icon-home.png';
|
||||||
|
static const String iconShop = 'assets/icons/icon-shop.png';
|
||||||
|
static const String iconCard = 'assets/icons/icon-card.png';
|
||||||
|
static const String iconChat = 'assets/icons/icon-chat.png';
|
||||||
|
static const String iconLogout = 'assets/icons/icon-logout.png';
|
||||||
|
static const String iconProfile = 'assets/icons/icon-profile.png';
|
||||||
|
static const String iconSetting = 'assets/icons/icon-setting.png';
|
||||||
|
static const String iconGadgetOutline =
|
||||||
|
'assets/icons/icon-gadget-outline.png';
|
||||||
|
|
||||||
/// IMAGE
|
/// IMAGE
|
||||||
static const String imgSplashLogo = 'assets/images/splash-logo.png';
|
static const String imgSplashLogo = 'assets/images/splash-logo.png';
|
||||||
|
@ -22,4 +83,52 @@ class PathAssets {
|
||||||
static const String imgKtpCropped = 'assets/images/img-ktp-cropped.png';
|
static const String imgKtpCropped = 'assets/images/img-ktp-cropped.png';
|
||||||
static const String imgKtpClear = 'assets/images/img-ktp-clear.png';
|
static const String imgKtpClear = 'assets/images/img-ktp-clear.png';
|
||||||
static const String imgKtpBlur = 'assets/images/img-ktp-blur.png';
|
static const String imgKtpBlur = 'assets/images/img-ktp-blur.png';
|
||||||
|
static const String imgDashboardAccount =
|
||||||
|
'assets/images/img-dashboard-account.png';
|
||||||
|
static const String imgCarousel = 'assets/images/img-carousel.png';
|
||||||
|
static const String imgArticles = 'assets/images/img-articles.png';
|
||||||
|
static const String imgProduct = 'assets/images/img-product.png';
|
||||||
|
static const String imgSuccessSignup = 'assets/images/img-success-signup.png';
|
||||||
|
static const String imgBgKtp = 'assets/images/img-bg-photo-ktp.png';
|
||||||
|
static const String imgBgSelfie = 'assets/images/img-bg-photo-selfie.png';
|
||||||
|
static const String imgDataReport = 'assets/images/img-data-report.png';
|
||||||
|
static const String imgDataAnalysis = 'assets/images/img-data-analysis.png';
|
||||||
|
static const String imgBusinessFailure =
|
||||||
|
'assets/images/img-business-failure.png';
|
||||||
|
static const String imgLeader = 'assets/images/img-leader.png';
|
||||||
|
static const String imgMoneyIncome = 'assets/images/img-money-income.png';
|
||||||
|
static const String imgGrowing = 'assets/images/img-growing.png';
|
||||||
|
static const String imgCat = 'assets/images/img-cat.png';
|
||||||
|
static const String imgCatOutlined = 'assets/images/img-cat-outlined.png';
|
||||||
|
static const String imgDeer = 'assets/images/img-deer.png';
|
||||||
|
static const String imgLion = 'assets/images/img-lion.png';
|
||||||
|
static const String imgGuideBank = 'assets/images/img-guide-bank.png';
|
||||||
|
static const String imgGuide1 = 'assets/images/img-guide1.png';
|
||||||
|
static const String imgGuide2 = 'assets/images/img-guide2.png';
|
||||||
|
static const String imgOpenShopping = 'assets/images/img-open-shopping.png';
|
||||||
|
static const String imgPaymentSuccess =
|
||||||
|
'assets/images/img-payment-success.png';
|
||||||
|
static const String frameSignature = 'assets/images/frame-signature.png';
|
||||||
|
static const String imgFinish = 'assets/images/img-finish.png';
|
||||||
|
static const String imgExpandPurchase =
|
||||||
|
'assets/images/img-expand-purchase.png';
|
||||||
|
static const String imgEmptyTransaction =
|
||||||
|
'assets/images/img-empty-transaction.png';
|
||||||
|
static const String bgProfile = 'assets/images/bg-profile.png';
|
||||||
|
|
||||||
|
static const Map<String, String> goalInvestIcon = {
|
||||||
|
'Education': iconToga,
|
||||||
|
'Marriage': iconCake,
|
||||||
|
'Old age days': iconHouse,
|
||||||
|
'Home': iconHouse,
|
||||||
|
'Other Plan': iconCreatePlan,
|
||||||
|
'Create Plan': iconCreatePlan,
|
||||||
|
'Entertainment': iconTicket,
|
||||||
|
'Gadget': iconGadget,
|
||||||
|
'Business': iconMarket,
|
||||||
|
'Fashion': iconBag,
|
||||||
|
'Shop': iconBag,
|
||||||
|
'Vehicle': iconCar,
|
||||||
|
'Holiday': iconPlane,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
27
lib/application/component/button/back_button_view.dart
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import 'package:cims_apps/application/theme/color_palette.dart';
|
||||||
|
import 'package:cims_apps/core/utils/size_config.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class BackButtonView extends StatelessWidget {
|
||||||
|
final EdgeInsets? margin;
|
||||||
|
final void Function()? onPress;
|
||||||
|
const BackButtonView({super.key, this.margin, this.onPress});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
margin: margin ?? EdgeInsets.all(0),
|
||||||
|
width: SizeConfig.width * 0.1,
|
||||||
|
child: IconButton(
|
||||||
|
style: IconButton.styleFrom(
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
shape: const CircleBorder(
|
||||||
|
side: BorderSide(color: ColorPalette.slate200)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
onPressed: onPress ?? () => Navigator.pop(context),
|
||||||
|
icon: const Icon(Icons.arrow_back)
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,13 +9,14 @@ class ButtonView extends StatelessWidget {
|
||||||
final double? height, width, widthSuffix, widthPrefix, marginVertical;
|
final double? height, width, widthSuffix, widthPrefix, marginVertical;
|
||||||
final EdgeInsetsGeometry? contentPadding;
|
final EdgeInsetsGeometry? contentPadding;
|
||||||
final bool isSecondaryColor, isOutlined, heightWrapContent, disabled;
|
final bool isSecondaryColor, isOutlined, heightWrapContent, disabled;
|
||||||
final Color? backgroundColor, textColor;
|
final Color? backgroundColor, textColor, borderColor, disabledBgColor;
|
||||||
final MainAxisAlignment? mainAxisAlignmentContent;
|
final MainAxisAlignment? mainAxisAlignmentContent;
|
||||||
// final _widthBtn = SizeConfig.screenWidth / 1.5;
|
// final _widthBtn = SizeConfig.screenWidth / 1.5;
|
||||||
final _widthBtn = SizeConfig.width * .9;
|
final _widthBtn = SizeConfig.width * .9;
|
||||||
// final _heightBtn = SizeConfig.screenHeight / 12;
|
// final _heightBtn = SizeConfig.screenHeight / 12;
|
||||||
final _heightBtn = SizeConfig.height * .07;
|
final _heightBtn = SizeConfig.height * .07;
|
||||||
final FontWeight textWeight;
|
final FontWeight textWeight;
|
||||||
|
final TextAlign textAlign;
|
||||||
final double? textSize, sizeBorderRadius;
|
final double? textSize, sizeBorderRadius;
|
||||||
final int? maxLines;
|
final int? maxLines;
|
||||||
|
|
||||||
|
@ -31,9 +32,12 @@ class ButtonView extends StatelessWidget {
|
||||||
this.width,
|
this.width,
|
||||||
this.contentPadding,
|
this.contentPadding,
|
||||||
this.backgroundColor,
|
this.backgroundColor,
|
||||||
|
this.borderColor,
|
||||||
this.textColor,
|
this.textColor,
|
||||||
|
this.disabledBgColor,
|
||||||
this.textWeight = FontWeight.bold,
|
this.textWeight = FontWeight.bold,
|
||||||
this.textSize,
|
this.textSize,
|
||||||
|
this.textAlign = TextAlign.center,
|
||||||
this.mainAxisAlignmentContent,
|
this.mainAxisAlignmentContent,
|
||||||
this.disabled = false,
|
this.disabled = false,
|
||||||
this.heightWrapContent = false,
|
this.heightWrapContent = false,
|
||||||
|
@ -55,78 +59,80 @@ class ButtonView extends StatelessWidget {
|
||||||
final widthPrefix =
|
final widthPrefix =
|
||||||
this.widthPrefix ?? (heightWrapContent ? width! / 4.7 : _widthBtn / 16);
|
this.widthPrefix ?? (heightWrapContent ? width! / 4.7 : _widthBtn / 16);
|
||||||
|
|
||||||
return Container(
|
return Center(
|
||||||
margin: EdgeInsets.symmetric(vertical: marginVertical ?? 32.0),
|
child: Container(
|
||||||
width: width ?? _widthBtn,
|
margin: EdgeInsets.symmetric(vertical: marginVertical ?? 24.0),
|
||||||
height: heightWrapContent ? null : height ?? _heightBtn,
|
width: width ?? _widthBtn,
|
||||||
child: ElevatedButton(
|
height: heightWrapContent ? null : height ?? _heightBtn,
|
||||||
style: ElevatedButton.styleFrom(
|
child: ElevatedButton(
|
||||||
disabledBackgroundColor: isOutlined ? Colors.white : color.surface,
|
style: ElevatedButton.styleFrom(
|
||||||
padding: contentPadding,
|
disabledBackgroundColor: disabledBgColor ?? (isOutlined ? Colors.white : color.surface),
|
||||||
backgroundColor: backgroundColor ??
|
padding: contentPadding,
|
||||||
(isOutlined
|
backgroundColor: backgroundColor ??
|
||||||
? Colors.white
|
(isOutlined
|
||||||
: isSecondaryColor
|
? Colors.white
|
||||||
? ColorPalette.grey
|
: isSecondaryColor
|
||||||
: ColorPalette.primary),
|
? ColorPalette.grey
|
||||||
elevation: 0,
|
: ColorPalette.primary),
|
||||||
shape: RoundedRectangleBorder(
|
elevation: 0,
|
||||||
borderRadius: BorderRadius.circular(sizeBorderRadius ?? 48),
|
shape: RoundedRectangleBorder(
|
||||||
side: isOutlined
|
borderRadius: BorderRadius.circular(sizeBorderRadius ?? 48),
|
||||||
? BorderSide(
|
side: isOutlined
|
||||||
color: disabled
|
? BorderSide(
|
||||||
? color.surface
|
color: borderColor ?? (disabled
|
||||||
: isSecondaryColor
|
? color.surface
|
||||||
? ColorPalette.greyBorder
|
: isSecondaryColor
|
||||||
: ColorPalette.primary,
|
? ColorPalette.greyBorder
|
||||||
)
|
: ColorPalette.primary),
|
||||||
: BorderSide.none,
|
)
|
||||||
|
: BorderSide.none,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
onPressed: disabled ? null : onPressed,
|
||||||
onPressed: disabled ? null : onPressed,
|
child: Row(
|
||||||
child: Row(
|
mainAxisAlignment: mainAxisAlignmentContent ??
|
||||||
mainAxisAlignment: mainAxisAlignmentContent ??
|
(prefixIcon != null
|
||||||
(prefixIcon != null
|
? MainAxisAlignment.center
|
||||||
? MainAxisAlignment.center
|
: suffixIcon != null
|
||||||
: suffixIcon != null
|
? MainAxisAlignment.end
|
||||||
? MainAxisAlignment.end
|
: MainAxisAlignment.center),
|
||||||
: MainAxisAlignment.center),
|
children: [
|
||||||
children: [
|
if (prefixIcon != null) ...[
|
||||||
if (prefixIcon != null) ...[
|
prefixIcon!,
|
||||||
prefixIcon!,
|
SizedBox(width: widthPrefix),
|
||||||
SizedBox(width: widthPrefix),
|
] else
|
||||||
] else
|
Container(),
|
||||||
Container(),
|
Flexible(
|
||||||
Flexible(
|
child: Text(
|
||||||
child: Text(
|
name,
|
||||||
name,
|
textAlign: textAlign,
|
||||||
textAlign: TextAlign.center,
|
maxLines: maxLines,
|
||||||
maxLines: maxLines,
|
overflow: TextOverflow.ellipsis,
|
||||||
overflow: TextOverflow.ellipsis,
|
style: TextStyle(
|
||||||
style: TextStyle(
|
fontSize: textSize ?? 16,
|
||||||
fontSize: textSize ?? 16,
|
fontWeight: textWeight,
|
||||||
fontWeight: textWeight,
|
color: textColor ??
|
||||||
color: textColor ??
|
(disabled && isOutlined
|
||||||
(disabled && isOutlined
|
? color.primary
|
||||||
? color.primary
|
: disabled
|
||||||
: disabled
|
? Colors.white
|
||||||
? Colors.white
|
: isOutlined && isSecondaryColor
|
||||||
: isOutlined && isSecondaryColor
|
? ColorPalette.blackFont
|
||||||
? ColorPalette.blackFont
|
: isOutlined
|
||||||
: isOutlined
|
? color.primary
|
||||||
? color.primary
|
: isSecondaryColor
|
||||||
: isSecondaryColor
|
? Colors.white
|
||||||
? Colors.white
|
: Colors.white),
|
||||||
: Colors.white),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
if (suffixIcon != null) ...[
|
||||||
if (suffixIcon != null) ...[
|
SizedBox(width: widthSuffix),
|
||||||
SizedBox(width: widthSuffix),
|
suffixIcon!
|
||||||
suffixIcon!
|
] else
|
||||||
] else
|
Container()
|
||||||
Container()
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
import 'package:cims_apps/application/component/image/image_view.dart';
|
||||||
|
import 'package:cims_apps/application/theme/color_palette.dart';
|
||||||
|
import 'package:cims_apps/core/utils/size_config.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class CardTransactionView extends StatelessWidget {
|
||||||
|
final VoidCallback onTap;
|
||||||
|
final String iconPath, type, amount, subs, step;
|
||||||
|
final String? timeTransaction;
|
||||||
|
const CardTransactionView({
|
||||||
|
Key? key,
|
||||||
|
required this.step,
|
||||||
|
required this.type,
|
||||||
|
required this.amount,
|
||||||
|
required this.iconPath,
|
||||||
|
required this.subs,
|
||||||
|
required this.onTap,
|
||||||
|
this.timeTransaction,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
TextTheme textTheme = Theme.of(context).textTheme;
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: onTap,
|
||||||
|
child: Container(
|
||||||
|
margin: const EdgeInsets.symmetric(vertical: 16.0),
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 16.0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
border: Border.all(width: 1, color: ColorPalette.slate200),
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: SizeConfig.width * .4,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
ImageView(
|
||||||
|
image: iconPath, width: SizeConfig.width * .12),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 8.0),
|
||||||
|
child: Text(
|
||||||
|
type,
|
||||||
|
style: textTheme.headlineSmall,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 16.0),
|
||||||
|
child: Text(
|
||||||
|
amount,
|
||||||
|
style: textTheme.headlineSmall,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
SizedBox(
|
||||||
|
width: SizeConfig.width * .4,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: SizeConfig.height * .08,
|
||||||
|
child: Text(
|
||||||
|
subs,
|
||||||
|
style: const TextStyle(color: ColorPalette.primary),
|
||||||
|
)),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
step == 'waiting'
|
||||||
|
? const Icon(Icons.access_time_sharp,
|
||||||
|
color: ColorPalette.slate400)
|
||||||
|
: const SizedBox(),
|
||||||
|
step == 'waiting'
|
||||||
|
? Text(timeTransaction.toString())
|
||||||
|
: const SizedBox(),
|
||||||
|
const Padding(
|
||||||
|
padding: EdgeInsets.only(left: 16.0),
|
||||||
|
child: Icon(Icons.arrow_forward_ios),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
import 'package:cims_apps/application/assets/path_assets.dart';
|
||||||
|
import 'package:cims_apps/application/component/button/button_view.dart';
|
||||||
|
import 'package:cims_apps/application/component/image/image_view.dart';
|
||||||
|
import 'package:cims_apps/core/utils/size_config.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class EmptyCardTransaction extends StatelessWidget {
|
||||||
|
final VoidCallback onPressedButton;
|
||||||
|
const EmptyCardTransaction({Key? key, required this.onPressedButton})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
TextTheme textTheme = Theme.of(context).textTheme;
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||||
|
child: Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
children: [
|
||||||
|
ImageView(
|
||||||
|
image: PathAssets.imgEmptyTransaction,
|
||||||
|
width: SizeConfig.width * .4,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'No Transaction Yet',
|
||||||
|
style: textTheme.headlineSmall,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"Let's keep building your investment for even greater financial growth!",
|
||||||
|
style: textTheme.bodyMedium,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
ButtonView(
|
||||||
|
name: 'Investing Now',
|
||||||
|
width: SizeConfig.width * .5,
|
||||||
|
onPressed: onPressedButton,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
42
lib/application/component/custom_app_bar/custom_app_bar.dart
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import 'package:cims_apps/application/component/button/back_button_view.dart';
|
||||||
|
import 'package:cims_apps/application/theme/color_palette.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||||
|
final Widget? leading;
|
||||||
|
final String title;
|
||||||
|
final List<Widget>? trailing;
|
||||||
|
final double height;
|
||||||
|
|
||||||
|
const CustomAppBar({
|
||||||
|
Key? key,
|
||||||
|
required this.height,
|
||||||
|
required this.title,
|
||||||
|
this.trailing,
|
||||||
|
this.leading,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border(bottom: BorderSide(color: ColorPalette.slate200))
|
||||||
|
),
|
||||||
|
child: AppBar(
|
||||||
|
toolbarHeight: 70,
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
surfaceTintColor: Colors.white,
|
||||||
|
automaticallyImplyLeading: false,
|
||||||
|
leadingWidth: 40,
|
||||||
|
leading: leading ?? BackButtonView(),
|
||||||
|
title: Text(title),
|
||||||
|
centerTitle: true,
|
||||||
|
actions: trailing ?? [],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Size get preferredSize => Size.fromHeight(height);
|
||||||
|
}
|
144
lib/application/component/date_picker/date_picker_view.dart
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
import 'package:calendar_date_picker2/calendar_date_picker2.dart';
|
||||||
|
import 'package:cims_apps/application/component/button/button_view.dart';
|
||||||
|
import 'package:cims_apps/application/component/text_form/text_form_view.dart';
|
||||||
|
import 'package:cims_apps/application/theme/color_palette.dart';
|
||||||
|
import 'package:cims_apps/core/utils/size_config.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
|
class DatePickerView extends StatelessWidget {
|
||||||
|
final String name;
|
||||||
|
final TextEditingController ctrl;
|
||||||
|
final DateTime? minDate, maxDate;
|
||||||
|
final String? hintText, buttonName;
|
||||||
|
final bool isMultipleSelection, enabled;
|
||||||
|
final List<DateTime>? initialValue;
|
||||||
|
final ValueChanged<OnChangedDatePickerModel>? onChanged;
|
||||||
|
final ValueChanged<List<DateTime?>>? onFinish;
|
||||||
|
final FormFieldValidator<String>? validatorDate;
|
||||||
|
const DatePickerView(
|
||||||
|
{Key? key,
|
||||||
|
required this.name,
|
||||||
|
required this.ctrl,
|
||||||
|
this.minDate,
|
||||||
|
this.maxDate,
|
||||||
|
this.hintText,
|
||||||
|
this.buttonName,
|
||||||
|
required this.isMultipleSelection,
|
||||||
|
required this.enabled,
|
||||||
|
this.initialValue,
|
||||||
|
this.onChanged,
|
||||||
|
this.onFinish,
|
||||||
|
this.validatorDate})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
String _dateFormat(DateTime? dateTime) =>
|
||||||
|
dateTime != null ? DateFormat('dd/MM/yyyy').format(dateTime) : "";
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
List<DateTime?> initData = initialValue ?? [];
|
||||||
|
List<DateTime?> dateList = [];
|
||||||
|
|
||||||
|
String dateLabel() {
|
||||||
|
return dateList.map((e) => _dateFormat(e)).join(" - ");
|
||||||
|
}
|
||||||
|
|
||||||
|
onChangedDatePicker(List<DateTime?> value) {
|
||||||
|
if (isMultipleSelection) {
|
||||||
|
final pickerDateRange = PickerDateRange(
|
||||||
|
startDate: value[0] ?? DateTime.now(),
|
||||||
|
endDate:
|
||||||
|
value.length > 1 ? value[1] ?? DateTime.now() : DateTime.now(),
|
||||||
|
);
|
||||||
|
onChanged
|
||||||
|
?.call(OnChangedDatePickerModel(pickerDateRange: pickerDateRange));
|
||||||
|
} else {
|
||||||
|
onChanged?.call(OnChangedDatePickerModel(dateTime: value[0]));
|
||||||
|
}
|
||||||
|
dateList = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
dialogDatePicker() {
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.only(top: 16.0),
|
||||||
|
height: SizeConfig.height * .65,
|
||||||
|
child: Column(
|
||||||
|
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
CalendarDatePicker2(
|
||||||
|
value: [...initData],
|
||||||
|
config: CalendarDatePicker2Config(
|
||||||
|
centerAlignModePicker: true,
|
||||||
|
calendarType: isMultipleSelection
|
||||||
|
? CalendarDatePicker2Type.range
|
||||||
|
: CalendarDatePicker2Type.single,
|
||||||
|
customModePickerIcon: const SizedBox(),
|
||||||
|
firstDate: minDate ?? DateTime(1900),
|
||||||
|
lastDate: maxDate,
|
||||||
|
),
|
||||||
|
// initialValue: [...initData],
|
||||||
|
onValueChanged: (value) {
|
||||||
|
onChangedDatePicker(value);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
child: ButtonView(
|
||||||
|
width: SizeConfig.width,
|
||||||
|
onPressed: () {
|
||||||
|
if (dateList.isNotEmpty) {
|
||||||
|
onFinish?.call(dateList);
|
||||||
|
ctrl.text = dateLabel();
|
||||||
|
initData = dateList;
|
||||||
|
}
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
name: 'OK',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TextFormView(
|
||||||
|
name: name,
|
||||||
|
hintText: hintText,
|
||||||
|
readOnly: true,
|
||||||
|
ctrl: ctrl,
|
||||||
|
validator: validatorDate,
|
||||||
|
enabled: enabled,
|
||||||
|
onTap: () {
|
||||||
|
if (enabled) dialogDatePicker();
|
||||||
|
},
|
||||||
|
suffixIcon: const UnconstrainedBox(
|
||||||
|
child: Icon(
|
||||||
|
Icons.calendar_today_rounded,
|
||||||
|
color: ColorPalette.slate400,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OnChangedDatePickerModel {
|
||||||
|
final DateTime? dateTime;
|
||||||
|
final PickerDateRange? pickerDateRange;
|
||||||
|
|
||||||
|
OnChangedDatePickerModel({this.dateTime, this.pickerDateRange});
|
||||||
|
}
|
||||||
|
|
||||||
|
class PickerDateRange {
|
||||||
|
final DateTime startDate;
|
||||||
|
final DateTime endDate;
|
||||||
|
|
||||||
|
PickerDateRange({required this.startDate, required this.endDate});
|
||||||
|
}
|
|
@ -0,0 +1,119 @@
|
||||||
|
import 'package:cims_apps/application/component/expandable_widget/see_more_less_widget.dart';
|
||||||
|
import 'package:cims_apps/application/theme/color_palette.dart';
|
||||||
|
import 'package:cims_apps/core/utils/size_config.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ExpandableWidget extends StatelessWidget {
|
||||||
|
final String? content;
|
||||||
|
final double? fontSize;
|
||||||
|
final int maxLinesToShow;
|
||||||
|
final Alignment? alignmentMore;
|
||||||
|
final bool? hideTextMore;
|
||||||
|
final bool? hideIconMore;
|
||||||
|
|
||||||
|
ExpandableWidget({
|
||||||
|
super.key,
|
||||||
|
required this.content,
|
||||||
|
this.fontSize,
|
||||||
|
this.maxLinesToShow = 1,
|
||||||
|
this.alignmentMore,
|
||||||
|
this.hideTextMore = false,
|
||||||
|
this.hideIconMore = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
ValueNotifier<bool> expanded = ValueNotifier(false);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final TextSpan textSpan = TextSpan(
|
||||||
|
text: content ?? "",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: fontSize ?? 16.0,
|
||||||
|
color: ColorPalette.slate400,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final TextPainter textPainter = TextPainter(
|
||||||
|
text: textSpan,
|
||||||
|
maxLines: expanded.value ? null : maxLinesToShow,
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
strutStyle: StrutStyle(
|
||||||
|
fontSize: fontSize ?? 16.0,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
textPainter.layout(maxWidth: SizeConfig.width);
|
||||||
|
|
||||||
|
final int numberOfLines = textPainter.computeLineMetrics().length;
|
||||||
|
return ValueListenableBuilder(
|
||||||
|
valueListenable: expanded,
|
||||||
|
builder: (context, values, _) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
LayoutBuilder(
|
||||||
|
builder: (BuildContext context, BoxConstraints constraints) {
|
||||||
|
if (!expanded.value && numberOfLines >= maxLinesToShow) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
content ?? "",
|
||||||
|
maxLines: maxLinesToShow,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: fontSize ?? 16.0,
|
||||||
|
color: ColorPalette.slate400,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
/* See More :: type 1 - See More | 2 - See Less */
|
||||||
|
SeeMoreLessWidget(
|
||||||
|
textData: 'See More',
|
||||||
|
type: 1,
|
||||||
|
section: 1,
|
||||||
|
onSeeMoreLessTap: () {
|
||||||
|
expanded.value = true;
|
||||||
|
},
|
||||||
|
alignment: alignmentMore,
|
||||||
|
hideIconMore: hideIconMore!,
|
||||||
|
hideTextMore: hideTextMore!,
|
||||||
|
),
|
||||||
|
/* See More :: type 1 - See More | 2 - See Less */
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
content ?? "",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: fontSize ?? 16.0,
|
||||||
|
color: ColorPalette.slate400,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (expanded.value && numberOfLines >= maxLinesToShow)
|
||||||
|
/* See Less :: type 1 - See More | 2 - See Less */
|
||||||
|
SeeMoreLessWidget(
|
||||||
|
textData: 'See Less',
|
||||||
|
type: 2,
|
||||||
|
section: 1,
|
||||||
|
onSeeMoreLessTap: () {
|
||||||
|
expanded.value = false;
|
||||||
|
},
|
||||||
|
alignment: alignmentMore,
|
||||||
|
hideIconMore: hideIconMore!,
|
||||||
|
hideTextMore: hideTextMore!,
|
||||||
|
),
|
||||||
|
/* See Less :: type 1 - See More | 2 - See Less */
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
import 'package:cims_apps/application/theme/color_palette.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class SeeMoreLessWidget extends StatelessWidget {
|
||||||
|
final String? textData;
|
||||||
|
final int? type; /* type 1 - See More | 2 - See Less */
|
||||||
|
final Function? onSeeMoreLessTap;
|
||||||
|
final int?
|
||||||
|
section; /* 1: About the course | 2 - Who can take up this course? | 3 - Mentors | 4 - Course Video Reviews */
|
||||||
|
final Alignment? alignment;
|
||||||
|
final bool hideTextMore;
|
||||||
|
final bool hideIconMore;
|
||||||
|
|
||||||
|
const SeeMoreLessWidget({
|
||||||
|
super.key,
|
||||||
|
required this.textData,
|
||||||
|
required this.type,
|
||||||
|
required this.onSeeMoreLessTap,
|
||||||
|
required this.section,
|
||||||
|
required this.alignment,
|
||||||
|
required this.hideTextMore,
|
||||||
|
required this.hideIconMore,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Align(
|
||||||
|
alignment: alignment ?? Alignment.centerLeft,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
if (onSeeMoreLessTap != null) {
|
||||||
|
onSeeMoreLessTap!();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text.rich(
|
||||||
|
softWrap: true,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: ColorPalette.primary,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
TextSpan(
|
||||||
|
text: "",
|
||||||
|
children: [
|
||||||
|
if(!hideTextMore)
|
||||||
|
TextSpan(
|
||||||
|
text: textData,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: ColorPalette.primary,
|
||||||
|
decoration: TextDecoration.underline,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if(!hideTextMore && !hideIconMore)
|
||||||
|
const WidgetSpan(
|
||||||
|
child: SizedBox(
|
||||||
|
width: 3.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if(!hideIconMore)
|
||||||
|
WidgetSpan(
|
||||||
|
child: Icon(
|
||||||
|
(type == 1)
|
||||||
|
? Icons.keyboard_arrow_down
|
||||||
|
: Icons.keyboard_arrow_up,
|
||||||
|
color: ColorPalette.slate300,
|
||||||
|
size: 28,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
75
lib/application/component/list_tile/list_tile_view.dart
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
import 'package:cims_apps/application/assets/path_assets.dart';
|
||||||
|
import 'package:cims_apps/application/component/image/image_view.dart';
|
||||||
|
import 'package:cims_apps/application/theme/color_palette.dart';
|
||||||
|
import 'package:cims_apps/core/utils/size_config.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ListTileView extends StatelessWidget {
|
||||||
|
final String title;
|
||||||
|
final VoidCallback? onPressed;
|
||||||
|
final Widget? prefixIcon, suffixIcon;
|
||||||
|
final EdgeInsetsGeometry? padding, margin;
|
||||||
|
final TextStyle? textStyle;
|
||||||
|
const ListTileView(
|
||||||
|
{Key? key,
|
||||||
|
required this.title,
|
||||||
|
this.onPressed,
|
||||||
|
this.prefixIcon,
|
||||||
|
this.suffixIcon,
|
||||||
|
this.padding,
|
||||||
|
this.textStyle,
|
||||||
|
this.margin})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
width: SizeConfig.width,
|
||||||
|
padding: padding ??
|
||||||
|
const EdgeInsets.symmetric(vertical: 16.0, horizontal: 8.0),
|
||||||
|
margin: margin ?? const EdgeInsets.symmetric(vertical: 16.0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: ColorPalette.blue50,
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
border: Border.all(
|
||||||
|
color: ColorPalette.greyLights,
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
prefixIcon ??
|
||||||
|
const ImageView(
|
||||||
|
image: PathAssets.iconChecklistOutlined,
|
||||||
|
width: 38,
|
||||||
|
height: 38,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 16,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
title,
|
||||||
|
style: textStyle ??
|
||||||
|
const TextStyle(
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: ColorPalette.slate500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
suffixIcon ??
|
||||||
|
IconButton(
|
||||||
|
onPressed: onPressed,
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.arrow_forward_ios,
|
||||||
|
color: ColorPalette.primary,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// : const SizedBox(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
41
lib/application/component/modal_redirect_app.dart
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import 'package:cims_apps/application/assets/path_assets.dart';
|
||||||
|
import 'package:cims_apps/application/component/image/image_view.dart';
|
||||||
|
import 'package:cims_apps/application/theme/color_palette.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class TypeApp {
|
||||||
|
String img, title;
|
||||||
|
|
||||||
|
TypeApp(this.img, this.title);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ModalRedirectApp extends StatelessWidget {
|
||||||
|
final String value;
|
||||||
|
const ModalRedirectApp({super.key, required this.value});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
Map<String, TypeApp> typeApp = {
|
||||||
|
'Shopping Pay': TypeApp(PathAssets.imgOpenShopping, 'Shopping App'),
|
||||||
|
};
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
ImageView(image: typeApp[value]!.img),
|
||||||
|
Text('Open ${typeApp[value]!.title}',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 22,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: ColorPalette.slate800
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text('You will be redirected to the ${typeApp[value]!.title.toLowerCase()} to continue the payment',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: ColorPalette.slate400
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
130
lib/application/component/numeric_pad/numeric_pad.dart
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
import 'package:cims_apps/application/assets/path_assets.dart';
|
||||||
|
import 'package:cims_apps/application/component/image/image_view.dart';
|
||||||
|
import 'package:cims_apps/application/theme/color_palette.dart';
|
||||||
|
import 'package:cims_apps/core/utils/size_config.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class NumericPad extends StatelessWidget {
|
||||||
|
final Function(String) onNumberSelected;
|
||||||
|
final bool isPin;
|
||||||
|
const NumericPad({super.key, required this.onNumberSelected, this.isPin = false});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
numberWidget('1'),
|
||||||
|
dividerGradient(false, Alignment.bottomCenter, Alignment.topCenter),
|
||||||
|
numberWidget('2'),
|
||||||
|
dividerGradient(false, Alignment.bottomCenter, Alignment.topCenter),
|
||||||
|
numberWidget('3')
|
||||||
|
],
|
||||||
|
),
|
||||||
|
dividerGradient(true, Alignment.centerLeft, Alignment.centerRight),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
numberWidget('4'),
|
||||||
|
dividerGradient(false, Alignment.center, Alignment.center, fullColor: true),
|
||||||
|
numberWidget('5'),
|
||||||
|
dividerGradient(false, Alignment.center, Alignment.center, fullColor: true),
|
||||||
|
numberWidget('6')
|
||||||
|
],
|
||||||
|
),
|
||||||
|
dividerGradient(true, Alignment.centerLeft, Alignment.centerRight),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
numberWidget('7'),
|
||||||
|
dividerGradient(false, Alignment.center, Alignment.center, fullColor: true),
|
||||||
|
numberWidget('8'),
|
||||||
|
dividerGradient(false, Alignment.center, Alignment.center, fullColor: true),
|
||||||
|
numberWidget('9')
|
||||||
|
],
|
||||||
|
),
|
||||||
|
dividerGradient(true, Alignment.centerLeft, Alignment.centerRight),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
isPin ? spaceWidget() : numberWidget('0'),
|
||||||
|
dividerGradient(false, Alignment.topCenter, Alignment.bottomCenter),
|
||||||
|
numberWidget(isPin ? '0' : '000'),
|
||||||
|
dividerGradient(false, Alignment.topCenter, Alignment.bottomCenter),
|
||||||
|
removeWidget()
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget dividerGradient(bool isHorizontal, AlignmentGeometry gradientFrom, AlignmentGeometry gradientTo, {bool fullColor = false}) {
|
||||||
|
return Container(
|
||||||
|
width: isHorizontal ? SizeConfig.width : 1,
|
||||||
|
height: isHorizontal ? 1 : SizeConfig.height * 0.097,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
colors: [
|
||||||
|
if(isHorizontal) ...[
|
||||||
|
ColorPalette.slate200.withOpacity(0)
|
||||||
|
],
|
||||||
|
ColorPalette.slate200,
|
||||||
|
fullColor ? ColorPalette.slate200 : ColorPalette.slate200.withOpacity(0)
|
||||||
|
],
|
||||||
|
begin: gradientFrom,
|
||||||
|
end: gradientTo
|
||||||
|
)
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget spaceWidget() {
|
||||||
|
return Expanded(
|
||||||
|
child: SizedBox()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget numberWidget(String number) {
|
||||||
|
return Expanded(
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
onNumberSelected(number);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: SizeConfig.height * .028),
|
||||||
|
child: Text(
|
||||||
|
number,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 28,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: ColorPalette.slate800
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget removeWidget() {
|
||||||
|
return Expanded(
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
onNumberSelected('');
|
||||||
|
},
|
||||||
|
child: Icon(
|
||||||
|
Icons.backspace_outlined,
|
||||||
|
size: 28,
|
||||||
|
color: ColorPalette.slate800,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
145
lib/application/component/otp/otp_view.dart
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
import 'package:cims_apps/application/component/custom_app_bar/custom_app_bar.dart';
|
||||||
|
import 'package:cims_apps/application/component/otp/otp_viewmodel.dart';
|
||||||
|
import 'package:cims_apps/application/component/text_caption/text_caption.dart';
|
||||||
|
import 'package:cims_apps/application/theme/color_palette.dart';
|
||||||
|
import 'package:cims_apps/core/route/route.dart';
|
||||||
|
import 'package:cims_apps/core/utils/size_config.dart';
|
||||||
|
import 'package:cims_apps/features/auth/registration/view/registration_password_view.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:pinput/pinput.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class OtpView extends StatelessWidget {
|
||||||
|
final String title;
|
||||||
|
final String? contentTitle, contentSubtitle;
|
||||||
|
const OtpView({
|
||||||
|
Key? key,
|
||||||
|
required this.title,
|
||||||
|
this.contentTitle,
|
||||||
|
this.contentSubtitle,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
Widget _otpContent(BuildContext context, OtpViewModel provider) {
|
||||||
|
return Form(
|
||||||
|
key: provider.formKey,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Pinput(
|
||||||
|
length: 4,
|
||||||
|
controller: provider.ctrlPin,
|
||||||
|
focusNode: provider.focusNode,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
validator: (value) {
|
||||||
|
if (value!.isEmpty) {
|
||||||
|
return 'Pin must be complete';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
defaultPinTheme: PinTheme(
|
||||||
|
textStyle: const TextStyle(
|
||||||
|
color: ColorPalette.slate800,
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
width: SizeConfig.width * .19,
|
||||||
|
height: SizeConfig.height * .08,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(color: const Color(0xFFE2E8F0)),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
errorPinTheme: PinTheme(
|
||||||
|
textStyle: const TextStyle(
|
||||||
|
color: ColorPalette.slate800,
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
width: SizeConfig.width * .19,
|
||||||
|
height: SizeConfig.height * .08,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(color: Colors.redAccent),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onCompleted: (pin) => provider.enableButton(),
|
||||||
|
onChanged: (value) {
|
||||||
|
if (provider.ctrlPin.length != 4) {
|
||||||
|
provider.enableButton(isActive: false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: const EdgeInsets.symmetric(vertical: 32.0),
|
||||||
|
width: SizeConfig.width,
|
||||||
|
height: SizeConfig.height * .07,
|
||||||
|
child: ElevatedButton(
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
elevation: 0,
|
||||||
|
backgroundColor: ColorPalette.primary,
|
||||||
|
),
|
||||||
|
onPressed: !provider.buttonIsActive
|
||||||
|
? null
|
||||||
|
: () {
|
||||||
|
if (provider.formKey.currentState!.validate()) {
|
||||||
|
final pin = provider.ctrlPin.text;
|
||||||
|
provider.validateOtp(pin).then((value) {
|
||||||
|
if (value) {
|
||||||
|
routePush(context,
|
||||||
|
page: const RegistrationPasswordView(),
|
||||||
|
routeType: RouteType.pushReplace);
|
||||||
|
} else {
|
||||||
|
provider.ctrlPin.clear();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: const Text(
|
||||||
|
'Verify',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ChangeNotifierProvider(
|
||||||
|
create: (context) => OtpViewModel(),
|
||||||
|
builder: (context, child) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: CustomAppBar(height: SizeConfig.height * .1, title: title),
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child:
|
||||||
|
Consumer<OtpViewModel>(builder: (context, provider, child) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
TextCaption(
|
||||||
|
title: contentTitle ?? '',
|
||||||
|
subtitle: contentSubtitle ?? '',
|
||||||
|
),
|
||||||
|
_otpContent(context, provider),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
provider.ctrlPin.clear();
|
||||||
|
},
|
||||||
|
child: const Text(
|
||||||
|
'Resend Code',
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
),
|
||||||
|
))
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
22
lib/application/component/otp/otp_viewmodel.dart
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class OtpViewModel extends ChangeNotifier {
|
||||||
|
var formKey = GlobalKey<FormState>();
|
||||||
|
var focusNode = FocusNode();
|
||||||
|
bool buttonIsActive = false;
|
||||||
|
|
||||||
|
TextEditingController ctrlPin = TextEditingController();
|
||||||
|
|
||||||
|
Future<bool> validateOtp(String pin) async {
|
||||||
|
final pinLength = pin.length;
|
||||||
|
if (pinLength == 4) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void enableButton({bool isActive = true}) {
|
||||||
|
buttonIsActive = isActive;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
57
lib/application/component/radio_agreement.dart
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
import 'package:cims_apps/application/component/expandable_widget/expandable_widget.dart';
|
||||||
|
import 'package:cims_apps/application/theme/color_palette.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class RadioAgreement extends StatelessWidget {
|
||||||
|
final void Function() onTap;
|
||||||
|
final bool isAgree;
|
||||||
|
final String desc;
|
||||||
|
const RadioAgreement({super.key, required this.isAgree, required this.desc, required this.onTap,});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
GestureDetector(
|
||||||
|
onTap: onTap,
|
||||||
|
child: AnimatedContainer(
|
||||||
|
margin: const EdgeInsets.only(top: 4),
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
height: 16,
|
||||||
|
width: 16,
|
||||||
|
padding: const EdgeInsets.all(1),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
border: Border.all(
|
||||||
|
color: isAgree
|
||||||
|
? ColorPalette.primary
|
||||||
|
: ColorPalette.slate200)),
|
||||||
|
child: AnimatedContainer(
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color:
|
||||||
|
isAgree ? ColorPalette.primary : ColorPalette.white,
|
||||||
|
shape: BoxShape.circle),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 12,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: ExpandableWidget(
|
||||||
|
content: desc,
|
||||||
|
maxLinesToShow: 3,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
213
lib/application/component/risk_profile.dart
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
import 'package:cims_apps/application/assets/path_assets.dart';
|
||||||
|
import 'package:cims_apps/application/component/expandable_widget/expandable_widget.dart';
|
||||||
|
import 'package:cims_apps/application/theme/color_palette.dart';
|
||||||
|
import 'package:cims_apps/core/utils/size_config.dart';
|
||||||
|
import 'package:cims_apps/features/auth/registration/view/submission_data/risk_profile/risk_profile_view_model/risk_profile_view_model.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class RiskProfile extends StatelessWidget {
|
||||||
|
final int totalScore;
|
||||||
|
final bool rowSuitableProduct;
|
||||||
|
const RiskProfile({super.key, required this.totalScore, required this.rowSuitableProduct});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
List<RiskProfileResult> listRiskProfileResult = [
|
||||||
|
RiskProfileResult(
|
||||||
|
'Conservative',
|
||||||
|
PathAssets.imgCat,
|
||||||
|
ColorPalette.green500,
|
||||||
|
'Investors with a conservative risk profile are risk-averse or do not want to experience large losses. Therefore, mutual fund products that are suitable for conservative investors are products that have low risk and stable returns.',
|
||||||
|
[
|
||||||
|
{'desc': 'Money Market Mutual Fund', 'icon': PathAssets.iconStrongBox},
|
||||||
|
{'desc': 'Fixed Income Mutual Fund', 'icon': PathAssets.iconMoneyReceive},
|
||||||
|
{'desc': 'Balanced Mutual Fund', 'icon': PathAssets.iconBalance},
|
||||||
|
]
|
||||||
|
),
|
||||||
|
RiskProfileResult(
|
||||||
|
'Moderate',
|
||||||
|
PathAssets.imgDeer,
|
||||||
|
ColorPalette.orange500,
|
||||||
|
'Investors with a moderate risk profile are investors who are ready to accept moderate risk to get higher returns than conservative mutual fund products. Therefore, mutual fund products that are suitable for moderate investors are products that have moderate risk and higher returns than conservative mutual fund products.',
|
||||||
|
[
|
||||||
|
{'desc': 'Fixed Income Mutual Fund', 'icon': PathAssets.iconMoneyReceive},
|
||||||
|
{'desc': 'Balanced Mutual Fund', 'icon': PathAssets.iconBalance},
|
||||||
|
]
|
||||||
|
),
|
||||||
|
RiskProfileResult(
|
||||||
|
'Aggressive',
|
||||||
|
PathAssets.imgLion,
|
||||||
|
ColorPalette.purple500,
|
||||||
|
'Investors with an aggressive risk profile are investors who are ready to accept high risks to get high returns. Therefore, mutual fund products that are suitable for aggressive investors are products that have high risk and high returns.',
|
||||||
|
[
|
||||||
|
{'desc': 'Equity Mutual Fund', 'icon': PathAssets.iconCoins},
|
||||||
|
{'desc': 'Aggressive Balanced Fund', 'icon': PathAssets.iconBalance},
|
||||||
|
]
|
||||||
|
)
|
||||||
|
];
|
||||||
|
RiskProfileResult riskProfile;
|
||||||
|
if(totalScore <= 25){
|
||||||
|
riskProfile = listRiskProfileResult[0];
|
||||||
|
}else if(totalScore <= 50){
|
||||||
|
riskProfile = listRiskProfileResult[1];
|
||||||
|
}else{
|
||||||
|
riskProfile = listRiskProfileResult[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: riskProfile.color,
|
||||||
|
image: DecorationImage(image: AssetImage(riskProfile.img), alignment: Alignment.centerRight),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: riskProfile.color.withOpacity(0.2),
|
||||||
|
blurRadius: 30
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(24),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
riskProfile.type,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 24,
|
||||||
|
color: ColorPalette.white
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16,),
|
||||||
|
const Text('Total Score :',
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 16,
|
||||||
|
color: ColorPalette.white
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text('$totalScore',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 28,
|
||||||
|
color: ColorPalette.white
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 24,
|
||||||
|
),
|
||||||
|
ExpandableWidget(
|
||||||
|
content: riskProfile.desc,
|
||||||
|
hideTextMore: true,
|
||||||
|
hideIconMore: false,
|
||||||
|
maxLinesToShow: 4,
|
||||||
|
alignmentMore: Alignment.center,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 24,
|
||||||
|
),
|
||||||
|
const Text(
|
||||||
|
'Suitable Product',
|
||||||
|
style: TextStyle(
|
||||||
|
color: ColorPalette.slate800,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 18
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
rowSuitableProduct ?
|
||||||
|
Row(
|
||||||
|
children: riskProfile.suitableProduct.asMap().entries.map((e) {
|
||||||
|
return Expanded(
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.only(left: e.key != 0 ? 12 : 0),
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(color: ColorPalette.slate200),
|
||||||
|
borderRadius: BorderRadius.circular(6)
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: riskProfile.color.withOpacity(0.1)
|
||||||
|
),
|
||||||
|
child: Image.asset(e.value['icon'], width: SizeConfig.width * 0.07, color: riskProfile.color)
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 12,
|
||||||
|
),
|
||||||
|
Text(e.value['desc'],
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: ColorPalette.slate800
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
)
|
||||||
|
: Wrap(
|
||||||
|
runSpacing: 16,
|
||||||
|
children: riskProfile.suitableProduct.map((e) {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(6),
|
||||||
|
border: Border.all(color: ColorPalette.slate200),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: riskProfile.color.withOpacity(0.1)
|
||||||
|
),
|
||||||
|
child: Image.asset(e['icon'], width: SizeConfig.width * 0.07, color: riskProfile.color)
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 12,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Text(e['desc'],
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: ColorPalette.slate800
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
185
lib/application/component/select_form/select_form_view.dart
Normal file
|
@ -0,0 +1,185 @@
|
||||||
|
import 'package:cims_apps/application/component/button/button_view.dart';
|
||||||
|
import 'package:cims_apps/application/component/text_form/text_form_view.dart';
|
||||||
|
import 'package:cims_apps/application/theme/color_palette.dart';
|
||||||
|
import 'package:cims_apps/core/utils/size_config.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ItemSelectForm {
|
||||||
|
final String key;
|
||||||
|
final String text;
|
||||||
|
final String? description;
|
||||||
|
final bool isOther;
|
||||||
|
String image;
|
||||||
|
|
||||||
|
ItemSelectForm(
|
||||||
|
this.key,
|
||||||
|
this.text, {
|
||||||
|
this.isOther = false,
|
||||||
|
this.image = "",
|
||||||
|
this.description,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class SelectFormView extends StatelessWidget {
|
||||||
|
final String name;
|
||||||
|
final String? hintText;
|
||||||
|
final TextStyle? hintTextStyle;
|
||||||
|
final TextEditingController? ctrl;
|
||||||
|
final List<ItemSelectForm> listItem;
|
||||||
|
final ValueChanged<String> onSelect;
|
||||||
|
final FormFieldValidator<String>? validator;
|
||||||
|
final _borderRadius = const Radius.circular(24);
|
||||||
|
final bool? enabled;
|
||||||
|
const SelectFormView(
|
||||||
|
{Key? key,
|
||||||
|
required this.name,
|
||||||
|
this.hintText,
|
||||||
|
this.hintTextStyle,
|
||||||
|
this.ctrl,
|
||||||
|
required this.listItem,
|
||||||
|
required this.onSelect,
|
||||||
|
this.validator,
|
||||||
|
this.enabled})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
bottomSheet() {
|
||||||
|
showModalBottomSheet<void>(
|
||||||
|
context: context,
|
||||||
|
isDismissible: false,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: _borderRadius,
|
||||||
|
topRight: _borderRadius,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return StatefulBuilder(builder: (
|
||||||
|
BuildContext context,
|
||||||
|
StateSetter stateSetter,
|
||||||
|
) {
|
||||||
|
return Container(
|
||||||
|
height: SizeConfig.height * .45,
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.vertical(
|
||||||
|
top: Radius.circular(20),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
name,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: ColorPalette.slate800,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
ctrl?.clear();
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.clear,
|
||||||
|
size: 26,
|
||||||
|
color: ColorPalette.greyBase,
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Expanded(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.vertical,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
...listItem.map((e) {
|
||||||
|
bool selected = e.text == ctrl?.text;
|
||||||
|
return Card(
|
||||||
|
elevation: 0,
|
||||||
|
color: Colors.white,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
side: BorderSide(
|
||||||
|
color: selected
|
||||||
|
? ColorPalette.primary
|
||||||
|
: ColorPalette.greyBorder,
|
||||||
|
),
|
||||||
|
borderRadius:
|
||||||
|
const BorderRadius.all(Radius.circular(12)),
|
||||||
|
),
|
||||||
|
child: ListTile(
|
||||||
|
title: Text(
|
||||||
|
e.text,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: selected
|
||||||
|
? ColorPalette.primary
|
||||||
|
: ColorPalette.slate500),
|
||||||
|
),
|
||||||
|
subtitle: e.description != null
|
||||||
|
? Text(
|
||||||
|
e.description!,
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
trailing: selected
|
||||||
|
? const Icon(Icons.check_circle_rounded,
|
||||||
|
color: ColorPalette.primary)
|
||||||
|
: null,
|
||||||
|
onTap: () {
|
||||||
|
stateSetter(() {
|
||||||
|
ctrl?.text = e.text;
|
||||||
|
onSelect(e.text);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ButtonView(
|
||||||
|
name: 'Select',
|
||||||
|
marginVertical: 4.0,
|
||||||
|
onPressed: () {
|
||||||
|
if (ctrl!.text.isNotEmpty) {
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TextFormView(
|
||||||
|
name: name,
|
||||||
|
readOnly: true,
|
||||||
|
enabled: enabled ?? true,
|
||||||
|
onTap: () {
|
||||||
|
if (listItem.isNotEmpty) bottomSheet();
|
||||||
|
},
|
||||||
|
validator: validator,
|
||||||
|
hintText: hintText,
|
||||||
|
hintTextStyle: hintTextStyle,
|
||||||
|
ctrl: ctrl,
|
||||||
|
suffixIcon: Icon(
|
||||||
|
Icons.keyboard_arrow_down,
|
||||||
|
size: SizeConfig.width * .07,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
135
lib/application/component/set_pin_view/set_pin_view.dart
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
import 'package:cims_apps/application/assets/path_assets.dart';
|
||||||
|
import 'package:cims_apps/application/component/custom_app_bar/custom_app_bar.dart';
|
||||||
|
import 'package:cims_apps/application/component/image/image_view.dart';
|
||||||
|
import 'package:cims_apps/application/component/set_pin_view/set_pin_viewmodel.dart';
|
||||||
|
import 'package:cims_apps/application/theme/color_palette.dart';
|
||||||
|
import 'package:cims_apps/core/route/route.dart';
|
||||||
|
import 'package:cims_apps/core/utils/size_config.dart';
|
||||||
|
import 'package:cims_apps/features/auth/registration/view/registration_success_view.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:pinput/pinput.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class SetPinView extends StatelessWidget {
|
||||||
|
final String currentPin;
|
||||||
|
final void Function(BuildContext context, String pin) submitPin;
|
||||||
|
|
||||||
|
const SetPinView({
|
||||||
|
Key? key,
|
||||||
|
required this.currentPin,
|
||||||
|
required this.submitPin,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
Widget _stepItem({bool isCurrentStep = false, bool isDone = false}) {
|
||||||
|
return Container(
|
||||||
|
margin:
|
||||||
|
const EdgeInsets.only(right: 4.0, left: 4.0, top: 16.0, bottom: 40.0),
|
||||||
|
height: 6,
|
||||||
|
width: SizeConfig.width * .08,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: isCurrentStep || isDone
|
||||||
|
? ColorPalette.primary
|
||||||
|
: ColorPalette.greyBorderNeutrals,
|
||||||
|
borderRadius: BorderRadius.circular(50),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final textTheme = Theme.of(context).textTheme;
|
||||||
|
final pinInputController = TextEditingController();
|
||||||
|
const pinInputLength = 6;
|
||||||
|
const defaultPinTheme = PinTheme(
|
||||||
|
margin: EdgeInsets.symmetric(horizontal: 4.0, vertical: 16.0),
|
||||||
|
textStyle: TextStyle(
|
||||||
|
fontSize: 22,
|
||||||
|
color: Colors.black,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(),
|
||||||
|
);
|
||||||
|
|
||||||
|
final pInputFocusNode = FocusNode();
|
||||||
|
|
||||||
|
final focusedPinTheme = defaultPinTheme.copyWith(
|
||||||
|
decoration: defaultPinTheme.decoration?.copyWith(),
|
||||||
|
);
|
||||||
|
|
||||||
|
final submittedPinTheme = defaultPinTheme.copyWith(
|
||||||
|
decoration: defaultPinTheme.decoration?.copyWith(),
|
||||||
|
);
|
||||||
|
final followingPinTheme = defaultPinTheme.copyWith(
|
||||||
|
width: 13,
|
||||||
|
height: 13,
|
||||||
|
textStyle: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: Color.fromRGBO(30, 60, 87, 1),
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
decoration: defaultPinTheme.decoration?.copyWith(
|
||||||
|
color: ColorPalette.white,
|
||||||
|
border: Border.all(color: ColorPalette.slate300),
|
||||||
|
borderRadius: BorderRadius.circular(100),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return ChangeNotifierProvider(
|
||||||
|
create: (context) => SetPinViewModel(),
|
||||||
|
builder: (context, child) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: CustomAppBar(
|
||||||
|
height: SizeConfig.height * .1, title: 'Registration'),
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||||
|
child: Consumer<SetPinViewModel>(
|
||||||
|
builder: (context, provider, child) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: List.generate(
|
||||||
|
9,
|
||||||
|
(index) => _stepItem(isCurrentStep: true, isDone: true),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ImageView(
|
||||||
|
image: PathAssets.iconLock,
|
||||||
|
width: SizeConfig.width * .15,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
!provider.isPinCompleted
|
||||||
|
? 'Set your PIN'
|
||||||
|
: 'Confirm your PIN',
|
||||||
|
style: textTheme.headlineLarge,
|
||||||
|
),
|
||||||
|
Pinput(
|
||||||
|
onCompleted: (pin) {
|
||||||
|
if (!provider.isPinCompleted) {
|
||||||
|
provider.changePin();
|
||||||
|
pinInputController.clear();
|
||||||
|
} else {
|
||||||
|
routePush(context,
|
||||||
|
routeType: RouteType.pushReplace,
|
||||||
|
page: const RegistrationSuccessView());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
obscureText: true,
|
||||||
|
autofocus: true,
|
||||||
|
isCursorAnimationEnabled: false,
|
||||||
|
length: pinInputLength,
|
||||||
|
controller: pinInputController,
|
||||||
|
defaultPinTheme: defaultPinTheme,
|
||||||
|
focusNode: pInputFocusNode,
|
||||||
|
focusedPinTheme: focusedPinTheme,
|
||||||
|
submittedPinTheme: submittedPinTheme,
|
||||||
|
followingPinTheme: followingPinTheme,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class SetPinViewModel extends ChangeNotifier {
|
||||||
|
bool isPinCompleted = false;
|
||||||
|
void changePin() {
|
||||||
|
isPinCompleted = !isPinCompleted;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
77
lib/application/component/subscribe/goal_investing_view.dart
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
import 'package:cims_apps/application/assets/path_assets.dart';
|
||||||
|
import 'package:cims_apps/application/component/subscribe/other_plan_view.dart';
|
||||||
|
import 'package:cims_apps/application/theme/color_palette.dart';
|
||||||
|
import 'package:cims_apps/core/route/route.dart';
|
||||||
|
import 'package:cims_apps/core/utils/size_config.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class GoalInvest {
|
||||||
|
String icon;
|
||||||
|
String title;
|
||||||
|
|
||||||
|
GoalInvest(this.icon, this.title);
|
||||||
|
}
|
||||||
|
|
||||||
|
class GoalInvestingView extends StatelessWidget {
|
||||||
|
final void Function(String) onListSelected;
|
||||||
|
const GoalInvestingView({super.key, required this.onListSelected});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
List<GoalInvest> listGoalInvest = [
|
||||||
|
GoalInvest(PathAssets.iconToga, 'Education'),
|
||||||
|
GoalInvest(PathAssets.iconCake, 'Marriage'),
|
||||||
|
GoalInvest(PathAssets.iconHouse, 'Old age days'),
|
||||||
|
GoalInvest(PathAssets.iconCreatePlan, 'Other Plan'),
|
||||||
|
];
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children:
|
||||||
|
listGoalInvest.asMap().entries.map((e) {
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.only(top: e.key != 0 ? 16 : 0),
|
||||||
|
child: ListTile(
|
||||||
|
onTap: () {
|
||||||
|
if(e.value.title == 'Other Plan'){
|
||||||
|
routePush(
|
||||||
|
context,
|
||||||
|
page: OtherPlanView(
|
||||||
|
selectedPlan: (val) {
|
||||||
|
onListSelected(val);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}else{
|
||||||
|
onListSelected(e.value.title);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
side: BorderSide(color: ColorPalette.slate200),
|
||||||
|
borderRadius: BorderRadius.circular(14)
|
||||||
|
),
|
||||||
|
leading: Container(
|
||||||
|
padding: EdgeInsets.all(4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: ColorPalette.blue200.withOpacity(0.5),
|
||||||
|
borderRadius: BorderRadius.circular(8)
|
||||||
|
),
|
||||||
|
child: Image.asset(
|
||||||
|
e.value.icon,
|
||||||
|
width: SizeConfig.width * 0.07
|
||||||
|
)
|
||||||
|
),
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||||
|
title: Text(e.value.title,
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
fontSize: 16,
|
||||||
|
color: ColorPalette.slate800
|
||||||
|
),
|
||||||
|
),
|
||||||
|
trailing: Icon(Icons.chevron_right_rounded, color: ColorPalette.slate400),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
177
lib/application/component/subscribe/input_investment_view.dart
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:cims_apps/application/component/button/button_view.dart';
|
||||||
|
import 'package:cims_apps/application/component/numeric_pad/numeric_pad.dart';
|
||||||
|
import 'package:cims_apps/application/theme/color_palette.dart';
|
||||||
|
import 'package:cims_apps/core/utils/number_formatter.dart';
|
||||||
|
import 'package:cims_apps/core/utils/size_config.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class InputInvestmentView extends StatefulWidget {
|
||||||
|
final String? currentPlan;
|
||||||
|
final void Function()? changePlan;
|
||||||
|
final int? currentPrice;
|
||||||
|
final int? minimumPrice;
|
||||||
|
final int? maximumPrice;
|
||||||
|
final void Function(String value) nextMove;
|
||||||
|
const InputInvestmentView({super.key, required this.nextMove, this.currentPlan, this.minimumPrice, this.maximumPrice, this.currentPrice, this.changePlan});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<InputInvestmentView> createState() => _InputInvestmentViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _InputInvestmentViewState extends State<InputInvestmentView> {
|
||||||
|
TextEditingController inputController = TextEditingController();
|
||||||
|
|
||||||
|
void validationInputValue(String currentValue) {
|
||||||
|
currentValue = currentValue.replaceAll('Rp ', '').replaceAll('.', '');
|
||||||
|
if(currentValue.isEmpty){
|
||||||
|
currentValue = '0';
|
||||||
|
}
|
||||||
|
double parseValue = double.parse(currentValue);
|
||||||
|
if(widget.minimumPrice != null){
|
||||||
|
if(parseValue <= widget.minimumPrice!){
|
||||||
|
parseValue = widget.minimumPrice!.toDouble();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(widget.maximumPrice != null){
|
||||||
|
if(parseValue >= widget.maximumPrice!){
|
||||||
|
parseValue = widget.maximumPrice!.toDouble();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inputController.text = NumberFormatter.numberCurrency(parseValue, 'Rp ', 'id_ID', decimalDigits: 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
// TODO: implement initState
|
||||||
|
if(widget.currentPrice != null){
|
||||||
|
inputController.text = NumberFormatter.numberCurrency(widget.currentPrice, 'Rp ', 'id_ID', decimalDigits: 0);
|
||||||
|
}else{
|
||||||
|
inputController.text = 'Rp 0';
|
||||||
|
}
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
// TODO: implement dispose
|
||||||
|
super.dispose();
|
||||||
|
inputController.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(16)
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
if(widget.currentPlan != null)
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(widget.currentPlan ?? '',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
InkWell(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
onTap: widget.changePlan,
|
||||||
|
child: const Row(
|
||||||
|
children: [
|
||||||
|
Icon(Icons.change_circle_outlined, color: ColorPalette.primary, size: 20),
|
||||||
|
SizedBox(width: 4),
|
||||||
|
Text('Change',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: ColorPalette.primary
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: inputController,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 28,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: ColorPalette.slate800
|
||||||
|
),
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
onChanged: (value) {
|
||||||
|
validationInputValue(value);
|
||||||
|
},
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: ColorPalette.primary,
|
||||||
|
width: 2
|
||||||
|
),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
if(widget.minimumPrice != null)
|
||||||
|
Text('Minimum ${NumberFormatter.numberCurrency(widget.minimumPrice, 'Rp ', 'id_ID')}',
|
||||||
|
style: const TextStyle(
|
||||||
|
color: ColorPalette.slate400,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if(widget.maximumPrice != null)
|
||||||
|
Text('Maximum ${NumberFormatter.numberCurrency(widget.maximumPrice, 'Rp ', 'id_ID')}',
|
||||||
|
style: const TextStyle(
|
||||||
|
color: ColorPalette.slate400,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
NumericPad(onNumberSelected: (p0) {
|
||||||
|
String currentValue = inputController.text;
|
||||||
|
if(p0.isNotEmpty){
|
||||||
|
if(currentValue != '0'){
|
||||||
|
currentValue = currentValue + p0;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
currentValue = currentValue.substring(0, currentValue.length - 1);
|
||||||
|
}
|
||||||
|
validationInputValue(currentValue);
|
||||||
|
}),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
ButtonView(
|
||||||
|
name: 'Next',
|
||||||
|
onPressed: () {
|
||||||
|
widget.nextMove(inputController.text);
|
||||||
|
},
|
||||||
|
width: SizeConfig.width,
|
||||||
|
heightWrapContent: true,
|
||||||
|
contentPadding: const EdgeInsets.symmetric(vertical: 16),
|
||||||
|
marginVertical: 0,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|