yoga #6

Merged
prayogaprajna merged 6 commits from yoga into dev 2024-02-12 17:04:28 +07:00
33 changed files with 1127 additions and 152 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'
@ -51,11 +57,21 @@ android {
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
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 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: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 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: 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: 74 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

View File

@ -10,14 +10,10 @@ 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 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';
@ -28,6 +24,10 @@ class PathAssets {
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';
/// IMAGE
static const String imgSplashLogo = 'assets/images/splash-logo.png';
@ -40,12 +40,20 @@ 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 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';
}

View File

@ -1,21 +1,25 @@
import 'package:cims_apps/application/theme/color_palette.dart';
import 'package:cims_apps/core/utils/size_config.dart';
import 'package:flutter/material.dart';
class ButtonBack extends StatelessWidget {
const ButtonBack({super.key});
final EdgeInsets? margin;
final void Function()? onPress;
const ButtonBack({super.key, this.margin, this.onPress});
@override
Widget build(BuildContext context) {
return SizedBox(
return Container(
margin: margin ?? EdgeInsets.all(0),
width: SizeConfig.width * 0.1,
child: IconButton(
style: IconButton.styleFrom(
backgroundColor: Colors.white,
shape: const CircleBorder()
shape: const CircleBorder(
side: BorderSide(color: ColorPalette.slate200)
)
),
onPressed: () {
Navigator.pop(context);
},
onPressed: onPress ?? () => Navigator.pop(context),
icon: const Icon(Icons.arrow_back)
),
);

View File

@ -33,6 +33,7 @@ class TextFormView extends StatelessWidget {
final Color? disabledborderColor;
final bool? enableInteractiveSelection;
final Color? fontColorDisabled;
final EdgeInsets? contentPadding;
final FocusNode? focusNode;
// ignore: prefer_const_constructors_in_immutables
@ -189,10 +190,11 @@ class TextFormView extends StatelessWidget {
suffixIconConstraints: suffixIconConstraints,
prefixIconConstraints: preffixIconConstraints,
prefix: prefix,
contentPadding: const EdgeInsets.symmetric(
contentPadding: contentPadding ?? const EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 16.0,
)),
)
),
)
],
);

View File

@ -73,13 +73,13 @@ class ColorPalette {
static const Color chathamsBlue = Color(0xFF285BB9);
static const Color background = Color(0xFFDADADA);
static const Color backgroundBlueLight = Color(0xFFEBF3FD);
static const Color slate500 = Color(0xFF64748B);
static const Color blue50 = Color(0xFFEFF6FF);
static const Color blue200 = Color(0xFFBFDBFE);
static const Color slate50 = Color(0xFFF8FAFC);
static const Color slate200 = Color(0xFFE2E8F0);
static const Color slate300 = Color(0xFFCBD5E1);
static const Color slate400 = Color(0xFF94A3B8);
static const Color slate500 = Color(0xFF64748B);
static const Color slate800 = Color(0xFF1E293B);
static const Color purple100 = Color(0xFFEDE9FE);
static const Color purple500 = Color(0xFF8B5CF6);

View File

@ -0,0 +1,96 @@
import 'package:cims_apps/application/assets/path_assets.dart';
import 'package:cims_apps/application/component/button/button_back.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/application/component/text_form/text_form_view.dart';
import 'package:cims_apps/application/component/text_title/text_title.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/login/view/password_view.dart';
import 'package:cims_apps/features/auth/login/view/phone_number_view.dart';
import 'package:cims_apps/features/auth/login/view_model/login_view_model.dart';
import 'package:cims_apps/features/auth/registration/view/risk_profile/risk_profile_view.dart';
import 'package:cims_apps/features/bottom_navigation_view.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
class LoginView extends StatefulWidget {
const LoginView({super.key});
@override
State<LoginView> createState() => _LoginViewState();
}
class _LoginViewState extends State<LoginView> {
int currentPage = 0;
PageController pageController = PageController(initialPage: 0);
TextEditingController phoneNumberController = TextEditingController();
TextEditingController passwordController = TextEditingController();
@override
void dispose() {
// TODO: implement dispose
pageController.dispose();
phoneNumberController.dispose();
passwordController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => LoginViewModel(),
child: WillPopScope(
onWillPop: () async {
if(currentPage == 1){
currentPage--;
pageController.jumpToPage(0);
return false;
}
return true;
},
child: Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
toolbarHeight: 70,
backgroundColor: Colors.white,
surfaceTintColor: Colors.white,
automaticallyImplyLeading: false,
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ButtonBack(),
const Text('Sign In'),
SizedBox(
width: SizeConfig.width * 0.1,
)
],
),
shape: const RoundedRectangleBorder(
side: BorderSide(color: ColorPalette.slate200)
),
),
body: PageView(
physics: NeverScrollableScrollPhysics(),
controller: pageController,
children: [
PhoneNumberView(nextStep: nextStep, controller: phoneNumberController),
PasswordView(nextStep: nextStep, controller: passwordController)
],
),
),
),
);
}
void nextStep() {
if(pageController.page == 0){
currentPage++;
pageController.jumpToPage(1);
}else{
routePush(context, page: RiskProfileView());
}
}
}

View File

@ -0,0 +1,78 @@
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/component/text_title/text_title.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/login/view_model/login_view_model.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class PasswordView extends StatelessWidget {
final void Function() nextStep;
final TextEditingController controller;
const PasswordView({super.key, required this.nextStep, required this.controller});
@override
Widget build(BuildContext context) {
return Consumer<LoginViewModel>(
builder: (context, provider, child) {
return Container(
width: SizeConfig.width,
height: SizeConfig.height,
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const TextTitle(title: 'Enter your password', fontSize: 24),
SizedBox(
height: 24,
),
TextFormView(
name: 'Password',
ctrl: controller,
obscureText: !provider.showPassword,
contentPadding: EdgeInsets.all(12),
suffixIcon: GestureDetector(
onTap: () {
provider.changeShowPassword();
},
child: Icon(
provider.showPassword
? Icons.visibility_outlined
: Icons.visibility_off_outlined,
color: ColorPalette.greyDarker,
),
),
),
TextButton(
style: TextButton.styleFrom(
padding: EdgeInsets.all(0)
),
onPressed: () {
},
child: Text(
'Forget the password?',
style: TextStyle(
color: ColorPalette.primary,
),
)
),
SizedBox(
height: 16,
),
ButtonView(
name: 'Confirm',
heightWrapContent: true,
width: SizeConfig.width,
marginVertical: 0,
contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
onPressed: nextStep,
)
],
),
);
}
);
}
}

View File

@ -0,0 +1,116 @@
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/application/component/text_form/text_form_view.dart';
import 'package:cims_apps/application/component/text_title/text_title.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_view.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class PhoneNumberView extends StatelessWidget {
final void Function() nextStep;
final TextEditingController controller;
const PhoneNumberView({super.key, required this.nextStep, required this.controller});
@override
Widget build(BuildContext context) {
return Container(
width: SizeConfig.width,
height: SizeConfig.height,
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const TextTitle(title: 'Enter your phone number', fontSize: 24),
SizedBox(
height: 24,
),
TextFormView(
name: 'Phone Number',
keyboardType: TextInputType.number,
ctrl: controller,
inputFormatters: [
FilteringTextInputFormatter.deny(RegExp(r'^0'))
],
prefixIcon: Container(
width: SizeConfig.width * .23,
padding:
const EdgeInsets.symmetric(horizontal: 16.0),
margin: const EdgeInsets.only(right: 16),
decoration: const BoxDecoration(
color: ColorPalette.grey,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(8),
bottomLeft: Radius.circular(8),
)),
child: const Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ImageView(
image: PathAssets.iconFlag,
fit: BoxFit.contain,
width: 24,
height: 24,
),
Text(
'+62',
style: TextStyle(
fontWeight: FontWeight.w600,
color: ColorPalette.slate800,
),
)
],
)),
validator: (value) {
if (value!.isEmpty) {
return 'Phone number must be filled';
} else {
return null;
}
},
),
SizedBox(
height: 32,
),
ButtonView(
name: 'Next',
heightWrapContent: true,
width: SizeConfig.width,
marginVertical: 0,
contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
onPressed: nextStep,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Don't have an account yet?",
style: TextStyle(
color: ColorPalette.slate500,
),
),
TextButton(
onPressed: () {
routePush(context, page: RegistrationView());
},
style: TextButton.styleFrom(
padding: EdgeInsets.all(0)
),
child: Text(
'Sign Up',
style: TextStyle(
fontWeight: FontWeight.w600,
color: ColorPalette.primary
),
)
)
],
)
],
),
);
}
}

View File

@ -0,0 +1,10 @@
import 'package:flutter/foundation.dart';
class LoginViewModel extends ChangeNotifier {
bool showPassword = false;
void changeShowPassword() {
showPassword = !showPassword;
notifyListeners();
}
}

View File

@ -0,0 +1,164 @@
import 'package:cims_apps/application/assets/path_assets.dart';
import 'package:cims_apps/application/component/button/button_back.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/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/risk_profile/results_view.dart';
import 'package:cims_apps/features/auth/registration/view/risk_profile/risk_profile_view.dart';
import 'package:cims_apps/features/auth/registration/view/risk_profile/risk_profile_view_model/risk_profile_view_model.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class QuestionView extends StatefulWidget {
const QuestionView({super.key});
@override
State<QuestionView> createState() => _QuestionViewState();
}
class _QuestionViewState extends State<QuestionView> {
int currentPage = 0;
PageController pageController = PageController();
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => RiskProfileViewModel(),
child: Consumer<RiskProfileViewModel>(
builder: (context, provider, child) {
return Scaffold(
appBar: AppBar(
toolbarHeight: 70,
backgroundColor: Colors.white,
surfaceTintColor: Colors.white,
automaticallyImplyLeading: false,
centerTitle: true,
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ButtonBack(),
const Text('Risk Profile', textAlign: TextAlign.center),
SizedBox(
width: SizeConfig.width * 0.1
)
],
),
shape: const RoundedRectangleBorder(
side: BorderSide(color: ColorPalette.slate200)
),
),
body: PageView(
controller: pageController,
physics: NeverScrollableScrollPhysics(),
children: provider.listRiskProfileQuestion.asMap().entries.map((e) {
return containerQuestion(
e.value,
e.key,
(index, score) => provider.selectedAnswer(index, score)
);
}).toList(),
),
bottomNavigationBar: SizedBox(
height: 84,
child: ButtonView(
name: 'Next',
marginVertical: 16,
onPressed: () {
if(currentPage > 3){
int totalScore = provider.listScore.reduce((value, element) => value + element);
provider.setTypeResult(totalScore);
routePush(context, page: ResultsView(totalScore: totalScore.toString(), typeResult: provider.typeResult));
}else{
setState(() {
currentPage += 1;
});
pageController.nextPage(duration: Duration(milliseconds: 300), curve: Curves.ease);
}
},
),
),
);
}
),
);
}
Widget containerQuestion(RiskProfileQuestion data, int index, void Function(int index, int score) onTapAnswers) {
return SizedBox(
height: SizeConfig.height,
width: SizeConfig.width,
child: ListView(
padding: EdgeInsets.all(24),
children: [
ImageView(
image: data.img,
fit: BoxFit.fitWidth,
),
SizedBox(
height: 16,
),
Text(
'${index + 1} of 5 question',
style: TextStyle(
fontWeight: FontWeight.bold,
color: ColorPalette.primary
),
),
SizedBox(
height: 12,
),
Text(
data.question,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 24,
color: ColorPalette.slate800
),
),
SizedBox(
height: 16,
),
Column(
children: data.answers.asMap().entries.map((e) {
bool selected = data.selectedScore == e.value.score;
return GestureDetector(
onTap: () {
onTapAnswers(index, e.value.score);
},
child: AnimatedContainer(
duration: Duration(milliseconds: 300),
margin: EdgeInsets.only(top: 12),
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
border: Border.all(color: selected ? ColorPalette.primary : ColorPalette.slate200),
borderRadius: BorderRadius.circular(8)
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
e.value.title,
style: TextStyle(
fontSize: 16,
color: selected ? ColorPalette.primary : ColorPalette.slate500,
fontWeight: FontWeight.w500
),
),
),
if(selected)...[
Icon(Icons.check_circle_rounded, color: ColorPalette.primary)
]
],
),
),
);
}).toList(),
)
],
),
);
}
}

View File

@ -0,0 +1,187 @@
import 'package:cims_apps/application/assets/path_assets.dart';
import 'package:cims_apps/application/component/button/button_back.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/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:cims_apps/features/auth/registration/view/risk_profile/risk_profile_view_model/risk_profile_view_model.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class ResultsView extends StatelessWidget {
final String totalScore;
final RiskProfileResult typeResult;
const ResultsView({super.key, required this.typeResult, required this.totalScore});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
toolbarHeight: 70,
backgroundColor: Colors.white,
surfaceTintColor: Colors.white,
automaticallyImplyLeading: false,
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ButtonBack(),
const Text('Risk Profile', textAlign: TextAlign.center),
SizedBox(
width: SizeConfig.width * 0.1
)
],
),
shape: const RoundedRectangleBorder(
side: BorderSide(color: ColorPalette.slate200)
),
),
body: SingleChildScrollView(
padding: EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Container(
decoration: BoxDecoration(
color: typeResult.color,
image: DecorationImage(image: AssetImage(typeResult.img), alignment: Alignment.centerRight)
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
padding: EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
typeResult.type,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 24,
color: ColorPalette.white
),
),
SizedBox(height: 16,),
Text('Total Score :',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: ColorPalette.white
),
),
Text(totalScore,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 28,
color: ColorPalette.white
),
)
],
),
),
],
),
),
),
SizedBox(
height: 24,
),
Text(
typeResult.desc,
style: TextStyle(
color: ColorPalette.slate500,
fontSize: 16
)
),
SizedBox(
height: 24,
),
Text(
'Suitable Product',
style: TextStyle(
color: ColorPalette.slate800,
fontWeight: FontWeight.bold,
fontSize: 16
),
),
SizedBox(
height: 16,
),
Wrap(
runSpacing: 16,
children: typeResult.suitableProduct.map((e) {
return Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6),
border: Border.all(color: ColorPalette.slate200),
),
child: Row(
children: [
Container(
padding: EdgeInsets.all(8),
alignment: Alignment.center,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: typeResult.color.withOpacity(0.1)
),
child: Image.asset(e['icon'], width: SizeConfig.width * 0.07, color: typeResult.color)
),
SizedBox(
width: 12,
),
Expanded(
child: Text(e['desc'],
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: ColorPalette.slate800
),
),
)
],
),
);
}).toList(),
),
SizedBox(
height: 32,
),
ButtonView(
name: 'Re-test',
onPressed: () {
},
marginVertical: 0,
backgroundColor: ColorPalette.white,
textColor: ColorPalette.primary,
borderColor: ColorPalette.primary,
isOutlined: true,
textSize: 16,
heightWrapContent: true,
contentPadding: EdgeInsets.all(16),
width: SizeConfig.width,
),
SizedBox(
height: 16,
),
ButtonView(
name: 'Confirm',
onPressed: () {
routePush(context, page: DialogSuccess());
},
marginVertical: 0,
textSize: 16,
heightWrapContent: true,
contentPadding: EdgeInsets.all(16),
width: SizeConfig.width,
)
],
),
),
);
}
}

View File

@ -0,0 +1,112 @@
import 'package:cims_apps/application/assets/path_assets.dart';
import 'package:cims_apps/application/component/button/button_back.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/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/risk_profile/question_view.dart';
import 'package:flutter/material.dart';
class RiskProfileView extends StatelessWidget {
const RiskProfileView({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
toolbarHeight: 70,
backgroundColor: Colors.white,
surfaceTintColor: Colors.white,
automaticallyImplyLeading: false,
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ButtonBack(),
const Text('Risk Profile', textAlign: TextAlign.center),
SizedBox(
width: SizeConfig.width * 0.1
)
],
),
shape: const RoundedRectangleBorder(
side: BorderSide(color: ColorPalette.slate200)
),
),
body: Container(
width: SizeConfig.width,
height: SizeConfig.height,
padding: EdgeInsets.all(24),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
children: [
ImageView(image: PathAssets.imgDataReport),
SizedBox(
height: 24,
),
Text(
'Know Your Risk Profile',
textAlign: TextAlign.center,
style: TextStyle(
color: ColorPalette.slate800,
fontWeight: FontWeight.bold,
fontSize: 24
),
),
SizedBox(
height: 12,
),
Text(
'We will provide recommendations that match your profile and risk tolerance level.',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: ColorPalette.slate500
),
),
],
),
Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ImageView(
image: PathAssets.iconShield,
width: 20,
height: 22,
),
SizedBox(
width: 8,
),
Text(
'Your data is secure and encrypted',
style: TextStyle(
fontWeight: FontWeight.w600,
color: ColorPalette.primary,
fontSize: 16
),
)
],
),
SizedBox(
height: 24,
),
ButtonView(
name: "Let's Start",
onPressed: () {
routePush(context, page: QuestionView());
},
marginVertical: 0,
)
],
)
],
),
),
);
}
}

View File

@ -0,0 +1,133 @@
import 'package:cims_apps/application/assets/path_assets.dart';
import 'package:cims_apps/application/theme/color_palette.dart';
import 'package:flutter/material.dart';
class RiskProfileQuestion {
String img;
int selectedScore;
String question;
List<Answer> answers;
RiskProfileQuestion(this.img, this.question, this.selectedScore, this.answers);
}
class RiskProfileResult {
String type;
String img;
String desc;
Color color;
List<dynamic> suitableProduct;
RiskProfileResult(this.type, this.img, this.color, this.desc, this.suitableProduct);
}
class Answer {
int score;
String title;
Answer(this.score, this.title);
}
class RiskProfileViewModel extends ChangeNotifier {
List<int> listScore = [0,0,0,0,0];
RiskProfileResult typeResult = RiskProfileResult('', '', Colors.transparent, '', []);
List<RiskProfileQuestion> listRiskProfileQuestion = [
RiskProfileQuestion(
PathAssets.imgGrowing, 'How long is your planned investment horizon?', 0,
[
Answer(2, '<1 year'),
Answer(4, '1-3 year'),
Answer(6, '3-5 year'),
Answer(8, '>5 year')
]
),
RiskProfileQuestion(
PathAssets.imgLeader, 'Investment objectives of mutualfund investors?', 0,
[
Answer(2, 'Security of Investment Funds'),
Answer(4, 'Investment Income and Security'),
Answer(6, 'Revenue and growth over the long term'),
Answer(8, 'Growth')
]
),
RiskProfileQuestion(
PathAssets.imgBusinessFailure, 'How much can I afford to lose on my investment (Risk level you can afford)?', 0,
[
Answer(2, '0%'),
Answer(4, '<25%'),
Answer(6, '26-50%'),
Answer(8, '>50%')
]
),
RiskProfileQuestion(
PathAssets.imgMoneyIncome, 'I am willing to use .... % of my income for investment (financial circumstances of the financier)?', 0,
[
Answer(2, '0%'),
Answer(4, '<25%'),
Answer(6, '26-50%'),
Answer(8, '>50%')
]
),
RiskProfileQuestion(
PathAssets.imgDataAnalysis, 'Mutual Fund Investment Knowledge Level?', 0,
[
Answer(2, 'Low'),
Answer(4, 'Medium'),
Answer(6, 'High'),
]
)
];
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},
]
)
];
void selectedAnswer(int index, int score) {
listScore[index] = score;
listRiskProfileQuestion[index].selectedScore = score;
notifyListeners();
}
void setTypeResult(int totalScore) {
if(totalScore <= 25){
typeResult = listRiskProfileResult[0];
}else if(totalScore <= 50){
typeResult = listRiskProfileResult[1];
}else{
typeResult = listRiskProfileResult[2];
}
notifyListeners();
}
}

View File

@ -1,10 +1,13 @@
import 'package:carousel_slider/carousel_slider.dart';
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/application/component/text_title/text_title.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/initial_registration_step.dart';
import 'package:cims_apps/features/auth/registration/view/registration_view.dart';
import 'package:cims_apps/features/dashboard/dashboard_account/view/invest_type/invest_type_view.dart';
import 'package:flutter/material.dart';
@ -47,7 +50,7 @@ class _HomeViewState extends State<HomeView> {
InvestType('Sharia', PathAssets.iconPortofolioSharia)
];
StepVerification listStepVerification = StepVerification(1, [
StepVerification listStepVerification = StepVerification(0, [
'Registration', 'Verification', 'Complete'
]);
@ -268,6 +271,7 @@ class _HomeViewState extends State<HomeView> {
const SizedBox(
height: 24,
),
if(listStepVerification.currentStep == 1)...[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
@ -368,6 +372,38 @@ class _HomeViewState extends State<HomeView> {
],
),
)
]else if(listStepVerification.currentStep == 0)...[
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: ColorPalette.blue50,
borderRadius: BorderRadius.circular(12)
),
child: Column(
children: [
Text(
"Let's start registering your data to start mutual fund investment at PT Gemilang Indonesia",
style: TextStyle(
color: ColorPalette.slate500
),
),
SizedBox(
height: 16,
),
ButtonView(
name: 'Registration',
width: SizeConfig.width,
marginVertical: 0,
heightWrapContent: true,
contentPadding: EdgeInsets.all(12),
onPressed: () {
routePush(context, page: InitialRegistrationStep());
},
)
],
),
)
]
],
),
);
@ -515,9 +551,15 @@ class _HomeViewState extends State<HomeView> {
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30),
color: ColorPalette.colorSwitchButtonActive
color: ColorPalette.green100
),
child: Text(
article.type,
style: TextStyle(
fontWeight: FontWeight.w600,
color: ColorPalette.green500
),
),
child: Text(article.type),
),
],
)

View File

@ -1,3 +1,5 @@
import 'dart:math';
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';
@ -5,11 +7,12 @@ import 'package:cims_apps/core/utils/size_config.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
class CategoryPortofolio {
class InvestmentType {
String name;
int value;
int mutualFunds;
CategoryPortofolio(this.value, this.name);
InvestmentType(this.value, this.name, this.mutualFunds);
}
class PortofolioView extends StatefulWidget {
@ -36,11 +39,11 @@ class _PortofolioViewState extends State<PortofolioView> {
bool seePortofolioValue = false;
int touchedIndex = -1;
List<CategoryPortofolio> listCategoryPortofolio = [
CategoryPortofolio(20, 'Money Market'),
CategoryPortofolio(15, 'Shares'),
CategoryPortofolio(8, 'Bonds'),
CategoryPortofolio(50, 'Sharia'),
List<InvestmentType> listInvestmentType = [
InvestmentType(20, 'Money Market', 2),
InvestmentType(15, 'Shares', 5),
InvestmentType(8, 'Bonds', 3),
InvestmentType(50, 'Sharia', 4),
];
List<String> assetsImage = [
@ -91,7 +94,7 @@ class _PortofolioViewState extends State<PortofolioView> {
topRight: Radius.circular(24)),
),
child: ListView(
padding: const EdgeInsets.symmetric(vertical: 0),
padding: const EdgeInsets.symmetric(vertical: 16),
children: [
Stack(alignment: Alignment.center, children: [
AspectRatio(
@ -117,16 +120,25 @@ class _PortofolioViewState extends State<PortofolioView> {
show: true,
),
sectionsSpace: 20,
centerSpaceRadius: 100,
centerSpaceRadius: 110,
sections: sectionsDataPortofolio())),
),
const Column(
Column(
children: [
Text('Total Mutual Fund'),
Text('10',
const Text('Total Mutual Fund',
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 18,
color: ColorPalette.slate400
),
),
Text(listInvestmentType.map((e) => e.mutualFunds).reduce((value, element) => value + element).toString(),
style: const TextStyle(
fontSize: 44,
fontWeight: FontWeight.w700)),
fontWeight: FontWeight.w700
)
)
,
],
)
]),
@ -138,9 +150,6 @@ class _PortofolioViewState extends State<PortofolioView> {
height: 24,
),
...listColumnPortofolio(),
const SizedBox(
height: 24,
)
],
),
),
@ -223,7 +232,7 @@ class _PortofolioViewState extends State<PortofolioView> {
}
List<PieChartSectionData> sectionsDataPortofolio() {
return listCategoryPortofolio.asMap().entries.map((e) {
return listInvestmentType.asMap().entries.map((e) {
final isTouched = e.key == touchedIndex;
final radius = isTouched ? 40.0 : 30.0;
return PieChartSectionData(
@ -256,7 +265,7 @@ class _PortofolioViewState extends State<PortofolioView> {
child: Wrap(
alignment: WrapAlignment.center,
spacing: 12,
children: listCategoryPortofolio.asMap().entries.map((e) {
children: listInvestmentType.asMap().entries.map((e) {
return Container(
color: Colors.white,
width: SizeConfig.width * 0.18,
@ -294,7 +303,7 @@ class _PortofolioViewState extends State<PortofolioView> {
}
List<Widget> listColumnPortofolio() {
return listCategoryPortofolio.asMap().entries.map((e) {
return listInvestmentType.asMap().entries.map((e) {
return Column(
children: [
if (e.key > 0) ...[
@ -316,7 +325,7 @@ class _PortofolioViewState extends State<PortofolioView> {
style: const TextStyle(fontWeight: FontWeight.w600),
),
subtitle: Text(
e.value.name,
'${e.value.mutualFunds} Mutual Funds',
style: const TextStyle(
fontWeight: FontWeight.w600, color: Color(0xff94A3B8)),
),

View File

@ -124,22 +124,13 @@ class _ProductChartViewState extends State<ProductChartView> {
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 48),
padding: const EdgeInsets.only(top: 48, bottom: 24),
child: AspectRatio(
aspectRatio: 2.5,
child: LayoutBuilder(
builder: (context, constraints) {
return LineChart(
LineChartData(
// showingTooltipIndicators: showingTooltipOnSpots.map((index) {
// return ShowingTooltipIndicators([
// LineBarSpot(
// tooltipsOnBar,
// lineBarsData.indexOf(tooltipsOnBar),
// tooltipsOnBar.spots[index],
// ),
// ]);
// }).toList(),
extraLinesData: ExtraLinesData(
verticalLines: [
VerticalLine(

View File

@ -99,8 +99,6 @@ class _ProductViewState extends State<ProductView> {
Widget build(BuildContext context) {
return Scaffold(
body: SizedBox(
width: SizeConfig.width,
height: SizeConfig.height,
child: Stack(
children: [
const ImageView(image: PathAssets.imgDashboardAccount),
@ -141,7 +139,8 @@ class _ProductViewState extends State<ProductView> {
],
),
),
bottomNavigationBar: Padding(
bottomNavigationBar: Container(
height: SizeConfig.height * .1,
padding: const EdgeInsets.symmetric(horizontal: 24),
child: ButtonView(
name: 'Buy',
@ -275,7 +274,7 @@ class _ProductViewState extends State<ProductView> {
),
cardInformation('Time Machine', timeMachine()),
const SizedBox(
height: 24,
height: 32,
),
topFiveHoldings(),
const SizedBox(
@ -498,6 +497,9 @@ class _ProductViewState extends State<ProductView> {
)
],
),
SizedBox(
height: 16,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
@ -523,11 +525,12 @@ class _ProductViewState extends State<ProductView> {
onPressed: () {
},
prefixIcon: Icon(Icons.space_dashboard_sharp),
prefixIcon: Icon(Icons.calendar_month_rounded),
backgroundColor: ColorPalette.blue50,
sizeBorderRadius: 8,
isSecondaryColor: false,
width: SizeConfig.width * .5,
marginVertical: 0,
heightWrapContent: true,
isOutlined: true,
widthPrefix: 10,
@ -552,6 +555,7 @@ class _ProductViewState extends State<ProductView> {
heightWrapContent: true,
backgroundColor: ColorPalette.orange50,
sizeBorderRadius: 8,
marginVertical: 0,
isOutlined: true,
borderColor: ColorPalette.orange500,
textSize: 14,
@ -590,6 +594,8 @@ class _ProductViewState extends State<ProductView> {
TextFormView(
name: '',
ctrl: machineController,
keyboardType: TextInputType.number,
contentPadding: EdgeInsets.all(12),
onChanged: (value) {
value = value.replaceAll('Rp ', '').replaceAll('.', '');
double parseValue = double.parse(value);
@ -600,7 +606,6 @@ class _ProductViewState extends State<ProductView> {
}
setEstimatedValue();
},
keyboardType: TextInputType.number,
),
const Padding(
padding: EdgeInsets.only(top: 16, bottom: 8),

View File

@ -4,6 +4,7 @@ import 'package:cims_apps/application/component/image/image_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:cims_apps/features/auth/login/view/login_view.dart';
import 'package:cims_apps/features/auth/registration/view/registration_view.dart';
import 'package:cims_apps/features/bottom_navigation_view.dart';
import 'package:flutter/material.dart';
@ -70,7 +71,7 @@ class DashboardPublicView extends StatelessWidget {
width: SizeConfig.width * .43,
height: SizeConfig.height * .06,
onPressed: () {
routePush(context, page: const BottomNavigationView());
routePush(context, page: const LoginView());
},
),
ButtonView(

View File

@ -17,7 +17,7 @@ class _SplashScreenState extends State<SplashScreen> {
@override
void initState() {
Future.delayed(const Duration(seconds: 3)).then(
(value) => routePush(context, page: const DashboardPublicView()),
(value) => routePush(context, page: const DashboardPublicView(), routeType: RouteType.pushRemove),
);
super.initState();
}

View File

@ -28,6 +28,7 @@ class MyApp extends StatelessWidget {
titleTextStyle: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w700,
fontFamily: 'Manrope',
color: ColorPalette.slate800,
)),
fontFamily: 'Manrope',