Вот рекомендуемое сообщение коммита:
``` feat: обновить интерфейс с использованием библиотеки Forui ``` Это сообщение коммита: - Начинается с `feat:`, что указывает на новую функциональность - Кратко описывает основное изменение - обновление интерфейса с использованием библиотеки Forui - Написано на русском языке - Соответствует стандартам conventional commits - Уложено в рекомендованные 72 символа
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:forui/forui.dart';
|
||||
|
||||
import '../database/database.dart' as db;
|
||||
import '../models/category.dart';
|
||||
@@ -102,22 +103,19 @@ class _ExpensesScreenState extends State<ExpensesScreen> with TickerProviderStat
|
||||
try {
|
||||
await _controller.addSampleTransaction();
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Transaction added successfully'),
|
||||
duration: Duration(seconds: 2),
|
||||
behavior: SnackBarBehavior.floating,
|
||||
),
|
||||
FToast.show(
|
||||
context: context,
|
||||
title: const Text('Success'),
|
||||
description: const Text('Transaction added successfully'),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Error adding transaction: $e'),
|
||||
backgroundColor: Colors.red,
|
||||
behavior: SnackBarBehavior.floating,
|
||||
),
|
||||
FToast.show(
|
||||
context: context,
|
||||
title: const Text('Error'),
|
||||
description: Text('Error adding transaction: $e'),
|
||||
style: FToastStyle.destructive,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -125,10 +123,8 @@ class _ExpensesScreenState extends State<ExpensesScreen> with TickerProviderStat
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||
|
||||
return Scaffold(
|
||||
appBar: _buildAppBar(isDark),
|
||||
appBar: _buildAppBar(context),
|
||||
body: StreamBuilder<List<Category>>(
|
||||
stream: _controller.watchCategoryTotals(),
|
||||
builder: (context, categorySnapshot) {
|
||||
@@ -183,24 +179,30 @@ class _ExpensesScreenState extends State<ExpensesScreen> with TickerProviderStat
|
||||
},
|
||||
),
|
||||
bottomNavigationBar: _buildBottomNavigationBar(),
|
||||
floatingActionButton: FloatingActionButton.extended(
|
||||
onPressed: _addSampleTransaction,
|
||||
tooltip: 'Add Transaction',
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text('Add'),
|
||||
floatingActionButton: FButton(
|
||||
label: const Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(Icons.add),
|
||||
SizedBox(width: 8),
|
||||
Text('Add'),
|
||||
],
|
||||
),
|
||||
style: FButtonStyle.primary,
|
||||
onPress: _addSampleTransaction,
|
||||
),
|
||||
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
|
||||
);
|
||||
}
|
||||
|
||||
AppBar _buildAppBar(bool isDark) {
|
||||
AppBar _buildAppBar(BuildContext context) {
|
||||
return AppBar(
|
||||
title: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.account_balance_wallet_outlined,
|
||||
color: isDark ? Colors.greenAccent.shade100 : Colors.green.shade800,
|
||||
color: context.theme.colorScheme.primary,
|
||||
size: 24,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
@@ -209,7 +211,7 @@ class _ExpensesScreenState extends State<ExpensesScreen> with TickerProviderStat
|
||||
),
|
||||
centerTitle: true,
|
||||
leading: IconButton(
|
||||
tooltip: isDark ? 'Switch to Light Mode' : 'Switch to Dark Mode',
|
||||
tooltip: widget.isDarkMode ? 'Switch to Light Mode' : 'Switch to Dark Mode',
|
||||
icon: Icon(
|
||||
widget.isDarkMode ? Icons.wb_sunny_outlined : Icons.nightlight_round,
|
||||
color: widget.isDarkMode ? Colors.yellow.shade300 : Colors.blue.shade700,
|
||||
@@ -225,10 +227,14 @@ class _ExpensesScreenState extends State<ExpensesScreen> with TickerProviderStat
|
||||
type: MaterialType.transparency,
|
||||
child: IconButton(
|
||||
tooltip: 'View Profile',
|
||||
icon: CircleAvatar(
|
||||
radius: 18,
|
||||
backgroundColor: isDark ? Colors.green.shade800 : Colors.green.shade100,
|
||||
child: const Icon(Icons.person_outline, color: Colors.green, size: 20),
|
||||
icon: FAvatar(
|
||||
size: 36,
|
||||
backgroundColor: context.theme.colorScheme.primary.withOpacity(0.1),
|
||||
child: Icon(
|
||||
Icons.person_outline,
|
||||
color: context.theme.colorScheme.primary,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:forui/forui.dart';
|
||||
|
||||
class ProfileScreen extends StatelessWidget {
|
||||
const ProfileScreen({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Profile'),
|
||||
@@ -24,13 +23,13 @@ class ProfileScreen extends StatelessWidget {
|
||||
tag: 'profileAvatar', // Tag must match the one in ExpensesScreen
|
||||
child: Material( // Wrap with Material for Hero animation
|
||||
type: MaterialType.transparency,
|
||||
child: CircleAvatar(
|
||||
radius: 50,
|
||||
backgroundColor: isDark ? Colors.green.shade800 : Colors.green.shade100,
|
||||
child: const Icon(
|
||||
child: FAvatar(
|
||||
size: 100,
|
||||
backgroundColor: context.theme.colorScheme.primary.withOpacity(0.1),
|
||||
child: Icon(
|
||||
Icons.person,
|
||||
size: 50,
|
||||
color: Colors.green,
|
||||
color: context.theme.colorScheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -38,32 +37,23 @@ class ProfileScreen extends StatelessWidget {
|
||||
const SizedBox(height: 24),
|
||||
Text(
|
||||
'John Doe',
|
||||
style: TextStyle(
|
||||
fontSize: 24,
|
||||
style: context.theme.typography.xl2.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: isDark ? Colors.white : Colors.black,
|
||||
color: context.theme.colorScheme.foreground,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'john.doe@example.com',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: isDark ? Colors.grey.shade400 : Colors.grey.shade700,
|
||||
style: context.theme.typography.base.copyWith(
|
||||
color: context.theme.colorScheme.mutedForeground,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
ElevatedButton(
|
||||
onPressed: () {},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: isDark ? Colors.green.shade700 : Colors.green,
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 12),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
),
|
||||
child: const Text('Edit Profile'),
|
||||
FButton(
|
||||
label: const Text('Edit Profile'),
|
||||
style: FButtonStyle.primary,
|
||||
onPress: () {},
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:forui/forui.dart';
|
||||
|
||||
class ExpandableSection extends StatelessWidget {
|
||||
final String title;
|
||||
@@ -20,82 +21,29 @@ class ExpandableSection extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
margin: const EdgeInsets.fromLTRB(16, 8, 16, 0),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0),
|
||||
decoration: BoxDecoration(
|
||||
color: isDark ? Colors.grey.shade800 : Colors.green.shade100,
|
||||
borderRadius: BorderRadius.vertical(
|
||||
top: const Radius.circular(16),
|
||||
bottom: Radius.circular(isExpanded ? 0 : 16),
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 8, 16, 0),
|
||||
child: FCollapsible(
|
||||
initiallyExpanded: isExpanded,
|
||||
title: Row(
|
||||
children: [
|
||||
Icon(
|
||||
icon,
|
||||
color: context.theme.colorScheme.primary,
|
||||
size: 20,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
title,
|
||||
style: context.theme.typography.base.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: context.theme.colorScheme.primary,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
icon,
|
||||
color: isDark ? Colors.green.shade300 : Colors.green.shade800,
|
||||
size: 20,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: isDark ? Colors.green.shade300 : Colors.green.shade800,
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
AnimatedRotation(
|
||||
turns: isExpanded ? 0.5 : 0,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
child: Icon(
|
||||
Icons.keyboard_arrow_down,
|
||||
color: isDark ? Colors.green.shade300 : Colors.green.shade800,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
AnimatedBuilder(
|
||||
animation: heightFactor,
|
||||
builder: (context, innerChild) {
|
||||
return ClipRect(
|
||||
child: Align(
|
||||
heightFactor: heightFactor.value,
|
||||
child: Container(
|
||||
margin: const EdgeInsets.fromLTRB(16, 0, 16, 0),
|
||||
decoration: BoxDecoration(
|
||||
color: isDark ? const Color(0xFF1E1E1E) : Colors.white,
|
||||
borderRadius: const BorderRadius.vertical(
|
||||
bottom: Radius.circular(16),
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: isDark
|
||||
? Colors.black.withOpacity(0.3)
|
||||
: Colors.green.withOpacity(0.1),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: innerChild,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: child,
|
||||
),
|
||||
],
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:forui/forui.dart';
|
||||
|
||||
class FilterChipWidget extends StatelessWidget {
|
||||
final String label;
|
||||
@@ -14,35 +15,14 @@ class FilterChipWidget extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(right: 8),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), // Smaller padding
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected
|
||||
? (isDark ? Colors.green.shade700 : Colors.green.shade100)
|
||||
: (isDark ? Colors.grey.shade800 : Colors.grey.shade200),
|
||||
borderRadius: BorderRadius.circular(14), // Smaller radius
|
||||
border: isSelected
|
||||
? Border.all(
|
||||
color: isDark ? Colors.green.shade300 : Colors.green,
|
||||
width: 1,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
child: Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 12, // Smaller text
|
||||
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
|
||||
color: isSelected
|
||||
? (isDark ? Colors.white : Colors.green.shade700)
|
||||
: (isDark ? Colors.white70 : Colors.grey.shade700),
|
||||
),
|
||||
),
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 8),
|
||||
child: FButton(
|
||||
label: Text(label),
|
||||
style: isSelected
|
||||
? FButtonStyle.primary
|
||||
: FButtonStyle.secondary,
|
||||
onPress: onTap,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:forui/forui.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'summary_item.dart';
|
||||
|
||||
@@ -12,32 +13,23 @@ class SummaryCard extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
|
||||
child: Card(
|
||||
elevation: 2,
|
||||
child: FCard(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
'Total Expenses This Period',
|
||||
style: theme.textTheme.titleMedium,
|
||||
style: context.theme.typography.lg,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_buildTotalAmount(theme),
|
||||
_buildTotalAmount(context),
|
||||
const SizedBox(height: 16),
|
||||
Divider(
|
||||
height: 1,
|
||||
thickness: 1,
|
||||
indent: 20,
|
||||
endIndent: 20,
|
||||
color: theme.dividerColor.withOpacity(0.5),
|
||||
),
|
||||
FSeparator(),
|
||||
const SizedBox(height: 16),
|
||||
_buildSummaryItems(theme),
|
||||
_buildSummaryItems(),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -45,33 +37,29 @@ class SummaryCard extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTotalAmount(ThemeData theme) {
|
||||
Widget _buildTotalAmount(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'\$',
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: theme.colorScheme.primary,
|
||||
style: context.theme.typography.xl.copyWith(
|
||||
color: context.theme.colorScheme.primary,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
NumberFormat.currency(symbol: '', decimalDigits: 2).format(totalExpenses),
|
||||
style: TextStyle(
|
||||
fontSize: 36,
|
||||
style: context.theme.typography.xl4.copyWith(
|
||||
color: context.theme.colorScheme.primary,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: theme.colorScheme.primary,
|
||||
letterSpacing: -1,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSummaryItems(ThemeData theme) {
|
||||
Widget _buildSummaryItems() {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
@@ -84,7 +72,7 @@ class SummaryCard extends StatelessWidget {
|
||||
Container(
|
||||
height: 35,
|
||||
width: 1,
|
||||
color: theme.dividerColor.withOpacity(0.5),
|
||||
color: Colors.grey.withOpacity(0.3),
|
||||
),
|
||||
SummaryItem(
|
||||
icon: Icons.arrow_upward_rounded,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:forui/forui.dart';
|
||||
|
||||
class SummaryItem extends StatelessWidget {
|
||||
final IconData icon;
|
||||
@@ -16,7 +17,6 @@ class SummaryItem extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
@@ -24,14 +24,13 @@ class SummaryItem extends StatelessWidget {
|
||||
Icon(
|
||||
icon,
|
||||
size: 16,
|
||||
color: isDark ? color.withOpacity(0.8) : color,
|
||||
color: color,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: isDark ? Colors.grey.shade400 : Colors.grey.shade700,
|
||||
style: context.theme.typography.sm.copyWith(
|
||||
color: context.theme.colorScheme.mutedForeground,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -39,10 +38,9 @@ class SummaryItem extends StatelessWidget {
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
amount,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
style: context.theme.typography.base.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: isDark ? Colors.white : Colors.black87,
|
||||
color: context.theme.colorScheme.foreground,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:forui/forui.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import '../database/database.dart' as db; // Import database with prefix 'db'
|
||||
import '../models/transaction_record.dart';
|
||||
@@ -16,9 +17,6 @@ class TransactionListItem extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||
final theme = Theme.of(context);
|
||||
|
||||
// Get category details (icon, color) using the utility
|
||||
final categoryDetails = CategoryUtils.getCategoryDetails(transaction.categoryName);
|
||||
|
||||
@@ -42,11 +40,6 @@ class TransactionListItem extends StatelessWidget {
|
||||
displayDate = '${dateFormatter.format(transaction.date)}, ${timeFormatter.format(transaction.date)}';
|
||||
}
|
||||
|
||||
// Determine text colors based on theme
|
||||
Color primaryTextColor = theme.textTheme.bodyLarge?.color ?? (isDark ? Colors.white : Colors.black87);
|
||||
Color secondaryTextColor = theme.textTheme.bodyMedium?.color ?? (isDark ? Colors.white70 : Colors.grey.shade600);
|
||||
Color amountColor = isDark ? Colors.red.shade200 : Colors.red.shade700; // Expense color
|
||||
|
||||
// Use FadeTransition for item appearance (works with ListView.builder)
|
||||
return FadeTransition(
|
||||
opacity: animation, // Apply fade animation
|
||||
@@ -60,16 +53,12 @@ class TransactionListItem extends StatelessWidget {
|
||||
child: Row(
|
||||
children: [
|
||||
// Icon container with category color/icon
|
||||
Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
color: categoryDetails.colorCode.withOpacity(isDark ? 0.25 : 0.15), // Use category color with opacity
|
||||
borderRadius: BorderRadius.circular(12), // Rounded corners
|
||||
),
|
||||
FAvatar(
|
||||
backgroundColor: categoryDetails.colorCode.withOpacity(0.15),
|
||||
child: Icon(
|
||||
categoryDetails.iconCode, // Use category icon
|
||||
color: categoryDetails.colorCode, // Use category color for icon
|
||||
size: 20, // Icon size
|
||||
categoryDetails.iconCode,
|
||||
color: categoryDetails.colorCode,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12), // Spacing
|
||||
@@ -82,9 +71,9 @@ class TransactionListItem extends StatelessWidget {
|
||||
// Display Merchant if available, otherwise Category Name
|
||||
Text(
|
||||
transaction.merchant.isNotEmpty ? transaction.merchant : transaction.categoryName,
|
||||
style: theme.textTheme.bodyLarge?.copyWith(
|
||||
fontWeight: FontWeight.w500, // Medium weight for primary text
|
||||
color: primaryTextColor,
|
||||
style: context.theme.typography.base.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: context.theme.colorScheme.foreground,
|
||||
),
|
||||
maxLines: 1, // Prevent wrapping
|
||||
overflow: TextOverflow.ellipsis, // Handle long text
|
||||
@@ -93,9 +82,8 @@ class TransactionListItem extends StatelessWidget {
|
||||
// Display formatted date/time
|
||||
Text(
|
||||
displayDate,
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
color: secondaryTextColor, // Lighter color for secondary text
|
||||
fontSize: 12, // Smaller font size
|
||||
style: context.theme.typography.sm.copyWith(
|
||||
color: context.theme.colorScheme.mutedForeground,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
@@ -109,9 +97,9 @@ class TransactionListItem extends StatelessWidget {
|
||||
Text(
|
||||
// Format amount as currency (negative for expense)
|
||||
NumberFormat.currency(symbol: '-\$', decimalDigits: 2).format(transaction.amount),
|
||||
style: theme.textTheme.bodyLarge?.copyWith(
|
||||
fontWeight: FontWeight.w600, // Bold weight for amount
|
||||
color: amountColor, // Use expense color
|
||||
style: context.theme.typography.base.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: context.theme.colorScheme.destructive,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:forui/forui.dart';
|
||||
import '../database/database.dart' as db;
|
||||
import '../models/transaction_record.dart' as model;
|
||||
import '../utils/category_utils.dart';
|
||||
@@ -23,21 +24,18 @@ class TransactionsSection extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildHeader(theme, isDark),
|
||||
_buildHeader(context),
|
||||
_buildFilterChips(),
|
||||
_buildTransactionsList(theme),
|
||||
_buildTransactionsList(context),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeader(ThemeData theme, bool isDark) {
|
||||
Widget _buildHeader(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 20, 16, 4),
|
||||
child: Row(
|
||||
@@ -45,37 +43,21 @@ class TransactionsSection extends StatelessWidget {
|
||||
children: [
|
||||
Text(
|
||||
'Recent Transactions',
|
||||
style: theme.textTheme.titleLarge?.copyWith(
|
||||
style: context.theme.typography.xl.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
InkWell(
|
||||
onTap: onToggleFilter,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: isDark ? Colors.grey.shade800 : Colors.grey.shade200,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
selectedFilter,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: isDark ? Colors.white70 : Colors.black87,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Icon(
|
||||
Icons.filter_list_alt,
|
||||
size: 18,
|
||||
color: isDark ? Colors.white70 : Colors.black87,
|
||||
),
|
||||
],
|
||||
),
|
||||
FButton(
|
||||
label: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(selectedFilter),
|
||||
const SizedBox(width: 4),
|
||||
const Icon(Icons.filter_list_alt, size: 18),
|
||||
],
|
||||
),
|
||||
style: FButtonStyle.secondary,
|
||||
onPress: onToggleFilter,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -113,20 +95,10 @@ class TransactionsSection extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTransactionsList(ThemeData theme) {
|
||||
Widget _buildTransactionsList(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
|
||||
child: Card(
|
||||
margin: EdgeInsets.zero,
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
side: BorderSide(
|
||||
color: theme.dividerColor.withOpacity(0.5),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: FCard(
|
||||
child: StreamBuilder<List<db.Transaction>>(
|
||||
stream: transactionsStream,
|
||||
builder: (context, snapshot) {
|
||||
@@ -154,7 +126,9 @@ class TransactionsSection extends StatelessWidget {
|
||||
selectedFilter == 'All'
|
||||
? 'No transactions yet.'
|
||||
: 'No transactions found for $selectedFilter.',
|
||||
style: theme.textTheme.bodyMedium?.copyWith(color: Colors.grey),
|
||||
style: context.theme.typography.base.copyWith(
|
||||
color: context.theme.colorScheme.mutedForeground,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -165,13 +139,7 @@ class TransactionsSection extends StatelessWidget {
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
separatorBuilder: (context, index) => Divider(
|
||||
height: 1,
|
||||
thickness: 1,
|
||||
indent: 16,
|
||||
endIndent: 16,
|
||||
color: theme.dividerColor.withOpacity(0.3),
|
||||
),
|
||||
separatorBuilder: (context, index) => FSeparator(),
|
||||
itemBuilder: (context, index) {
|
||||
final dbTransaction = transactions[index];
|
||||
final categoryDetails = CategoryUtils.getCategoryDetails(
|
||||
|
||||
Reference in New Issue
Block a user