diff --git a/assets/icons/icon_cancel_category.svg b/assets/icons/icon_cancel_category.svg
new file mode 100644
index 0000000..02620d3
--- /dev/null
+++ b/assets/icons/icon_cancel_category.svg
@@ -0,0 +1,3 @@
+
diff --git a/lib/core/l10n/app_ar.arb b/lib/core/l10n/app_ar.arb
index 326108e..e337729 100644
--- a/lib/core/l10n/app_ar.arb
+++ b/lib/core/l10n/app_ar.arb
@@ -105,10 +105,8 @@
"no_monthly_overview": "لم يتم تسجيل أي دخل أو مصروفات لهذه الفترة",
"error_loading_statistics": "فشل تحميل الإحصائيات",
"retry": "إعادة المحاولة",
- "noDataAvailable": "لا توجد بيانات متاحة",
"current_balance":"الرصيد الحالي",
"how_much_money": "ما مقدار المال المتوفر لديك حاليًا؟",
- "noDataAvailable": "لا توجد بيانات متاحة",
"no_spending_categories": "لا يوجد معاملات متاحة",
"month_january": "يناير",
"month_february": "فبراير",
@@ -124,7 +122,6 @@
"month_december": "ديسمبر",
"no_transaction_record_title": "لا توجد سجلات معاملات",
"no_transaction_record_content": "أضف أول معاملة للبدء",
- "add_transaction": "إضافة معاملة",
"all": "الكل",
"incomes": "الإيرادات",
"expenses": "المصروفات",
@@ -139,7 +136,6 @@
"transaction_delete_fail" : "نم مسح المعاملة بنجاح",
"add" : "اضافة",
"transaction_details": "تفاصيل المعاملة",
- "transaction_details": "Transaction details",
"income_details": "تفاصيل الدخل",
"expense_details": "تفاصيل المصروف"
}
diff --git a/lib/core/l10n/app_en.arb b/lib/core/l10n/app_en.arb
index e9285ba..32ca868 100644
--- a/lib/core/l10n/app_en.arb
+++ b/lib/core/l10n/app_en.arb
@@ -21,9 +21,7 @@
"spendingTrend": "Spending Trend",
"noDataAvailable": "No data available",
"date": "Date",
- "amount": "Amount",
"addIncome": "Add income",
- "note": "Note",
"add": "Add",
"saving": "Saving...",
"incomeAddedSuccessfully": "Income added successfully!",
@@ -67,9 +65,9 @@
"updatePasswordSuccessMessage": "Password updated successfully",
"updatePasswordErrorMessage": "Error updating password",
"error": "Error",
- "success" : "Success",
- "loading" : "Loading",
- "login_successfully" : "Login Successfully",
+ "success": "Success",
+ "loading": "Loading",
+ "login_successfully": "Login Successfully",
"login_welcome_title": "Welcome again!",
"login_welcome_subtitle": "Enter your credentials to access your account",
"login_email_hint": "Email",
@@ -151,8 +149,10 @@
"no_monthly_overview": "No income or expenses recorded for this period",
"error_loading_statistics": "Failed to load statistics",
"retry": "Retry",
- "add" : "Add",
"transaction_details": "Transaction details",
"income_details": "Income details",
- "expense_details": "Expense details"
+ "expense_details": "Expense details",
+ "whereDoYouUsuallySpendYourMoney": "Where do you usually spend your money?",
+ "suggestions": "Suggestions:",
+ "selectedCategories": "Selected Categories:"
}
diff --git a/lib/design_system/assets/app_assets.dart b/lib/design_system/assets/app_assets.dart
index 9ad969f..40468a6 100644
--- a/lib/design_system/assets/app_assets.dart
+++ b/lib/design_system/assets/app_assets.dart
@@ -8,6 +8,7 @@ class AppAssets {
static const icArrowUp = '$_icons/ic_arrow_up.svg';
static const icArrowRight = '$_icons/ic_arrow_right.svg';
static const String iconCancel = "$_icons/ic_cancel.svg";
+ static const String iconCancelCategory = "$_icons/icon_cancel_category.svg";
static const String iconError = "$_icons/ic_error.svg";
static const String iconSuccess = "$_icons/ic_success.svg";
static const String icCategory = "$_icons/ic_menu-square.svg";
diff --git a/lib/design_system/widgets/selected_category_item.dart b/lib/design_system/widgets/selected_category_item.dart
new file mode 100644
index 0000000..b7e1eb7
--- /dev/null
+++ b/lib/design_system/widgets/selected_category_item.dart
@@ -0,0 +1,57 @@
+import 'package:flutter/material.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';
+
+class SelectedCategoryItem extends StatelessWidget {
+ final String label;
+ final VoidCallback onDelete;
+
+ const SelectedCategoryItem({
+ super.key,
+ required this.label,
+ required this.onDelete,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ final colors = context.colors;
+ final typo = context.typography;
+
+ return Row(
+ children: [
+ Expanded(
+ child: Container(
+ padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
+ decoration: BoxDecoration(
+ color: colors.surfaceLow,
+ borderRadius: BorderRadius.circular(16),
+ ),
+ child: Text(
+ label,
+ style: typo.body.medium.copyWith(color: colors.title),
+ ),
+ ),
+ ),
+ const SizedBox(width: 4),
+ GestureDetector(
+ onTap: onDelete,
+ behavior: HitTestBehavior.opaque,
+ child: Container(
+ padding: const EdgeInsets.all(12),
+ decoration: BoxDecoration(
+ color: colors.redVariant,
+ borderRadius: BorderRadius.circular(16),
+ ),
+ child: SvgPicture.asset(
+ AppAssets.iconCancelCategory,
+ width: 52,
+ height: 28,
+ colorFilter: ColorFilter.mode(colors.red, BlendMode.srcIn),
+ ),
+ ),
+ ),
+ ],
+ );
+ }
+}
\ No newline at end of file
diff --git a/lib/presentation/account_setup/cubit/account_setup_cubit.dart b/lib/presentation/account_setup/cubit/account_setup_cubit.dart
index 3c2ea7e..cabb8db 100644
--- a/lib/presentation/account_setup/cubit/account_setup_cubit.dart
+++ b/lib/presentation/account_setup/cubit/account_setup_cubit.dart
@@ -72,4 +72,13 @@ class AccountSetupCubit extends Cubit {
break;
}
}
+ void toggleCategory(String category) {
+ final List updatedCategories = List.from(state.categories);
+ if (updatedCategories.contains(category)) {
+ updatedCategories.remove(category);
+ } else {
+ updatedCategories.add(category);
+ }
+ emit(state.copyWith(categories: updatedCategories));
+ }
}
diff --git a/lib/presentation/account_setup/cubit/account_setup_state.dart b/lib/presentation/account_setup/cubit/account_setup_state.dart
index b81c676..e541e64 100644
--- a/lib/presentation/account_setup/cubit/account_setup_state.dart
+++ b/lib/presentation/account_setup/cubit/account_setup_state.dart
@@ -14,6 +14,8 @@ class AccountSetupState {
final String query;
final bool isButtonEnabled;
final List currencies;
+ final List categories;
+ final List suggestions;
final bool isLoading;
final String errorMessage;
final AccountSetupStep accountStep;
@@ -27,6 +29,19 @@ class AccountSetupState {
this.query = "",
this.isButtonEnabled = false,
this.currencies = const [],
+ this.categories = const [],
+ this.suggestions = const [
+ 'Food',
+ 'Transport',
+ 'Shopping',
+ 'Health',
+ 'Education',
+ 'Gift',
+ 'Cafe',
+ 'Work',
+ 'Home',
+ 'Travel'
+ ],
this.isLoading = true,
this.errorMessage = "",
this.accountStep = AccountSetupStep.step1,
@@ -41,6 +56,8 @@ class AccountSetupState {
String? query,
bool? isButtonEnabled,
List? currencies,
+ List? categories,
+ List? suggestions,
String? errorMessage,
bool? isLoading,
AccountSetupStep? accountStep,
diff --git a/lib/presentation/account_setup/screen/account_setup_screen.dart b/lib/presentation/account_setup/screen/account_setup_screen.dart
index ff9e187..b5592f0 100644
--- a/lib/presentation/account_setup/screen/account_setup_screen.dart
+++ b/lib/presentation/account_setup/screen/account_setup_screen.dart
@@ -5,6 +5,7 @@ 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 'package:moneyplus/presentation/account_setup/screen/account_setup_step_three.dart';
import '../../../core/di/injection.dart';
import '../../../core/l10n/app_localizations.dart';
@@ -108,7 +109,8 @@ class _AccountSetupScreenState extends State {
children: [
SingleChildScrollView(child: Page1(state: state,)),
SingleChildScrollView(child: Page2(currency: state.currency, currentBalanceState: state.currentBalance)),
- // page3()
+ SingleChildScrollView(child: AccountSetupStepThree(state: state)),
+
],
),
),
diff --git a/lib/presentation/account_setup/screen/account_setup_step_three.dart b/lib/presentation/account_setup/screen/account_setup_step_three.dart
new file mode 100644
index 0000000..2d473b6
--- /dev/null
+++ b/lib/presentation/account_setup/screen/account_setup_step_three.dart
@@ -0,0 +1,122 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:moneyplus/design_system/widgets/chip.dart';
+import 'package:moneyplus/design_system/widgets/text_field.dart';
+import 'package:moneyplus/presentation/account_setup/cubit/account_setup_cubit.dart';
+import 'package:moneyplus/presentation/account_setup/cubit/account_setup_state.dart';
+
+import '../../../core/l10n/app_localizations.dart';
+import '../../../design_system/theme/money_extension_context.dart';
+import '../../../design_system/widgets/selected_category_item.dart';
+
+class AccountSetupStepThree extends StatefulWidget {
+ final AccountSetupState state;
+ const AccountSetupStepThree({super.key, required this.state});
+
+ @override
+ State createState() => _AccountSetupStepThreeState();
+}
+
+class _AccountSetupStepThreeState extends State {
+ final TextEditingController categoryController = TextEditingController();
+ String _searchQuery = '';
+
+ @override
+ void initState() {
+ super.initState();
+ categoryController.addListener(() {
+ setState(() {
+ _searchQuery = categoryController.text.toLowerCase();
+ });
+ });
+ }
+
+ @override
+ void dispose() {
+ categoryController.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final l10n = AppLocalizations.of(context)!;
+ final cubit = context.read();
+
+ final filteredSuggestions = widget.state.suggestions
+ .where((suggestion) =>
+ suggestion.toLowerCase().contains(_searchQuery) &&
+ !widget.state.categories.contains(suggestion))
+ .toList();
+
+ return SingleChildScrollView(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ l10n.whereDoYouUsuallySpendYourMoney,
+ style: context.typography.label.small.copyWith(
+ color: context.colors.body,
+ ),
+ ),
+ const SizedBox(height: 24),
+ MTextField(
+ hint: l10n.category_name,
+ keyboardType: TextInputType.text,
+ value: categoryController.text,
+ onChanged: (value) {
+ categoryController.text = value;
+ },
+ ),
+ const SizedBox(height: 16),
+ if (filteredSuggestions.isNotEmpty) ...[
+ Text(
+ l10n.suggestions,
+ style: context.typography.label.medium.copyWith(
+ color: context.colors.title,
+ ),
+ ),
+ const SizedBox(height: 12),
+ Wrap(
+ spacing: 8,
+ runSpacing: 8,
+ children: filteredSuggestions.map((suggestion) {
+ return MChip(
+ label: suggestion,
+ selected: false,
+ onTap: () {
+ cubit.toggleCategory(suggestion);
+ categoryController.clear();
+ },
+ );
+ }).toList(),
+ ),
+ ],
+ if (widget.state.categories.isNotEmpty) ...[
+ const SizedBox(height: 24),
+ Text(
+ l10n.selectedCategories,
+ style: context.typography.label.medium.copyWith(
+ color: context.colors.title,
+ ),
+ ),
+ const SizedBox(height: 12),
+ ListView.separated(
+ shrinkWrap: true,
+ physics: const NeverScrollableScrollPhysics(),
+ itemCount: widget.state.categories.length,
+ separatorBuilder: (context, index) => const SizedBox(height: 8),
+ itemBuilder: (context, index) {
+ final category = widget.state.categories[index];
+ return SelectedCategoryItem(
+ label: category,
+ onDelete: () => cubit.toggleCategory(category),
+ );
+ },
+ ),
+ ],
+ const SizedBox(height: 24),
+ ],
+ ),
+ );
+ }
+}