diff --git a/android/app/build.gradle b/android/app/build.gradle index 768a80a..c2d4e08 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -45,7 +45,7 @@ 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 diff --git a/assets/icons/icon-ktp1.png b/assets/icons/icon-ktp1.png new file mode 100644 index 0000000..1bddb2e Binary files /dev/null and b/assets/icons/icon-ktp1.png differ diff --git a/assets/icons/icon-ktp2.png b/assets/icons/icon-ktp2.png new file mode 100644 index 0000000..6410a66 Binary files /dev/null and b/assets/icons/icon-ktp2.png differ diff --git a/assets/icons/icon-ktp3.png b/assets/icons/icon-ktp3.png new file mode 100644 index 0000000..a8ffe6e Binary files /dev/null and b/assets/icons/icon-ktp3.png differ diff --git a/assets/icons/icon-ktp4.png b/assets/icons/icon-ktp4.png new file mode 100644 index 0000000..de968bd Binary files /dev/null and b/assets/icons/icon-ktp4.png differ diff --git a/assets/icons/icon-selfie1.png b/assets/icons/icon-selfie1.png new file mode 100644 index 0000000..dfd6ee4 Binary files /dev/null and b/assets/icons/icon-selfie1.png differ diff --git a/assets/icons/icon-selfie2.png b/assets/icons/icon-selfie2.png new file mode 100644 index 0000000..de968bd Binary files /dev/null and b/assets/icons/icon-selfie2.png differ diff --git a/assets/icons/icon-selfie3.png b/assets/icons/icon-selfie3.png new file mode 100644 index 0000000..d5ba125 Binary files /dev/null and b/assets/icons/icon-selfie3.png differ diff --git a/assets/icons/icon-selfie4.png b/assets/icons/icon-selfie4.png new file mode 100644 index 0000000..1bddb2e Binary files /dev/null and b/assets/icons/icon-selfie4.png differ diff --git a/assets/images/img-bg-photo-ktp.png b/assets/images/img-bg-photo-ktp.png new file mode 100644 index 0000000..ebe30f2 Binary files /dev/null and b/assets/images/img-bg-photo-ktp.png differ diff --git a/assets/images/img-bg-photo-selfie.png b/assets/images/img-bg-photo-selfie.png new file mode 100644 index 0000000..e907616 Binary files /dev/null and b/assets/images/img-bg-photo-selfie.png differ diff --git a/lib/application/assets/path_assets.dart b/lib/application/assets/path_assets.dart index e86ba76..22f6b7d 100644 --- a/lib/application/assets/path_assets.dart +++ b/lib/application/assets/path_assets.dart @@ -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,12 +10,24 @@ 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'; + 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'; /// IMAGE static const String imgSplashLogo = 'assets/images/splash-logo.png'; @@ -28,9 +40,12 @@ 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'; } diff --git a/lib/application/component/take_picture_screen/take_picture_screen.dart b/lib/application/component/take_picture_screen/take_picture_screen.dart new file mode 100644 index 0000000..a659c32 --- /dev/null +++ b/lib/application/component/take_picture_screen/take_picture_screen.dart @@ -0,0 +1,140 @@ +import 'package:camera/camera.dart'; +import 'package:cims_apps/application/assets/path_assets.dart'; +import 'package:cims_apps/application/component/image/image_view.dart'; +import 'package:cims_apps/application/component/take_picture_screen/DisplayPictureScreen.dart'; +import 'package:cims_apps/core/route/route.dart'; +import 'package:cims_apps/core/utils/size_config.dart'; +import 'package:flutter/material.dart'; + +class TakePictureScreen extends StatefulWidget { + const TakePictureScreen({ + super.key, + required this.camera, + required this.takeContent, + }); + + final CameraDescription camera; + final String takeContent; + + @override + TakePictureScreenState createState() => TakePictureScreenState(); +} + +class TakePictureScreenState extends State { + late CameraController _controller; + late Future _initializeControllerFuture; + bool isFlash = false; + late String _takeContent; + + Future changeFlash() async { + await _controller.setFlashMode(FlashMode.auto); + setState(() { + isFlash = !isFlash; + }); + } + + @override + void initState() { + super.initState(); + _controller = CameraController( + widget.camera, + ResolutionPreset.medium, + enableAudio: false, + ); + // Next, initialize the controller. This returns a Future. + _initializeControllerFuture = _controller.initialize(); + _takeContent = widget.takeContent; + } + + @override + void dispose() { + // Dispose of the controller when the widget is disposed. + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + // Fill this out in the next steps. + return Scaffold( + appBar: AppBar( + title: const Text('Registration'), + actions: [ + IconButton( + onPressed: () { + changeFlash(); + }, + icon: Icon(isFlash ? Icons.flash_on : Icons.flash_off)) + ], + ), + body: FutureBuilder( + future: _initializeControllerFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + // If the Future is complete, display the preview. + return Stack( + children: [ + SizedBox( + width: SizeConfig.width, + height: SizeConfig.height, + child: CameraPreview(_controller)), + Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.transparent, + Colors.black.withOpacity(0.5), + ], + ), + ), + child: ImageView( + image: _takeContent == 'ktp' + ? PathAssets.imgBgKtp + : PathAssets.imgBgSelfie, + width: SizeConfig.width, + height: SizeConfig.height, + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: IconButton( + onPressed: () async { + try { + // Ensure that the camera is initialized. + await _initializeControllerFuture; + + // Attempt to take a picture and get the file `image` + // where it was saved. + final image = await _controller.takePicture(); + + if (!mounted) return; + routePush(context, + page: DisplayPictureScreen( + imagePath: image.path, + content: _takeContent, + ), + routeType: RouteType.pushReplace); + } catch (e) { + // If an error occurs, log the error to the console. + debugPrint(e.toString()); + } + }, + icon: Icon( + Icons.album_outlined, + color: Colors.white, + size: SizeConfig.width * .16, + ), + ), + ) + ], + ); + } else { + return const Center(child: CircularProgressIndicator()); + } + }, + ), + ); + } +} diff --git a/lib/application/component/text_form/text_form_view.dart b/lib/application/component/text_form/text_form_view.dart index 8617893..96668ba 100644 --- a/lib/application/component/text_form/text_form_view.dart +++ b/lib/application/component/text_form/text_form_view.dart @@ -127,7 +127,7 @@ class TextFormView extends StatelessWidget { initialValue: initialValue, enabled: enabled, controller: ctrl, - // maxLength: maxLength, + maxLength: maxLength, keyboardType: keyboardType, onTap: onTap, onEditingComplete: onSubmit, @@ -189,7 +189,10 @@ class TextFormView extends StatelessWidget { suffixIconConstraints: suffixIconConstraints, prefixIconConstraints: preffixIconConstraints, prefix: prefix, - contentPadding: EdgeInsets.zero), + contentPadding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 16.0, + )), ) ], ); diff --git a/lib/application/theme/color_palette.dart b/lib/application/theme/color_palette.dart index e4d255b..6e37bd2 100644 --- a/lib/application/theme/color_palette.dart +++ b/lib/application/theme/color_palette.dart @@ -80,7 +80,6 @@ class ColorPalette { 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); @@ -93,7 +92,6 @@ class ColorPalette { static const Color green400 = Color(0xFF4ADE80); static const Color green500 = Color(0xFF16A34A); - static const Map investTypeColor = { 'Money Market': purple500, 'Shares': orange500, diff --git a/lib/features/auth/registration/view/submission_data/initial_take_photo.dart b/lib/features/auth/registration/view/submission_data/initial_take_photo.dart index 10f20a2..485f65a 100644 --- a/lib/features/auth/registration/view/submission_data/initial_take_photo.dart +++ b/lib/features/auth/registration/view/submission_data/initial_take_photo.dart @@ -1,10 +1,14 @@ 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/take_picture_screen/take_picture_screen.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/viewmodel/submission_data_viewmodel.dart'; import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; class InitialTakePhoto extends StatelessWidget { const InitialTakePhoto({Key? key}) : super(key: key); @@ -17,75 +21,94 @@ class InitialTakePhoto extends StatelessWidget { {'urlImg': PathAssets.imgKtpCropped, 'tag': 'Cropped Photo'}, {'urlImg': PathAssets.imgKtpClear, 'tag': 'Clear Photo'}, ]; - return SizedBox( - height: SizeConfig.height * .75, - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const TextCaption( - title: 'Take a photo your ID card', - subtitle: - 'Make sure your photo is clearly legible for identity verification purposes', + return MultiProvider( + providers: [ + ChangeNotifierProvider( + create: (context) => SubmissionDataViewModel(), ), - SizedBox( - width: SizeConfig.height, - child: Wrap( - alignment: WrapAlignment.spaceBetween, - spacing: 10, - runSpacing: 10, - children: List.generate(listImg.length, (index) { - final urlList = listImg[index]['urlImg']; - final tag = listImg[index]['tag']; - return Column( - children: [ - ImageView( - image: urlList, - width: SizeConfig.width * .42, - ), - const SizedBox( - height: 8, - ), - Text( - tag, - style: const TextStyle( - color: ColorPalette.slate800, - fontWeight: FontWeight.w600), - ), - ], - ); - }), - ), - ), - // const Spacer(), - const Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - ImageView( - image: PathAssets.iconShield, - width: 20, - height: 22, - ), - SizedBox( - width: 8, - ), - Expanded( - child: Text( - 'In accordance with OJK regulations, an ID card is required to purchase mutual funds.', - style: TextStyle( - fontWeight: FontWeight.w600, - color: ColorPalette.primary, + ], + builder: (context, child) { + return SizedBox( + height: SizeConfig.height * .75, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const TextCaption( + title: 'Take a photo your ID card', + subtitle: + 'Make sure your photo is clearly legible for identity verification purposes', + ), + SizedBox( + width: SizeConfig.height, + child: Wrap( + alignment: WrapAlignment.spaceBetween, + spacing: 10, + runSpacing: 10, + children: List.generate(listImg.length, (index) { + final urlList = listImg[index]['urlImg']; + final tag = listImg[index]['tag']; + return Column( + children: [ + ImageView( + image: urlList, + width: SizeConfig.width * .42, + ), + const SizedBox( + height: 8, + ), + Text( + tag, + style: const TextStyle( + color: ColorPalette.slate800, + fontWeight: FontWeight.w600), + ), + ], + ); + }), ), ), - ) - ], - ), - ButtonView( - name: 'Take a Photo', - marginVertical: 16.0, - onPressed: () {}, - ) - ], - ), - ); + // const Spacer(), + const Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + ImageView( + image: PathAssets.iconShield, + width: 20, + height: 22, + ), + SizedBox( + width: 8, + ), + Expanded( + child: Text( + 'In accordance with OJK regulations, an ID card is required to purchase mutual funds.', + style: TextStyle( + fontWeight: FontWeight.w600, + color: ColorPalette.primary, + ), + ), + ) + ], + ), + Consumer( + builder: (context, provider, child) { + return ButtonView( + name: 'Take a Photo', + marginVertical: 16.0, + onPressed: () { + provider.initCamera().then((cameras) { + routePush(context, + page: TakePictureScreen( + camera: cameras.first, + takeContent: 'selfie', + )); + }); + }, + ); + }) + ], + ), + ); + }); } } diff --git a/lib/features/auth/registration/viewmodel/submission_data_viewmodel.dart b/lib/features/auth/registration/viewmodel/submission_data_viewmodel.dart index c7a97fb..dd5cf62 100644 --- a/lib/features/auth/registration/viewmodel/submission_data_viewmodel.dart +++ b/lib/features/auth/registration/viewmodel/submission_data_viewmodel.dart @@ -1,3 +1,4 @@ +import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; class SubmissionDataViewModel extends ChangeNotifier { @@ -6,6 +7,12 @@ class SubmissionDataViewModel extends ChangeNotifier { bool _isEmailVerify = false; bool get isEmailVerify => _isEmailVerify; + Future> initCamera() async { + final cameras = await availableCameras(); + final camerasDesc = cameras; + return camerasDesc; + } + submitEmail() { _isEmailVerify = !_isEmailVerify; notifyListeners(); diff --git a/pubspec.lock b/pubspec.lock index 18050bf..fe9bc01 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -49,6 +49,46 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + camera: + dependency: "direct main" + description: + name: camera + sha256: "9499cbc2e51d8eb0beadc158b288380037618ce4e30c9acbc4fae1ac3ecb5797" + url: "https://pub.dev" + source: hosted + version: "0.10.5+9" + camera_android: + dependency: transitive + description: + name: camera_android + sha256: "351429510121d179b9aac5a2e8cb525c3cd6c39f4d709c5f72dfb21726e52371" + url: "https://pub.dev" + source: hosted + version: "0.10.8+16" + camera_avfoundation: + dependency: transitive + description: + name: camera_avfoundation + sha256: "608b56b0880722f703871329c4d7d4c2f379c8e2936940851df7fc041abc6f51" + url: "https://pub.dev" + source: hosted + version: "0.9.13+10" + camera_platform_interface: + dependency: transitive + description: + name: camera_platform_interface + sha256: fceb2c36038b6392317b1d5790c6ba9e6ca9f1da3031181b8bea03882bf9387a + url: "https://pub.dev" + source: hosted + version: "2.7.3" + camera_web: + dependency: transitive + description: + name: camera_web + sha256: f18ccfb33b2a7c49a52ad5aa3f07330b7422faaecbdfd9b9fe8e51182f6ad67d + url: "https://pub.dev" + source: hosted + version: "0.3.2+4" carousel_slider: dependency: "direct main" description: @@ -89,6 +129,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.1" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: fedaadfa3a6996f75211d835aaeb8fede285dae94262485698afd832371b9a5e + url: "https://pub.dev" + source: hosted + version: "0.3.3+8" crypto: dependency: transitive description: @@ -174,6 +222,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.3" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da + url: "https://pub.dev" + source: hosted + version: "2.0.17" flutter_svg: dependency: "direct main" description: @@ -289,7 +345,7 @@ packages: source: hosted version: "2.0.0" path: - dependency: transitive + dependency: "direct main" description: name: path sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" @@ -313,7 +369,7 @@ packages: source: hosted version: "1.0.1" path_provider: - dependency: transitive + dependency: "direct main" description: name: path_provider sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b @@ -485,6 +541,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + url: "https://pub.dev" + source: hosted + version: "2.1.0" string_scanner: dependency: transitive description: @@ -493,6 +557,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + syncfusion_flutter_core: + dependency: transitive + description: + name: syncfusion_flutter_core + sha256: e8580e201c7197feac830b501889e877796a9fabbe20dcdbe90a981603939101 + url: "https://pub.dev" + source: hosted + version: "24.2.4" + syncfusion_flutter_signaturepad: + dependency: "direct main" + description: + name: syncfusion_flutter_signaturepad + sha256: "878e1063b909a83c83677627261780d42d532d0b5e7e259d84da805008e7fb0d" + url: "https://pub.dev" + source: hosted + version: "24.2.4" synchronized: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index f26ba10..b5fe5ab 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -45,6 +45,10 @@ dependencies: provider: ^6.1.1 group_button: ^5.3.4 pinput: ^2.2.21 + camera: ^0.10.5+9 + path_provider: ^2.1.2 + path: ^1.8.3 + syncfusion_flutter_signaturepad: ^24.2.4