diff --git a/assets/icons/icon-education.png b/assets/icons/icon-education.png new file mode 100644 index 0000000..8d304f0 Binary files /dev/null and b/assets/icons/icon-education.png differ diff --git a/assets/icons/icon-fund.png b/assets/icons/icon-fund.png new file mode 100644 index 0000000..4af5a2c Binary files /dev/null and b/assets/icons/icon-fund.png differ diff --git a/assets/icons/icon-gadget-outline.png b/assets/icons/icon-gadget-outline.png new file mode 100644 index 0000000..5cef4ce Binary files /dev/null and b/assets/icons/icon-gadget-outline.png differ diff --git a/assets/icons/icon-home.png b/assets/icons/icon-home.png new file mode 100644 index 0000000..04bd1c0 Binary files /dev/null and b/assets/icons/icon-home.png differ diff --git a/assets/icons/icon-shop.png b/assets/icons/icon-shop.png new file mode 100644 index 0000000..6053960 Binary files /dev/null and b/assets/icons/icon-shop.png differ diff --git a/assets/images/img-empty-transaction.png b/assets/images/img-empty-transaction.png new file mode 100644 index 0000000..f202c5a Binary files /dev/null and b/assets/images/img-empty-transaction.png differ diff --git a/lib/application/assets/path_assets.dart b/lib/application/assets/path_assets.dart index fc3a0ee..9b760ae 100644 --- a/lib/application/assets/path_assets.dart +++ b/lib/application/assets/path_assets.dart @@ -49,12 +49,23 @@ class PathAssets { 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 iconNavigationHome = + 'assets/icons/icon-navigation-home.png'; + static const String iconNavigationPlan = + 'assets/icons/icon-navigation-plan.png'; + static const String iconNavigationTransaction = + 'assets/icons/icon-navigation-transaction.png'; + static const String iconNavigationPortfolio = + 'assets/icons/icon-navigation-portfolio.png'; + static const String iconNavigationProfile = + 'assets/icons/icon-navigation-profile.png'; static const String iconRemove = 'assets/icons/icon-remove.png'; + static const String iconEducation = 'assets/icons/icon-education.png'; + static const String iconFund = 'assets/icons/icon-fund.png'; + static const String iconHome = 'assets/icons/icon-home.png'; + static const String iconShop = 'assets/icons/icon-shop.png'; + static const String iconGadgetOutline = + 'assets/icons/icon-gadget-outline.png'; /// IMAGE static const String imgSplashLogo = 'assets/images/splash-logo.png'; @@ -89,9 +100,12 @@ class PathAssets { 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 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 imgEmptyTransaction = + 'assets/images/img-empty-transaction.png'; static const Map goalInvestIcon = { 'Education': iconToga, diff --git a/lib/application/component/card_transaction/card_transaction_view.dart b/lib/application/component/card_transaction/card_transaction_view.dart new file mode 100644 index 0000000..cf79d90 --- /dev/null +++ b/lib/application/component/card_transaction/card_transaction_view.dart @@ -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), + ), + ], + ), + ], + )), + ], + ), + ), + ); + } +} diff --git a/lib/application/component/card_transaction/empty_card_transaction.dart b/lib/application/component/card_transaction/empty_card_transaction.dart new file mode 100644 index 0000000..af14e03 --- /dev/null +++ b/lib/application/component/card_transaction/empty_card_transaction.dart @@ -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, + ), + ], + ), + ), + ); + } +} diff --git a/lib/core/utils/string_utils.dart b/lib/core/utils/string_utils.dart index 307fb19..d503567 100644 --- a/lib/core/utils/string_utils.dart +++ b/lib/core/utils/string_utils.dart @@ -1,3 +1,5 @@ +import 'package:intl/intl.dart'; + class StringUtils { static bool emailValidation(String email) { return RegExp( @@ -13,4 +15,12 @@ class StringUtils { return RegExp(r'^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*?[\W_])(?=.{8,})') .hasMatch(password); } + + static String formatTime(DateTime? dateTime) { + if (dateTime != null) { + DateFormat formatter = DateFormat('HH:mm:ss'); + return formatter.format(dateTime); + } + return '--:--:--'; + } } diff --git a/lib/features/transaction/view/cancel_view.dart b/lib/features/transaction/view/cancel_view.dart new file mode 100644 index 0000000..3a5d243 --- /dev/null +++ b/lib/features/transaction/view/cancel_view.dart @@ -0,0 +1,35 @@ +import 'package:cims_apps/application/component/card_transaction/card_transaction_view.dart'; +import 'package:cims_apps/application/component/card_transaction/empty_card_transaction.dart'; +import 'package:cims_apps/features/transaction/viewmodel/transaction_viewmodel.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class CancelView extends StatelessWidget { + const CancelView({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: Consumer( + builder: (context, provider, child) => Column( + children: [ + if (provider.listOnProcessTransaction.isEmpty) + EmptyCardTransaction( + onPressedButton: () {}, + ), + ...provider.listOnProcessTransaction.map((e) { + return CardTransactionView( + step: 'cancel', + type: 'type', + amount: 'amount', + iconPath: 'iconPath', + subs: 'subs', + onTap: () {}, + ); + }), + ], + ), + ), + ); + } +} diff --git a/lib/features/transaction/view/done_view.dart b/lib/features/transaction/view/done_view.dart new file mode 100644 index 0000000..808104e --- /dev/null +++ b/lib/features/transaction/view/done_view.dart @@ -0,0 +1,37 @@ +import 'package:cims_apps/application/component/card_transaction/card_transaction_view.dart'; +import 'package:cims_apps/application/component/card_transaction/empty_card_transaction.dart'; +import 'package:cims_apps/features/transaction/viewmodel/transaction_viewmodel.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class DoneView extends StatelessWidget { + const DoneView({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: Consumer( + builder: (context, provider, child) { + return Column( + children: [ + if (provider.listOnProcessTransaction.isEmpty) + EmptyCardTransaction( + onPressedButton: () {}, + ), + ...provider.listOnProcessTransaction.map((e) { + return CardTransactionView( + step: 'done', + type: 'type', + amount: 'amount', + iconPath: 'iconPath', + subs: 'subs', + onTap: () {}, + ); + }), + ], + ); + }, + ), + ); + } +} diff --git a/lib/features/transaction/view/onprocess_view.dart b/lib/features/transaction/view/onprocess_view.dart new file mode 100644 index 0000000..fe52771 --- /dev/null +++ b/lib/features/transaction/view/onprocess_view.dart @@ -0,0 +1,34 @@ +import 'package:cims_apps/application/component/card_transaction/card_transaction_view.dart'; +import 'package:cims_apps/application/component/card_transaction/empty_card_transaction.dart'; +import 'package:cims_apps/features/transaction/viewmodel/transaction_viewmodel.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class OnProcessView extends StatelessWidget { + const OnProcessView({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: + Consumer(builder: (context, provider, child) { + return Column(children: [ + if (provider.listOnProcessTransaction.isEmpty) + EmptyCardTransaction( + onPressedButton: () {}, + ), + ...provider.listOnProcessTransaction.map((e) { + return CardTransactionView( + step: 'on process', + type: 'type', + amount: 'amount', + iconPath: 'iconPath', + subs: 'subs', + onTap: () {}, + ); + }), + ]); + }), + ); + } +} diff --git a/lib/features/transaction/view/transaction_view.dart b/lib/features/transaction/view/transaction_view.dart index 8a9b14b..c678123 100644 --- a/lib/features/transaction/view/transaction_view.dart +++ b/lib/features/transaction/view/transaction_view.dart @@ -1,14 +1,124 @@ -import 'package:cims_apps/application/component/custom_app_bar/custom_app_bar.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/theme/color_palette.dart'; import 'package:cims_apps/core/utils/size_config.dart'; +import 'package:cims_apps/features/transaction/view/cancel_view.dart'; +import 'package:cims_apps/features/transaction/view/done_view.dart'; +import 'package:cims_apps/features/transaction/view/onprocess_view.dart'; +import 'package:cims_apps/features/transaction/view/waiting_view.dart'; +import 'package:cims_apps/features/transaction/viewmodel/transaction_viewmodel.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_toggle_tab/flutter_toggle_tab.dart'; +import 'package:provider/provider.dart'; class TransactionView extends StatelessWidget { const TransactionView({Key? key}) : super(key: key); @override Widget build(BuildContext context) { - return Scaffold( - appBar: CustomAppBar(height: SizeConfig.height * 0.08, title: 'Transaction'), - ); + List textTabs = const [ + Tab(text: 'Waiting'), + Tab(text: 'On process'), + Tab(text: 'Done'), + Tab(text: 'Cancel'), + ]; + List listTabBarView = const [ + WaitingView(), + OnProcessView(), + DoneView(), + CancelView(), + ]; + + return ChangeNotifierProvider( + create: (context) => TransactionViewModel(), + builder: (context, child) { + return Scaffold( + backgroundColor: ColorPalette.primary, + body: SizedBox( + child: Stack( + children: [ + const ImageView(image: PathAssets.imgDashboardAccount), + Column( + children: [ + SizedBox( + height: SizeConfig.height * .05, + ), + const Center( + child: Text( + 'Transaction', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.w700, + color: Colors.white), + ), + ), + SizedBox( + height: SizeConfig.height * .04, + ), + Container( + margin: const EdgeInsets.symmetric(horizontal: 24), + child: FlutterToggleTab( + height: SizeConfig.height * .065, + width: SizeConfig.width * .2, + marginSelected: const EdgeInsets.all(8.0), + isScroll: false, + selectedTextStyle: const TextStyle( + color: ColorPalette.primary, + fontWeight: FontWeight.w700, + ), + unSelectedTextStyle: const TextStyle( + color: ColorPalette.blackFont, + fontWeight: FontWeight.w700, + ), + unSelectedBackgroundColors: const [ + ColorPalette.blue50 + ], + selectedBackgroundColors: const [ColorPalette.white], + labels: const ['Subscribe', 'Reedem'], + selectedLabelIndex: (p0) {}, + selectedIndex: 0, + ), + ), + Expanded( + child: DefaultTabController( + length: textTabs.length, + child: Container( + color: Colors.transparent, + padding: const EdgeInsets.only(top: 32.0), + child: Container( + margin: const EdgeInsets.only(top: 24), + padding: const EdgeInsets.only(top: 16.0), + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(24), + topRight: Radius.circular(24)), + ), + child: Column( + children: [ + TabBar( + tabs: textTabs, + indicatorColor: Colors.blueAccent, + ), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 4.0, + ), + child: TabBarView(children: listTabBarView), + )) + ], + ), + ), + ), + )), + ], + ) + ], + ), + ), + ); + }); } } diff --git a/lib/features/transaction/view/waiting_view.dart b/lib/features/transaction/view/waiting_view.dart new file mode 100644 index 0000000..decce6a --- /dev/null +++ b/lib/features/transaction/view/waiting_view.dart @@ -0,0 +1,42 @@ +import 'package:cims_apps/application/assets/path_assets.dart'; +import 'package:cims_apps/application/component/card_transaction/card_transaction_view.dart'; +import 'package:cims_apps/application/component/card_transaction/empty_card_transaction.dart'; +import 'package:cims_apps/core/utils/number_formatter.dart'; +import 'package:cims_apps/core/utils/string_utils.dart'; +import 'package:cims_apps/features/transaction/viewmodel/transaction_viewmodel.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class WaitingView extends StatelessWidget { + const WaitingView({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: + Consumer(builder: (context, provider, child) { + return Column( + children: [ + provider.listWaitingTransaction.isNotEmpty + ? CardTransactionView( + onTap: () {}, + iconPath: PathAssets.iconEducation, + type: 'Education', + amount: NumberFormatter.numberCurrency( + 6000000, + 'Rp ', + 'id_ID', + decimalDigits: 0, + ), + timeTransaction: StringUtils.formatTime(DateTime.now()), + subs: '3 Subscription', + step: 'waiting') + : EmptyCardTransaction( + onPressedButton: () {}, + ), + ], + ); + }), + ); + } +} diff --git a/lib/features/transaction/viewmodel/transaction_viewmodel.dart b/lib/features/transaction/viewmodel/transaction_viewmodel.dart new file mode 100644 index 0000000..9e5579c --- /dev/null +++ b/lib/features/transaction/viewmodel/transaction_viewmodel.dart @@ -0,0 +1,8 @@ +import 'package:flutter/material.dart'; + +class TransactionViewModel extends ChangeNotifier { + List listWaitingTransaction = [1]; + List listOnProcessTransaction = []; + List listDoneTransaction = []; + List listCancelTransaction = []; +} diff --git a/lib/main.dart b/lib/main.dart index 70c5712..4ccabd7 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -20,68 +20,66 @@ class MyApp extends StatelessWidget { title: 'CIMS', debugShowCheckedModeBanner: false, theme: ThemeData( - appBarTheme: const AppBarTheme( - centerTitle: true, - backgroundColor: Colors.white, - elevation: 1, - foregroundColor: Colors.black, - titleTextStyle: TextStyle( - fontSize: 20, - fontWeight: FontWeight.w700, - fontFamily: 'Manrope', + appBarTheme: const AppBarTheme( + centerTitle: true, + backgroundColor: Colors.white, + elevation: 1, + foregroundColor: Colors.black, + titleTextStyle: TextStyle( + fontSize: 20, + fontWeight: FontWeight.w700, + fontFamily: 'Manrope', + color: ColorPalette.slate800, + )), + fontFamily: 'Manrope', + scaffoldBackgroundColor: Colors.white, + textTheme: const TextTheme( + displaySmall: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, color: ColorPalette.slate800, - )), - fontFamily: 'Manrope', - scaffoldBackgroundColor: Colors.white, - textTheme: const TextTheme( - displaySmall: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - color: ColorPalette.slate800, + ), + displayMedium: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: ColorPalette.slate800, + ), + displayLarge: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: ColorPalette.slate800, + ), + bodyMedium: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: ColorPalette.slate500, + ), + bodyLarge: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: ColorPalette.slate500, + ), + headlineSmall: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: ColorPalette.slate800, + ), + headlineLarge: TextStyle( + fontSize: 28, + fontWeight: FontWeight.bold, + color: ColorPalette.slate800, + ), ), - displayMedium: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - color: ColorPalette.slate800, + colorScheme: const ColorScheme.light().copyWith( + primary: const Color(0xff2563EB), + onPrimary: const Color(0xFFFF9130), + secondary: const Color(0xFFFECDA6), + onBackground: const Color(0xFFA9A9A9), ), - displayLarge: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: ColorPalette.slate800, + bottomSheetTheme: const BottomSheetThemeData( + backgroundColor: Colors.white, surfaceTintColor: Colors.white) + // useMaterial3: true, ), - bodyMedium: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w600, - color: ColorPalette.slate500, - ), - bodyLarge: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: ColorPalette.slate500, - ), - headlineSmall: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: ColorPalette.slate800, - ), - headlineLarge: TextStyle( - fontSize: 28, - fontWeight: FontWeight.bold, - color: ColorPalette.slate800, - ), - ), - colorScheme: const ColorScheme.light().copyWith( - primary: const Color(0xff2563EB), - onPrimary: const Color(0xFFFF9130), - secondary: const Color(0xFFFECDA6), - onBackground: const Color(0xFFA9A9A9), - ), - bottomSheetTheme: BottomSheetThemeData( - backgroundColor: Colors.white, - surfaceTintColor: Colors.white - ) - // useMaterial3: true, - ), initialRoute: initialRoute, onGenerateRoute: generateRoutes, navigatorObservers: [ diff --git a/pubspec.lock b/pubspec.lock index 446b2ba..29bef45 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -259,6 +259,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_toggle_tab: + dependency: "direct main" + description: + name: flutter_toggle_tab + sha256: "90ad0d050f656df677998825f985637d010117a1793828cd7e6dadada4ecd2c5" + url: "https://pub.dev" + source: hosted + version: "1.4.1" flutter_web_plugins: dependency: transitive description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index cb05aa4..a267e1f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -53,6 +53,7 @@ dependencies: shared_preferences: ^2.2.2 calendar_date_picker2: ^0.5.3 google_sign_in: ^6.2.1 + flutter_toggle_tab: ^1.4.1