This commit is contained in:
2026-03-21 00:56:15 +03:00
parent c827f6e475
commit 7b4b0cecd2
2 changed files with 77 additions and 31 deletions
+21 -3
View File
@@ -53,9 +53,13 @@ final searchQueryProvider = StateProvider<String>((ref) => '');
enum TransactionFilter { all, income, expense } enum TransactionFilter { all, income, expense }
enum TimeFilter { allTime, lastMonth }
final transactionFilterProvider = final transactionFilterProvider =
StateProvider<TransactionFilter>((ref) => TransactionFilter.all); StateProvider<TransactionFilter>((ref) => TransactionFilter.all);
final timeFilterProvider = StateProvider<TimeFilter>((ref) => TimeFilter.lastMonth);
final totalBalanceProvider = Provider<double>((ref) { final totalBalanceProvider = Provider<double>((ref) {
final txs = ref.watch(transactionsProvider); final txs = ref.watch(transactionsProvider);
final exchangeService = ref.watch(exchangeRateServiceProvider); final exchangeService = ref.watch(exchangeRateServiceProvider);
@@ -104,16 +108,30 @@ final currentMonthExpenseProvider = Provider<double>((ref) {
final filteredTransactionsProvider = Provider<List<Transaction>>((ref) { final filteredTransactionsProvider = Provider<List<Transaction>>((ref) {
final txs = ref.watch(transactionsProvider); final txs = ref.watch(transactionsProvider);
final query = ref.watch(searchQueryProvider).toLowerCase(); final query = ref.watch(searchQueryProvider).toLowerCase();
final filter = ref.watch(transactionFilterProvider); final typeFilter = ref.watch(transactionFilterProvider);
final timeFilter = ref.watch(timeFilterProvider);
var filtered = txs; var filtered = txs;
if (filter == TransactionFilter.income) { // Apply time filter first
if (timeFilter == TimeFilter.lastMonth) {
final now = DateTime.now();
final start = DateTime(now.year, now.month, 1);
final end = DateTime(now.year, now.month + 1, 1);
filtered = filtered.where((t) =>
t.date.isAfter(start.subtract(const Duration(seconds: 1))) &&
t.date.isBefore(end)
).toList();
}
// Apply type filter
if (typeFilter == TransactionFilter.income) {
filtered = filtered.where((t) => t.type == TransactionType.income).toList(); filtered = filtered.where((t) => t.type == TransactionType.income).toList();
} else if (filter == TransactionFilter.expense) { } else if (typeFilter == TransactionFilter.expense) {
filtered = filtered.where((t) => t.type == TransactionType.expense).toList(); filtered = filtered.where((t) => t.type == TransactionType.expense).toList();
} }
// Apply search query
if (query.isNotEmpty) { if (query.isNotEmpty) {
filtered = filtered.where((t) { filtered = filtered.where((t) {
final matchesCategory = t.category.toLowerCase().contains(query); final matchesCategory = t.category.toLowerCase().contains(query);
+51 -23
View File
@@ -76,7 +76,6 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
final monthExpense = ref.watch(currentMonthExpenseProvider); final monthExpense = ref.watch(currentMonthExpenseProvider);
final budget = ref.watch(budgetProvider); final budget = ref.watch(budgetProvider);
final recent = ref.watch(recentTransactionsProvider); final recent = ref.watch(recentTransactionsProvider);
final filter = ref.watch(transactionFilterProvider);
final currencyInfo = ref.watch(currencyProvider); final currencyInfo = ref.watch(currencyProvider);
final isDark = Theme.of(context).brightness == Brightness.dark; final isDark = Theme.of(context).brightness == Brightness.dark;
@@ -152,7 +151,7 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
ref: ref, ref: ref,
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
_FilterChips(selected: filter, ref: ref), const FilterChips(),
const SizedBox(height: 20), const SizedBox(height: 20),
Text( Text(
'Transactions', 'Transactions',
@@ -254,36 +253,61 @@ class _SearchBar extends StatelessWidget {
} }
} }
class _FilterChips extends StatelessWidget { class FilterChips extends ConsumerWidget {
final TransactionFilter selected; const FilterChips({super.key});
final WidgetRef ref;
const _FilterChips({required this.selected, required this.ref});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context, WidgetRef ref) {
final typeFilter = ref.watch(transactionFilterProvider);
final timeFilter = ref.watch(timeFilterProvider);
final isDark = Theme.of(context).brightness == Brightness.dark;
return Row( return Row(
children: [ children: [
// TIME GROUP
_FilterChip(
label: 'All Time',
isSelected: timeFilter == TimeFilter.allTime,
onTap: () => ref.read(timeFilterProvider.notifier).state = TimeFilter.allTime,
),
const SizedBox(width: 6),
_FilterChip(
label: 'Month',
isSelected: timeFilter == TimeFilter.lastMonth,
onTap: () => ref.read(timeFilterProvider.notifier).state = TimeFilter.lastMonth,
),
// VISUAL DIVIDER
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Container(
width: 1,
height: 20,
color: Theme.of(context).colorScheme.onSurface.withOpacity(
isDark ? 0.15 : 0.2,
),
),
),
// TYPE GROUP
_FilterChip( _FilterChip(
label: 'All', label: 'All',
isSelected: selected == TransactionFilter.all, isSelected: typeFilter == TransactionFilter.all,
onTap: () => ref.read(transactionFilterProvider.notifier).state = onTap: () => ref.read(transactionFilterProvider.notifier).state = TransactionFilter.all,
TransactionFilter.all,
), ),
const SizedBox(width: 8), const SizedBox(width: 6),
_FilterChip( _FilterChip(
label: 'Income', label: 'Income',
isSelected: selected == TransactionFilter.income, isSelected: typeFilter == TransactionFilter.income,
color: AppColors.income, color: AppColors.income,
onTap: () => ref.read(transactionFilterProvider.notifier).state = onTap: () => ref.read(transactionFilterProvider.notifier).state = TransactionFilter.income,
TransactionFilter.income,
), ),
const SizedBox(width: 8), const SizedBox(width: 6),
_FilterChip( _FilterChip(
label: 'Expense', label: 'Expense',
isSelected: selected == TransactionFilter.expense, isSelected: typeFilter == TransactionFilter.expense,
color: AppColors.expense, color: AppColors.expense,
onTap: () => ref.read(transactionFilterProvider.notifier).state = onTap: () => ref.read(transactionFilterProvider.notifier).state = TransactionFilter.expense,
TransactionFilter.expense,
), ),
], ],
); );
@@ -295,22 +319,24 @@ class _FilterChip extends StatelessWidget {
final bool isSelected; final bool isSelected;
final Color? color; final Color? color;
final VoidCallback onTap; final VoidCallback onTap;
const _FilterChip({ const _FilterChip({
required this.label, required this.label,
required this.isSelected, required this.isSelected,
this.color,
required this.onTap, required this.onTap,
this.color,
}); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final chipColor = color ?? AppColors.accent; final chipColor = color ?? AppColors.accent;
final isDark = Theme.of(context).brightness == Brightness.dark; final isDark = Theme.of(context).brightness == Brightness.dark;
return GestureDetector( return GestureDetector(
onTap: onTap, onTap: onTap,
child: AnimatedContainer( child: AnimatedContainer(
duration: const Duration(milliseconds: 200), duration: const Duration(milliseconds: 200),
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8), padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
decoration: BoxDecoration( decoration: BoxDecoration(
color: isSelected color: isSelected
? chipColor.withOpacity(0.2) ? chipColor.withOpacity(0.2)
@@ -318,13 +344,14 @@ class _FilterChip extends StatelessWidget {
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
border: isSelected border: isSelected
? Border.all(color: chipColor, width: 1.5) ? Border.all(color: chipColor, width: 1.5)
: (isDark : isDark
? null ? null
: Border.all(color: const Color(0xFFDDDDEE), width: 1)), : Border.all(color: const Color(0xFFDDDDEE), width: 1),
), ),
child: Text( child: Text(
label, label,
style: Theme.of(context).textTheme.bodySmall?.copyWith( style: const TextStyle(fontSize: 12).merge(
Theme.of(context).textTheme.bodySmall?.copyWith(
color: isSelected color: isSelected
? chipColor ? chipColor
: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), : Theme.of(context).colorScheme.onSurface.withOpacity(0.6),
@@ -332,6 +359,7 @@ class _FilterChip extends StatelessWidget {
), ),
), ),
), ),
),
); );
} }
} }