Compare commits

..

109 Commits
main ... yoga

Author SHA1 Message Date
e441c56bab fix: product view 2024-03-06 16:43:02 +07:00
e513df325c fix: homepage view 2024-03-05 19:25:49 +07:00
23e1a6628f fix: merge origin dev 2024-03-05 15:43:52 +07:00
4b07219928 Merge pull request 'bayu/dev' (#19) from bayu/dev into dev
Reviewed-on: #19
2024-03-05 15:27:21 +07:00
9cdda42b8b Merge branch 'dev' of https://git.empatnusabangsa.com/nugrohob825/cims_apps into bayu/dev 2024-03-05 15:26:46 +07:00
6f5d3ccca8 feat: transaction 2024-03-05 15:25:44 +07:00
eb99ad9d7f feat: profile page 2024-02-29 17:14:05 +07:00
ff515e2621 fix: styling step registration 2024-02-26 15:11:28 +07:00
f057a346c2 fix: add validation form data bank 2024-02-26 15:05:47 +07:00
f84fe1017d fix: add validation form data id card 2024-02-26 14:00:19 +07:00
27ba55314b fix: styling take picture 2024-02-26 13:52:29 +07:00
59e046bd92 fix: add validation form email 2024-02-26 13:44:27 +07:00
4461b78565 fix: add validation form 2024-02-26 13:21:20 +07:00
f2f688f9f3 fix: route back button 2024-02-26 12:30:18 +07:00
d1adfd2ab0 Merge pull request 'bayu/dev' (#18) from bayu/dev into dev
Reviewed-on: #18
2024-02-26 00:25:27 +07:00
33b2ab85e3 merge conflict 2024-02-26 00:23:54 +07:00
6a43a3dcaf fix: styling dashboard public 2024-02-26 00:17:58 +07:00
db1280b272 fix: select data bank 2024-02-25 23:43:48 +07:00
ae4f9c25c4 fix: component select form 2024-02-25 19:58:41 +07:00
a87afe16cb Merge pull request 'yoga' (#17) from yoga into dev
Reviewed-on: #17
2024-02-23 14:57:36 +07:00
1904e9a4a3 Merge branch 'dev' into yoga 2024-02-23 14:56:39 +07:00
d79959c47f fix: validation input investment 2024-02-23 14:56:06 +07:00
38837bd4f8 feat: expandable text component 2024-02-22 18:13:54 +07:00
d966108e9e fix: more detail redeem product 2024-02-22 17:01:56 +07:00
506f364b87 fix: layout dashboard public 2024-02-22 16:27:15 +07:00
506480d812 fix: button terms and condition 2024-02-22 15:56:09 +07:00
a6ea9a2314 Merge pull request 'yoga' (#16) from yoga into dev
Reviewed-on: #16
2024-02-22 14:46:52 +07:00
176261923d fix: provider view model agreement redeem and product 2024-02-22 14:43:40 +07:00
a3148d8210 feat: wip redeem portfolio 2024-02-21 19:45:22 +07:00
6e03fa5fa7 fix: validation questioner risk profile 2024-02-21 13:26:29 +07:00
de1782c2c2 fix: see more product view 2024-02-20 19:57:52 +07:00
89a79276a6 feat: new icon bottom navigation bar 2024-02-20 19:33:32 +07:00
4737a91ab1 Merge pull request 'bayu/dev' (#15) from bayu/dev into dev
Reviewed-on: #15
2024-02-20 18:49:59 +07:00
ff135dd47b fix: styling 2024-02-20 18:49:14 +07:00
2e98c1a234 fix: rotate photo selfie 2024-02-20 18:21:04 +07:00
c4c0479341 fix: add validation submit sign in 2024-02-20 15:38:21 +07:00
e510aaefd7 fix: widget overflow 2024-02-20 15:22:05 +07:00
dfb947dce5 fix: widget overflow 2024-02-20 15:14:25 +07:00
9761dc369c feat: profile and transaction page 2024-02-20 14:53:30 +07:00
9e2304990c feat: login gmail 2024-02-20 14:43:43 +07:00
8b1b3e950f Merge branch 'dev' of https://git.empatnusabangsa.com/nugrohob825/cims_apps into bayu/dev 2024-02-20 12:06:56 +07:00
aa987fe320 Merge pull request 'yoga' (#14) from yoga into dev
Reviewed-on: #14
2024-02-20 12:02:39 +07:00
ad6195061d fix: widget overflow 2024-02-20 10:24:32 +07:00
b5a382ce96 Merge pull request 'bayu/dev' (#13) from bayu/dev into dev
Reviewed-on: #13
2024-02-19 22:51:02 +07:00
fdca27233b fix: registration features 2024-02-19 22:50:07 +07:00
bd065242e6 feat: component date picker 2024-02-19 22:49:43 +07:00
a35110af71 fix: stepper registration 2024-02-19 21:47:46 +07:00
83211e76f9 git pull origin dev 2024-02-19 20:01:37 +07:00
99db140a0c feat: flow subscribe product 2024-02-19 19:58:29 +07:00
57a4e828c9 Merge pull request 'bayu/dev' (#12) from bayu/dev into dev
Reviewed-on: #12
2024-02-19 19:42:41 +07:00
466d49312d fix: submit data id card 2024-02-19 19:41:36 +07:00
9ba8b79112 fix: registration step 2024-02-19 18:24:20 +07:00
4f50dc951a fix: component select form 2024-02-19 15:24:36 +07:00
d66a9e3435 fix: add validator password 2024-02-19 13:40:38 +07:00
8e04b4e77e fix: change app bar in initial sign up 2024-02-19 13:28:42 +07:00
afc2bd3cc9 feat: set pin page 2024-02-19 13:04:28 +07:00
0762a8ab0c feat: plan view, wip plan view model 2024-02-16 20:32:34 +07:00
66ace5b217 Merge pull request 'bayu/dev' (#11) from bayu/dev into dev
Reviewed-on: #11
2024-02-16 18:10:48 +07:00
5cb76fca7f feat: registration success page 2024-02-16 18:03:36 +07:00
e86e67b9c9 fix: term condition page 2024-02-16 17:35:58 +07:00
a6248520ef Merge branch 'dev' of https://git.empatnusabangsa.com/nugrohob825/cims_apps into bayu/dev 2024-02-16 17:17:32 +07:00
eb1eb83d52 fix: risk profile 2024-02-16 17:17:19 +07:00
7706fe4387 Merge pull request 'yoga' (#10) from yoga into dev
Reviewed-on: #10
2024-02-16 17:14:45 +07:00
9da1675250 feat: submit signature 2024-02-16 17:00:19 +07:00
298d7f46d2 fix: submit data bank account 2024-02-16 16:03:06 +07:00
368f326123 merge conflict 2024-02-16 11:46:28 +07:00
a574f30424 fix: submit data bank account 2024-02-16 11:39:16 +07:00
3c1f7e210a fix: assets local history 2024-02-15 20:24:19 +07:00
b3a68b4436 feat: terms and condition view 2024-02-15 20:00:15 +07:00
ce2bf8a777 Merge pull request 'feat: wip plan view, and component numeric pad' (#9) from yoga into dev
Reviewed-on: #9
2024-02-15 19:56:43 +07:00
7e9c109fa2 feat: wip plan view, and component numeric pad 2024-02-15 19:55:21 +07:00
8537045d74 Merge pull request 'bayu/dev' (#8) from bayu/dev into dev
Reviewed-on: #8
2024-02-15 19:25:38 +07:00
23d189c288 feat: submit data bank account 2024-02-15 19:23:57 +07:00
4bad9cd18c feat: submit data id card 2024-02-15 17:34:40 +07:00
3dca727a5e fix: submit photo selfie 2024-02-15 14:14:31 +07:00
4c1cc7422b feat: submit photo selfie 2024-02-15 14:07:32 +07:00
219339f577 Merge pull request 'fix: padding text form view' (#7) from yoga into dev
Reviewed-on: #7
2024-02-12 17:07:20 +07:00
0c2441091f fix: padding text form view 2024-02-12 17:07:04 +07:00
d86820ec98 Merge pull request 'yoga' (#6) from yoga into dev
Reviewed-on: #6
2024-02-12 17:04:28 +07:00
ececa5e541 fix: pull origin dev 2024-02-12 17:04:01 +07:00
b0de8c255e feat: apk build 2024-02-12 17:00:20 +07:00
6977c8166d feat: risk profile view 2024-02-10 16:29:00 +07:00
7a1cddee03 fix: more detailing product view 2024-02-07 18:34:46 +07:00
db6e4d543d Merge pull request 'bayu/dev' (#4) from bayu/dev into dev
Reviewed-on: #4
2024-02-07 18:31:32 +07:00
d672a23564 fix: stepper submission data 2024-02-07 17:17:32 +07:00
5e97154100 rename file 2024-02-07 15:57:06 +07:00
59e6e82d13 fix: content padding text field 2024-02-07 15:54:53 +07:00
477eb5d2b1 feat: preview photo 2024-02-07 15:53:00 +07:00
370db229de feat: take photo 2024-02-07 15:52:50 +07:00
9475767021 feat: login view 2024-02-07 14:44:40 +07:00
0a347f5e6d Merge pull request 'yoga' (#3) from yoga into dev
Reviewed-on: #3
2024-02-06 21:51:04 +07:00
4d58a7dced fix: continued product view 2024-02-06 21:49:39 +07:00
81231505b1 fix: continued product view 2024-02-06 21:45:25 +07:00
6e2516d9c8 Merge pull request 'bayu/dev' (#2) from bayu/dev into dev
Reviewed-on: #2
2024-02-06 19:25:23 +07:00
e538fa5927 fix: sign up feature 2024-02-06 19:21:41 +07:00
7ec266cded fix: otp component 2024-02-06 18:38:34 +07:00
96c676ac4c feat: state time machine product view 2024-02-06 18:28:11 +07:00
e1cabe0a09 feat: product chart view 2024-02-06 17:48:49 +07:00
4b4b42beae add otp component 2024-02-06 17:02:12 +07:00
dacf5461f3 add registration password page 2024-02-06 14:40:10 +07:00
5d4bc47adf styling components 2024-02-06 14:39:16 +07:00
80e4657240 add string utils 2024-02-06 14:38:55 +07:00
d82d427bcc Merge remote-tracking branch 'origin/dev' into yoga
# Conflicts:
#	lib/application/assets/path_assets.dart
#	pubspec.yaml
2024-02-06 11:14:51 +07:00
e63e5588fb Merge pull request 'bayu/dev' (#1) from bayu/dev into dev
Reviewed-on: #1
2024-02-06 10:54:29 +07:00
f407eca735 feat: product view wip 2024-02-05 22:36:16 +07:00
ff1886cec1 fix: submission data regis 2024-02-05 18:11:16 +07:00
1616f22925 add initial take photo 2024-02-05 18:10:28 +07:00
0b754bf939 add plugin provider 2024-02-05 18:09:41 +07:00
0e7ad81345 feat: home view, portfolio view, invest type view 2024-02-05 15:49:02 +07:00
173 changed files with 11857 additions and 498 deletions

View File

@ -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')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
@ -45,17 +51,27 @@ android {
applicationId "com.example.cims_apps"
// 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.
minSdkVersion flutter.minSdkVersion
minSdkVersion 21
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
storePassword keystoreProperties['storePassword']
}
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
// signingConfig signingConfigs.debug
signingConfig signingConfigs.release
}
}
}

View File

@ -1,6 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:label="cims_apps"
android:label="cims investment"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"
android:enableOnBackInvokedCallback="true"

BIN
assets/icons/icon-bag.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
assets/icons/icon-cake.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
assets/icons/icon-car.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
assets/icons/icon-card.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
assets/icons/icon-cart.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
assets/icons/icon-cat.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
assets/icons/icon-coins.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
assets/icons/icon-faqs.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
assets/icons/icon-flag.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
assets/icons/icon-fund.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
assets/icons/icon-home.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
assets/icons/icon-house.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
assets/icons/icon-ktp1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

BIN
assets/icons/icon-ktp2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
assets/icons/icon-ktp3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

BIN
assets/icons/icon-ktp4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

BIN
assets/icons/icon-lock.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 899 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
assets/icons/icon-plane.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
assets/icons/icon-shop.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
assets/icons/icon-thumb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
assets/icons/icon-toga.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 636 KiB

BIN
assets/images/img-cat.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
assets/images/img-deer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

BIN
assets/images/img-lion.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

View File

@ -1,7 +1,7 @@
class PathAssets {
PathAssets._();
/// LOGO
/// ICON
static const String iconSplashRight = 'assets/icons/splash-right.png';
static const String iconSplashLeft = 'assets/icons/splash-left.png';
static const String iconReksadana = 'assets/icons/icon-reksadana.png';
@ -10,6 +10,69 @@ class PathAssets {
static const String iconGoogle = 'assets/icons/icon-google.png';
static const String icon1 = 'assets/icons/icon-1.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 iconCat = 'assets/icons/icon-cat.png';
static const String iconProfile = 'assets/icons/icon-profile.png';
static const String iconPadlock = 'assets/icons/icon-padlock.png';
static const String iconCard = 'assets/icons/icon-card.png';
static const String iconSetting = 'assets/icons/icon-setting.png';
static const String iconFaqs = 'assets/icons/icon-faqs.png';
static const String iconLogout = 'assets/icons/icon-logout.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 iconGadgetOutline =
'assets/icons/icon-gadget-outline.png';
/// IMAGE
static const String imgSplashLogo = 'assets/images/splash-logo.png';
@ -22,4 +85,50 @@ class PathAssets {
static const String imgKtpCropped = 'assets/images/img-ktp-cropped.png';
static const String imgKtpClear = 'assets/images/img-ktp-clear.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 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 imgDashboardProfile =
'assets/images/img-dashboard-profile.png';
static const String imgEmptyTransaction =
'assets/images/img-empty-transaction.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,
};
}

View 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)
),
);
}
}

View File

@ -9,13 +9,14 @@ class ButtonView extends StatelessWidget {
final double? height, width, widthSuffix, widthPrefix, marginVertical;
final EdgeInsetsGeometry? contentPadding;
final bool isSecondaryColor, isOutlined, heightWrapContent, disabled;
final Color? backgroundColor, textColor;
final Color? backgroundColor, textColor, borderColor, disabledBgColor;
final MainAxisAlignment? mainAxisAlignmentContent;
// final _widthBtn = SizeConfig.screenWidth / 1.5;
final _widthBtn = SizeConfig.width * .9;
// final _heightBtn = SizeConfig.screenHeight / 12;
final _heightBtn = SizeConfig.height * .07;
final FontWeight textWeight;
final TextAlign textAlign;
final double? textSize, sizeBorderRadius;
final int? maxLines;
@ -31,9 +32,12 @@ class ButtonView extends StatelessWidget {
this.width,
this.contentPadding,
this.backgroundColor,
this.borderColor,
this.textColor,
this.disabledBgColor,
this.textWeight = FontWeight.bold,
this.textSize,
this.textAlign = TextAlign.center,
this.mainAxisAlignmentContent,
this.disabled = false,
this.heightWrapContent = false,
@ -55,78 +59,80 @@ class ButtonView extends StatelessWidget {
final widthPrefix =
this.widthPrefix ?? (heightWrapContent ? width! / 4.7 : _widthBtn / 16);
return Container(
margin: EdgeInsets.symmetric(vertical: marginVertical ?? 32.0),
width: width ?? _widthBtn,
height: heightWrapContent ? null : height ?? _heightBtn,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
disabledBackgroundColor: isOutlined ? Colors.white : color.surface,
padding: contentPadding,
backgroundColor: backgroundColor ??
(isOutlined
? Colors.white
: isSecondaryColor
? ColorPalette.grey
: ColorPalette.primary),
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(sizeBorderRadius ?? 48),
side: isOutlined
? BorderSide(
color: disabled
? color.surface
: isSecondaryColor
? ColorPalette.greyBorder
: ColorPalette.primary,
)
: BorderSide.none,
return Center(
child: Container(
margin: EdgeInsets.symmetric(vertical: marginVertical ?? 24.0),
width: width ?? _widthBtn,
height: heightWrapContent ? null : height ?? _heightBtn,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
disabledBackgroundColor: disabledBgColor ?? (isOutlined ? Colors.white : color.surface),
padding: contentPadding,
backgroundColor: backgroundColor ??
(isOutlined
? Colors.white
: isSecondaryColor
? ColorPalette.grey
: ColorPalette.primary),
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(sizeBorderRadius ?? 48),
side: isOutlined
? BorderSide(
color: borderColor ?? (disabled
? color.surface
: isSecondaryColor
? ColorPalette.greyBorder
: ColorPalette.primary),
)
: BorderSide.none,
),
),
),
onPressed: disabled ? null : onPressed,
child: Row(
mainAxisAlignment: mainAxisAlignmentContent ??
(prefixIcon != null
? MainAxisAlignment.center
: suffixIcon != null
? MainAxisAlignment.end
: MainAxisAlignment.center),
children: [
if (prefixIcon != null) ...[
prefixIcon!,
SizedBox(width: widthPrefix),
] else
Container(),
Flexible(
child: Text(
name,
textAlign: TextAlign.center,
maxLines: maxLines,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: textSize ?? 16,
fontWeight: textWeight,
color: textColor ??
(disabled && isOutlined
? color.primary
: disabled
? Colors.white
: isOutlined && isSecondaryColor
? ColorPalette.blackFont
: isOutlined
? color.primary
: isSecondaryColor
? Colors.white
: Colors.white),
onPressed: disabled ? null : onPressed,
child: Row(
mainAxisAlignment: mainAxisAlignmentContent ??
(prefixIcon != null
? MainAxisAlignment.center
: suffixIcon != null
? MainAxisAlignment.end
: MainAxisAlignment.center),
children: [
if (prefixIcon != null) ...[
prefixIcon!,
SizedBox(width: widthPrefix),
] else
Container(),
Flexible(
child: Text(
name,
textAlign: textAlign,
maxLines: maxLines,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: textSize ?? 16,
fontWeight: textWeight,
color: textColor ??
(disabled && isOutlined
? color.primary
: disabled
? Colors.white
: isOutlined && isSecondaryColor
? ColorPalette.blackFont
: isOutlined
? color.primary
: isSecondaryColor
? Colors.white
: Colors.white),
),
),
),
),
if (suffixIcon != null) ...[
SizedBox(width: widthSuffix),
suffixIcon!
] else
Container()
],
if (suffixIcon != null) ...[
SizedBox(width: widthSuffix),
suffixIcon!
] else
Container()
],
),
),
),
);

View File

@ -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),
),
],
),
],
)),
],
),
),
);
}
}

View File

@ -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,
),
],
),
),
);
}
}

View 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);
}

View 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});
}

View File

@ -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 */
],
);
}
},
),
],
);
},
);
}
}

View File

@ -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,
),
),
],
),
),
),
);
}
}

View File

@ -0,0 +1,70 @@
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 Color? colorTitle;
const ListTileView(
{Key? key,
required this.title,
this.onPressed,
this.prefixIcon,
this.suffixIcon,
this.colorTitle})
: super(key: key);
@override
Widget build(BuildContext context) {
return Container(
width: SizeConfig.width,
padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 8.0),
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(
fontWeight: FontWeight.w600,
color: colorTitle ?? ColorPalette.slate500,
),
),
),
suffixIcon != null
? IconButton(
onPressed: onPressed,
icon: const Icon(
Icons.arrow_forward_ios,
color: ColorPalette.primary,
size: 20,
),
)
: const SizedBox(),
],
),
);
}
}

View 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
),
)
],
);
}
}

View File

@ -0,0 +1,133 @@
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 const Expanded(
child: SizedBox()
);
}
Widget numberWidget(String number) {
return Expanded(
child: GestureDetector(
onTap: () {
onNumberSelected(number);
},
child: Container(
color: Colors.transparent,
padding: EdgeInsets.symmetric(vertical: SizeConfig.height * .028),
child: Text(
number,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: ColorPalette.slate800
),
),
),
)
);
}
Widget removeWidget() {
return Expanded(
child: GestureDetector(
onTap: () {
onNumberSelected('');
},
child: Container(
color: Colors.transparent,
padding: EdgeInsets.symmetric(vertical: SizeConfig.height * .028),
child: const Icon(
Icons.backspace_outlined,
size: 28,
color: ColorPalette.slate800,
),
),
)
);
}
}

View 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,
),
))
],
);
}),
),
);
});
}
}

View 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();
}
}

View 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,
)
)
],
),
);
}
}

View 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(),
)
],
);
}
}

View 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,
),
);
}
}

View 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,
),
],
);
}),
),
);
});
}
}

View File

@ -0,0 +1,9 @@
import 'package:flutter/material.dart';
class SetPinViewModel extends ChangeNotifier {
bool isPinCompleted = false;
void changePin() {
isPinCompleted = !isPinCompleted;
notifyListeners();
}
}

View 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()
);
}
}

View 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,
)
],
),
),
],
),
);
}
}

Some files were not shown because too many files have changed in this diff Show More