From 44aa5f32a96cfcaafe8ec686597289fe3b3785ad Mon Sep 17 00:00:00 2001 From: NadeenRR Date: Tue, 10 Feb 2026 14:47:07 +0200 Subject: [PATCH] feat: add category management screens, bottom sheet and localization strings --- lib/core/l10n/app_ar.arb | 7 +- lib/core/l10n/app_en.arb | 7 +- .../buttons/button/default_button.dart | 4 +- .../screen/manage_categories_screen.dart | 93 +++++++++++++++++++ .../widget/add_category_bottom_sheet.dart | 92 ++++++++++++++++++ .../categories/widget/category_chip.dart | 34 +++++++ 6 files changed, 234 insertions(+), 3 deletions(-) create mode 100644 lib/presentation/categories/screen/manage_categories_screen.dart create mode 100644 lib/presentation/categories/widget/add_category_bottom_sheet.dart create mode 100644 lib/presentation/categories/widget/category_chip.dart diff --git a/lib/core/l10n/app_ar.arb b/lib/core/l10n/app_ar.arb index 99351da0..4e968d56 100644 --- a/lib/core/l10n/app_ar.arb +++ b/lib/core/l10n/app_ar.arb @@ -95,5 +95,10 @@ "expenses": "المصروفات", "transaction": "المعاملات", "transaction_error_title": "خطأ في المعاملات", - "transaction_error_content": "فشل في تحميل المعاملات" + "transaction_error_content": "فشل في تحميل المعاملات", + "manage_categories" : "ادارةالتصنيفات", + "add_new_category": "اضافة تصنيف جديد", + "add_custom_category" : "اضافة تصنيف مخصص", + "category_name" : "اسم التصنيف", + "add" : "اضافة" } diff --git a/lib/core/l10n/app_en.arb b/lib/core/l10n/app_en.arb index 8184aea2..01cd477d 100644 --- a/lib/core/l10n/app_en.arb +++ b/lib/core/l10n/app_en.arb @@ -134,5 +134,10 @@ "month_september": "September", "month_october": "October", "month_november": "November", - "month_december": "December" + "month_december": "December", + "manage_categories" : "Manage categories", + "add_new_category": "Add new category", + "add_custom_category" : "Add custom category", + "category_name" : "Category name", + "add" : "Add" } \ No newline at end of file diff --git a/lib/design_system/widgets/buttons/button/default_button.dart b/lib/design_system/widgets/buttons/button/default_button.dart index 0637faef..20de8bbf 100644 --- a/lib/design_system/widgets/buttons/button/default_button.dart +++ b/lib/design_system/widgets/buttons/button/default_button.dart @@ -41,13 +41,15 @@ class _DefaultButtonState extends State { offset: const Offset(0, 4), blurRadius: 12, spreadRadius: 0, + blurStyle: BlurStyle.inner, ), outerShadow: BoxShadow( color: const Color(0x29DC143C), offset: const Offset(0, 4), blurRadius: 8, spreadRadius: 0, + blurStyle: BlurStyle.outer, ), ); } -} \ No newline at end of file +} diff --git a/lib/presentation/categories/screen/manage_categories_screen.dart b/lib/presentation/categories/screen/manage_categories_screen.dart new file mode 100644 index 00000000..cb0159b6 --- /dev/null +++ b/lib/presentation/categories/screen/manage_categories_screen.dart @@ -0,0 +1,93 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:go_router/go_router.dart'; +import 'package:moneyplus/design_system/assets/app_assets.dart'; + +import '../../../core/l10n/app_localizations.dart'; +import '../../../design_system/theme/money_extension_context.dart'; +import '../../../design_system/widgets/buttons/button/default_button.dart'; +import '../widget/add_category_bottom_sheet.dart'; +import '../widget/category_chip.dart'; + +class ManageCategoriesScreen extends StatelessWidget { + ManageCategoriesScreen({super.key}); + + // TODO : Replace with actual categories from backend + final List categories = [ + 'Food', + 'Transport', + 'Rent', + 'Entertainment', + 'Electricity', + 'Shopping', + 'Health', + 'Fitness / Gym', + 'Internet', + 'Cats', + 'University', + ]; + + @override + Widget build(BuildContext context) { + final colors = context.colors; + final typography = context.typography; + final localizations = AppLocalizations.of(context)!; + return Scaffold( + backgroundColor: colors.surface, + appBar: AppBar( + leading: Padding( + padding: const EdgeInsetsGeometry.only(left: 16), + child: IconButton( + padding: EdgeInsets.zero, + onPressed: () => context.pop(), + icon: Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: colors.surface, + shape: BoxShape.circle, + ), + child: Center(child: SvgPicture.asset(AppAssets.icArrowLeft)), + ), + ), + ), + title: Text(localizations.manage_categories, style: typography.title.small), + ), + body: Padding( + padding: const EdgeInsetsGeometry.only(top: 16, left: 16, right: 16), + child: Wrap( + spacing: 8, + runSpacing: 8, + children: categories + .map( + (category) => CategoryChip( + label: category, + onEdit: () => print('$category'), + ), + ) + .toList(), + ), + ), + bottomNavigationBar: Container( + width: double.infinity, + height: 84, + color: colors.surfaceLow, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16), + child: DefaultButton( + text: localizations.add_new_category, + onPressed: () => _openBottomSheet(context), + ), + ), + ), + ); + } + + void _openBottomSheet(BuildContext context) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (context) => const AddCategorySheet(), + ); + } +} diff --git a/lib/presentation/categories/widget/add_category_bottom_sheet.dart b/lib/presentation/categories/widget/add_category_bottom_sheet.dart new file mode 100644 index 00000000..37bf2688 --- /dev/null +++ b/lib/presentation/categories/widget/add_category_bottom_sheet.dart @@ -0,0 +1,92 @@ +import 'package:flutter/material.dart'; +import 'package:moneyplus/design_system/assets/app_assets.dart'; +import 'package:moneyplus/design_system/widgets/text_field.dart'; +import 'package:svg_flutter/svg.dart'; + +import '../../../core/l10n/app_localizations.dart'; +import '../../../design_system/theme/money_extension_context.dart'; +import '../../../design_system/widgets/buttons/button/default_button.dart'; + +class AddCategorySheet extends StatefulWidget { + const AddCategorySheet({super.key}); + + @override + State createState() => _AddCategorySheetState(); +} + +class _AddCategorySheetState extends State { + final TextEditingController _controller = TextEditingController(); + bool _isButtonEnabled = false; + + @override + void initState() { + super.initState(); + _controller.addListener(() { + setState(() { + _isButtonEnabled = _controller.text.trim().isNotEmpty; + }); + }); + } + + @override + Widget build(BuildContext context) { + final colors = context.colors; + final typography = context.typography; + final localizations = AppLocalizations.of(context)!; + return Container( + padding: EdgeInsets.only( + left: 16, + right: 16, + top: 24, + bottom: MediaQuery.of(context).viewInsets.bottom + 24, + ), + decoration: BoxDecoration( + color: colors.surface, + borderRadius: BorderRadius.vertical(top: Radius.circular(24)), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(localizations.add_custom_category, style: typography.title.small), + IconButton( + onPressed: () => Navigator.pop(context), + icon: SvgPicture.asset(AppAssets.iconCancel), + ), + ], + ), + Divider(color: colors.stroke, thickness: 1), + const SizedBox(height: 12), + + MTextField( + leading: Padding( + padding: const EdgeInsetsGeometry.only(left: 16, right: 8), + child: SvgPicture.asset(AppAssets.icCategory), + ), + hint: localizations.category_name, + value: '', + onChanged: (String value) { + setState(() { + _isButtonEnabled = value.trim().isNotEmpty; + }); + }, + ), + + const SizedBox(height: 24), + + DefaultButton( + text:localizations.add, + isEnabled: _isButtonEnabled, + onPressed: _isButtonEnabled + ? () { + Navigator.pop(context); + } + : null, + ), + ], + ), + ); + } +} diff --git a/lib/presentation/categories/widget/category_chip.dart b/lib/presentation/categories/widget/category_chip.dart new file mode 100644 index 00000000..21872717 --- /dev/null +++ b/lib/presentation/categories/widget/category_chip.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; + +import '../../../design_system/assets/app_assets.dart'; +import '../../../design_system/theme/money_extension_context.dart'; + +class CategoryChip extends StatelessWidget { + final String label; + final VoidCallback onEdit; + + const CategoryChip({super.key, required this.label, required this.onEdit}); + + + @override + Widget build(BuildContext context) { + final colors = context.colors; + final typography = context.typography; + return Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + decoration: BoxDecoration( + color: colors.surfaceLow, + borderRadius: BorderRadius.circular(12), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text(label, style: typography.label.medium), + const SizedBox(width: 8), + SvgPicture.asset(AppAssets.icEdit), + ], + ), + ); + } +} \ No newline at end of file