diff --git a/lib/core/l10n/app_ar.arb b/lib/core/l10n/app_ar.arb index 99351da0..d991634f 100644 --- a/lib/core/l10n/app_ar.arb +++ b/lib/core/l10n/app_ar.arb @@ -74,6 +74,8 @@ "currencyCode": "دينار", "spendingTrend": "اتجاه الإنفاق", "noDataAvailable": "لا توجد بيانات متاحة", + "current_balance":"الرصيد الحالي", + "how_much_money": "ما مقدار المال المتوفر لديك حاليًا؟" "no_spending_categories": "لا يوجد معاملات متاحة", "month_january": "يناير", "month_february": "فبراير", diff --git a/lib/core/l10n/app_en.arb b/lib/core/l10n/app_en.arb index 8184aea2..aac1dbdc 100644 --- a/lib/core/l10n/app_en.arb +++ b/lib/core/l10n/app_en.arb @@ -122,6 +122,8 @@ "total": {} } }, + "current_balance":"Current balance", + "how_much_money": "How much money do you have right now?", "no_spending_categories": "No spending categories available", "month_january": "January", "month_february": "February", diff --git a/lib/presentation/account_setup/cubit/account_setup_cubit.dart b/lib/presentation/account_setup/cubit/account_setup_cubit.dart index 546a2a83..86b43a62 100644 --- a/lib/presentation/account_setup/cubit/account_setup_cubit.dart +++ b/lib/presentation/account_setup/cubit/account_setup_cubit.dart @@ -1,6 +1,7 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; + import '../../../domain/repository/account_repository.dart'; import 'account_setup_state.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; class AccountSetupCubit extends Cubit { final AccountRepository _accountSetupRepository; @@ -8,13 +9,67 @@ class AccountSetupCubit extends Cubit { AccountSetupCubit(this._accountSetupRepository) : super(AccountSetupState()); Future fetchCurrencies() async { - try{ + try { final currencies = await _accountSetupRepository.getCurrencies(); - emit(state.copyWith(currencies : currencies,isLoading: false)); - }catch(e){ + emit(state.copyWith(currencies: currencies, isLoading: false)); + } catch (e) { emit(state.copyWith(isLoading: false, errorMessage: e.toString())); } } -} \ No newline at end of file + bool _accountSetUpStep1ValidationInput() { + return state.currency.isNotEmpty && state.salary.isNotEmpty && state.salaryDay.isNotEmpty; + } + + bool _accountSetUpStep2ValidationInput() { + return state.currentBalance.isNotEmpty; + } + + void onSalaryChanged(String salary){ + emit(state.copyWith(salary: salary)); + _updateButtonEnabledState(); + } + + void onCurrencyChanged(String currency){ + emit(state.copyWith(currency: currency)); + _updateButtonEnabledState(); + } + + void onSalaryDayChanged(String salaryDay){ + emit(state.copyWith(salaryDay: salaryDay)); + _updateButtonEnabledState(); + } + + void onCurrentBalanceChanged(String currentBalance) { + emit(state.copyWith(currentBalance: currentBalance)); + _updateButtonEnabledState(); + } + + void _updateButtonEnabledState() { + + bool isButtonEnable = switch (state.accountStep) { + AccountStep.step1 => _accountSetUpStep1ValidationInput(), + AccountStep.step2 => _accountSetUpStep2ValidationInput(), + AccountStep.step3 => false, + }; + emit(state.copyWith(isButtonEnabled: isButtonEnable)); + } + + void onNextStep() { + switch (state.accountStep) { + case AccountStep.step1: + emit(state.copyWith(accountStep: AccountStep.step2)); + _updateButtonEnabledState(); + break; + case AccountStep.step2: + emit(state.copyWith(accountStep: AccountStep.step3)); + _updateButtonEnabledState(); + break; + case AccountStep.step3: + // submit account setup date + emit(state.copyWith(navigateToHome: true)); + break; + } + } +} diff --git a/lib/presentation/account_setup/cubit/account_setup_state.dart b/lib/presentation/account_setup/cubit/account_setup_state.dart index 40a4a0e9..1da74e6c 100644 --- a/lib/presentation/account_setup/cubit/account_setup_state.dart +++ b/lib/presentation/account_setup/cubit/account_setup_state.dart @@ -1,36 +1,51 @@ import 'package:moneyplus/domain/entity/currency.dart'; +enum AccountStep { + step1, + step2, + step3, +} + class AccountSetupState { final String currency; - final double salary; - final int salaryDay; + final String salary; + final String salaryDay; final String query; final bool isButtonEnabled; final List currencies; final bool isLoading; final String errorMessage; + final AccountStep accountStep; + final String currentBalance; + final bool navigateToHome; AccountSetupState({ this.currency = "", - this.salary = 0.0, - this.salaryDay = 0, + this.salary = "", + this.salaryDay = "", this.query = "", this.isButtonEnabled = false, this.currencies = const [], this.isLoading = true, this.errorMessage = "", + this.accountStep = AccountStep.step1, + this.currentBalance = "", + this.navigateToHome = false, }); AccountSetupState copyWith({ String? currency, - double? salary, - int? salaryDay, + String? salary, + String? salaryDay, String? query, bool? isButtonEnabled, List? currencies, String? errorMessage, bool? isLoading, + AccountStep? accountStep, + String? currentBalance, + bool? navigateToHome, }) { return AccountSetupState( currency: currency ?? this.currency, @@ -41,6 +56,9 @@ class AccountSetupState { currencies: currencies?? this.currencies, errorMessage: errorMessage?? this.errorMessage, isLoading: isLoading?? this.isLoading, + accountStep: accountStep?? this.accountStep, + currentBalance: currentBalance?? this.currentBalance, + navigateToHome: navigateToHome?? this.navigateToHome, ); } } diff --git a/lib/presentation/account_setup/screen/account_setup_screen.dart b/lib/presentation/account_setup/screen/account_setup_screen.dart index a461d3d6..14c1f77a 100644 --- a/lib/presentation/account_setup/screen/account_setup_screen.dart +++ b/lib/presentation/account_setup/screen/account_setup_screen.dart @@ -4,6 +4,7 @@ import 'package:flutter_svg/svg.dart'; import 'package:moneyplus/design_system/assets/app_assets.dart'; import 'package:moneyplus/design_system/widgets/app_bar.dart'; import 'package:moneyplus/presentation/account_setup/screen/page1.dart'; +import 'package:moneyplus/presentation/account_setup/screen/page2.dart'; import '../../../core/l10n/app_localizations.dart'; import '../../../design_system/theme/money_extension_context.dart'; @@ -42,74 +43,91 @@ class _AccountSetupScreenState extends State { return BlocProvider( create: (context) => cubit..fetchCurrencies(), - child: BlocBuilder( - builder: (context, state) { - return Scaffold( - backgroundColor: context.colors.surface, - body: SafeArea( - child: Padding( - padding: const EdgeInsetsDirectional.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - CustomAppBar( - leading: AppBarCircleButton( - assetPath: AppAssets.icArrowLeft, onTap: () {}), - title: l10n.accountSetup, - trailing: SvgPicture.asset(AppAssets.appBrand,), - ), - SizedBox(height: 36,), - Indicator(currentIndex: currentIndex), - SizedBox(height: 16,), - Text( - l10n.stepOfTotal(currentIndex + 1, 3), - style: context.typography.label.small.copyWith( - color: context.colors.body, + child: BlocListener( + listener: (context, state) { + if (state.accountStep.index != currentIndex) { + pageController.animateToPage( + state.accountStep.index, + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut, + ); + setState(() { + currentIndex = state.accountStep.index; + }); + } + if (state.navigateToHome) { + // navigate to home + } + }, + child: BlocBuilder( + builder: (context, state) { + return Scaffold( + backgroundColor: context.colors.surface, + body: SafeArea( + child: Padding( + padding: const EdgeInsetsDirectional.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + CustomAppBar( + leading: AppBarCircleButton( + assetPath: AppAssets.icArrowLeft, + onTap: () { + Navigator.pop(context); + }, + ), + title: l10n.accountSetup, + trailing: SvgPicture.asset(AppAssets.appBrand), + ), + SizedBox(height: 36), + Indicator(currentIndex: currentIndex), + SizedBox(height: 16), + Text( + l10n.stepOfTotal(currentIndex + 1, 3), + style: context.typography.label.small.copyWith( + color: context.colors.body, + ), + ), + SizedBox(height: 4), + Text( + l10n.setUpYourAccount, + style: context.typography.headline.medium.copyWith( + color: context.colors.title, + ), ), - ), - SizedBox(height: 4), - Text( - l10n.setUpYourAccount, - style: context.typography.headline.medium.copyWith( - color: context.colors.title, + SizedBox(height: 4), + Expanded( + child: PageView( + controller: pageController, + physics: const NeverScrollableScrollPhysics(), + onPageChanged: (index) { + setState(() { + currentIndex = index; + }); + }, + children: [ + SingleChildScrollView(child: Page1(state: state,)), + SingleChildScrollView(child: Page2(currency: state.currency, currentBalanceState: state.currentBalance)), + // page3() + ], + ), ), - ), - SizedBox(height: 4), - Expanded( - child: PageView( - controller: pageController, - onPageChanged: (index) { - setState(() { - currentIndex = index; - }); + DefaultButton( + text: state.accountStep == AccountStep.step3 + ? l10n.finishSetup + : l10n.next, + isEnabled: state.isButtonEnabled, + onPressed: () { + context.read().onNextStep(); }, - children: [ - SingleChildScrollView(child: Page1(state: state)) - // page2() - // page3() - ], ), - ), - DefaultButton( - text: currentIndex == 2 ? l10n.finishSetup : l10n.next, - isEnabled: false, - onPressed: () { - if (currentIndex < 2) { - pageController.nextPage( - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ); - } else { - // Navigate to home - } - }, - ), - ], + ], + ), ), ), - ), - ); - }, + ); + }, + ), ), ); } diff --git a/lib/presentation/account_setup/screen/currency_bottom_sheet.dart b/lib/presentation/account_setup/screen/currency_bottom_sheet.dart index 14c866a5..a407e90c 100644 --- a/lib/presentation/account_setup/screen/currency_bottom_sheet.dart +++ b/lib/presentation/account_setup/screen/currency_bottom_sheet.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:moneyplus/design_system/assets/app_assets.dart'; import 'package:moneyplus/design_system/theme/money_extension_context.dart'; @@ -6,6 +7,7 @@ import 'package:moneyplus/design_system/widgets/text_field.dart'; import '../../../core/l10n/app_localizations.dart'; import '../../../design_system/widgets/buttons/button/default_button.dart'; +import '../cubit/account_setup_cubit.dart'; import '../cubit/account_setup_state.dart'; import 'currency_list.dart'; @@ -65,9 +67,9 @@ class _CurrencyBottomSheetState extends State { ), child: MTextField( hint: l10n.search, - value: searchController.text, + value: widget.state.query, onChanged: (value) { - searchController.text = value; + context.read().onCurrencyChanged(value); }, leading: Padding( padding: const EdgeInsetsDirectional.only( diff --git a/lib/presentation/account_setup/screen/page1.dart b/lib/presentation/account_setup/screen/page1.dart index 6eec6bce..3d06b200 100644 --- a/lib/presentation/account_setup/screen/page1.dart +++ b/lib/presentation/account_setup/screen/page1.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:moneyplus/design_system/assets/app_assets.dart'; import 'package:moneyplus/design_system/widgets/text_field.dart'; @@ -6,10 +7,12 @@ import 'package:moneyplus/presentation/account_setup/cubit/account_setup_state.d import '../../../core/l10n/app_localizations.dart'; import '../../../design_system/theme/money_extension_context.dart'; +import '../cubit/account_setup_cubit.dart'; import 'currency_bottom_sheet.dart'; class Page1 extends StatefulWidget { final AccountSetupState state; + const Page1({super.key, required this.state}); @override @@ -18,10 +21,6 @@ class Page1 extends StatefulWidget { class _Page1State extends State { - final TextEditingController currencyController = TextEditingController(); - final TextEditingController salaryController = TextEditingController(); - final TextEditingController dayController = TextEditingController(); - @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; @@ -37,55 +36,83 @@ class _Page1State extends State { ), SizedBox(height: 24), MTextField( + key: ValueKey(widget.state.currency), hint: l10n.currency, leading: Padding( - padding: const EdgeInsetsDirectional.only(top: 14,bottom: 14,end: 8), + padding: const EdgeInsetsDirectional.only( + top: 14, + bottom: 14, + end: 8, + ), child: SvgPicture.asset(AppAssets.iconMoney), ), trailing: Padding( - padding: const EdgeInsetsDirectional.only(top: 14,bottom: 14,end: 8), + padding: const EdgeInsetsDirectional.only( + top: 14, + bottom: 14, + end: 8, + ), child: GestureDetector( onTap: () { - _openCurrencyBottomSheet(widget.state); + _openCurrencyBottomSheet(widget.state); }, - child: SvgPicture.asset(AppAssets.icArrowDownRound, height: 20,width: 20,), + child: SvgPicture.asset( + AppAssets.icArrowDownRound, + height: 20, + width: 20, + ), ), ), keyboardType: TextInputType.number, - value: currencyController.text, + value: widget.state.currency, onChanged: (value) { - currencyController.text = value; + context.read().onCurrencyChanged(value); }, ), SizedBox(height: 12), MTextField( hint: l10n.salary, leading: Padding( - padding: const EdgeInsetsDirectional.only(top: 14,bottom: 14,end: 8), + padding: const EdgeInsetsDirectional.only( + top: 14, + bottom: 14, + end: 8, + ), child: SvgPicture.asset(AppAssets.iconMoney), ), keyboardType: TextInputType.number, - value: salaryController.text, + value: widget.state.salary, onChanged: (value) { - salaryController.text = value; + context.read().onSalaryChanged(value); }, ), SizedBox(height: 12), MTextField( hint: l10n.salaryDay, leading: Padding( - padding: const EdgeInsetsDirectional.only(top: 14,bottom: 14,end: 8), + padding: const EdgeInsetsDirectional.only( + top: 14, + bottom: 14, + end: 8, + ), child: SvgPicture.asset(AppAssets.iconCalender), ), trailing: Padding( - padding: const EdgeInsetsDirectional.only(top: 14,bottom: 14,end: 8), + padding: const EdgeInsetsDirectional.only( + top: 14, + bottom: 14, + end: 8, + ), child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(100), color: context.colors.surface, ), child: Padding( - padding: const EdgeInsetsDirectional.symmetric(vertical: 4,horizontal: 8), + padding: const EdgeInsetsDirectional.symmetric( + vertical: 4, + horizontal: 8, + ), child: Text( l10n.fromEachMonth, style: context.typography.label.small.copyWith( @@ -96,15 +123,16 @@ class _Page1State extends State { ), ), keyboardType: TextInputType.number, - value: dayController.text , + value: widget.state.salaryDay, onChanged: (value) { - dayController.text = value; + context.read().onSalaryDayChanged(value); }, ), - SizedBox(height: 16,) + SizedBox(height: 16), ], ); } + Future _openCurrencyBottomSheet(AccountSetupState state) async { final result = await showModalBottomSheet( context: context, @@ -118,12 +146,7 @@ class _Page1State extends State { ); if (result != null) { - setState(() { - currencyController.text = result; - }); + context.read().onCurrencyChanged(result); } } - } - - diff --git a/lib/presentation/account_setup/screen/page2.dart b/lib/presentation/account_setup/screen/page2.dart new file mode 100644 index 00000000..52cc80a7 --- /dev/null +++ b/lib/presentation/account_setup/screen/page2.dart @@ -0,0 +1,74 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; + +import '../../../core/l10n/app_localizations.dart'; +import '../../../design_system/assets/app_assets.dart'; +import '../../../design_system/theme/money_extension_context.dart'; +import '../../../design_system/widgets/text_field.dart'; +import '../cubit/account_setup_cubit.dart'; + +class Page2 extends StatelessWidget { + final String currency; + final String currentBalanceState; + + const Page2({super.key, required this.currency, required this.currentBalanceState}); + + @override + Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + + return Column( + children: [ + Text( + l10n.how_much_money, + style: context.typography.body.small.copyWith( + color: context.colors.body, + ), + ), + SizedBox(height: 24), + MTextField( + hint: l10n.current_balance, + leading: Padding( + padding: const EdgeInsetsDirectional.only( + top: 14, + bottom: 14, + end: 8, + ), + child: SvgPicture.asset(AppAssets.iconMoney), + ), + trailing: Padding( + padding: const EdgeInsetsDirectional.only( + top: 14, + bottom: 14, + end: 8, + ), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(100), + color: context.colors.surface, + ), + child: Padding( + padding: const EdgeInsetsDirectional.symmetric( + vertical: 4, + horizontal: 8, + ), + child: Text( + currency, + style: context.typography.label.small.copyWith( + color: context.colors.body, + ), + ), + ), + ), + ), + keyboardType: TextInputType.number, + value: currentBalanceState, + onChanged: (value) { + context.read().onCurrentBalanceChanged(value); + }, + ), + ], + ); + } +}