import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:marco/controller/project_controller.dart'; import 'package:marco/helpers/widgets/my_spacing.dart'; import 'package:marco/helpers/widgets/my_text.dart'; import 'package:marco/model/expense/add_expense_bottom_sheet.dart'; import 'package:marco/view/expense/expense_detail_screen.dart'; class ExpenseMainScreen extends StatefulWidget { const ExpenseMainScreen({super.key}); @override State createState() => _ExpenseMainScreenState(); } class _ExpenseMainScreenState extends State { final RxBool isHistoryView = false.obs; final TextEditingController searchController = TextEditingController(); final RxString searchQuery = ''.obs; final ProjectController projectController = Get.find(); final List> expenseList = [ { 'title': 'Travel Expense', 'amount': '₹ 1,500', 'status': 'Request', 'date': '12 Jul 2025 • 3:45 PM', 'category': 'Transport', 'paymentMode': 'UPI', 'transactionId': 'TXN123451' }, { 'title': 'Hotel Stay', 'amount': '₹ 4,500', 'status': 'Approved', 'date': '11 Jul 2025 • 9:30 AM', 'category': 'Accommodation', 'paymentMode': 'Credit Card', 'transactionId': 'TXN123452' }, { 'title': 'Food Bill', 'amount': '₹ 1,200', 'status': 'Paid', 'date': '10 Jul 2025 • 7:10 PM', 'category': 'Food', 'paymentMode': 'Cash', 'transactionId': 'TXN123453' }, ]; Color _getStatusColor(String status) { switch (status) { case 'Request': return Colors.blue; case 'Review': return Colors.orange; case 'Approved': return Colors.green; case 'Paid': return Colors.purple; case 'Closed': return Colors.grey; default: return Colors.black; } } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, appBar: _buildAppBar(), body: SafeArea( child: Column( children: [ _buildSearchAndFilter(), _buildToggleButtons(), Expanded( child: Obx( () => isHistoryView.value ? _buildHistoryList() : _buildExpenseList(), ), ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: () { showAddExpenseBottomSheet(); }, backgroundColor: Colors.red, child: const Icon(Icons.add, color: Colors.white), ), ); } PreferredSizeWidget _buildAppBar() { return PreferredSize( preferredSize: const Size.fromHeight(72), child: AppBar( backgroundColor: const Color(0xFFF5F5F5), elevation: 0.5, automaticallyImplyLeading: false, titleSpacing: 0, title: Padding( padding: MySpacing.xy(16, 0), child: Row( children: [ IconButton( icon: const Icon(Icons.arrow_back_ios_new, color: Colors.black, size: 20), onPressed: () => Get.offNamed('/dashboard'), ), MySpacing.width(8), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ MyText.titleLarge( 'Expenses', fontWeight: 700, color: Colors.black, ), MySpacing.height(2), GetBuilder( builder: (controller) { final projectName = controller.selectedProject?.name ?? 'Select Project'; return InkWell( onTap: () => Get.toNamed('/project-selector'), child: Row( children: [ const Icon(Icons.work_outline, size: 14, color: Colors.grey), MySpacing.width(4), Expanded( child: MyText.bodySmall( projectName, fontWeight: 600, overflow: TextOverflow.ellipsis, color: Colors.grey[700], ), ), ], ), ); }, ), ], ), ), ], ), ), ), ); } Widget _buildSearchAndFilter() { return Padding( padding: MySpacing.fromLTRB(12, 10, 12, 0), child: Row( children: [ Expanded( child: SizedBox( height: 35, child: TextField( controller: searchController, onChanged: (value) => searchQuery.value = value, decoration: InputDecoration( contentPadding: const EdgeInsets.symmetric(horizontal: 12), prefixIcon: const Icon(Icons.search, size: 20, color: Colors.grey), hintText: 'Search expenses...', filled: true, fillColor: Colors.white, border: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: Colors.grey.shade300), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: Colors.grey.shade300), ), ), ), ), ), MySpacing.width(8), IconButton( icon: const Icon(Icons.tune, color: Colors.black), onPressed: _openFilterBottomSheet, ), ], ), ); } Widget _buildToggleButtons() { return Padding( padding: MySpacing.fromLTRB(8, 12, 8, 5), child: Obx(() { return Container( padding: const EdgeInsets.all(2), decoration: BoxDecoration( color: const Color(0xFFF0F0F0), borderRadius: BorderRadius.circular(10), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 4, offset: const Offset(0, 2), ), ], ), child: Row( children: [ _buildToggleButton( label: 'Expenses', icon: Icons.receipt_long, selected: !isHistoryView.value, onTap: () => isHistoryView.value = false, ), _buildToggleButton( label: 'History', icon: Icons.history, selected: isHistoryView.value, onTap: () => isHistoryView.value = true, ), ], ), ); }), ); } Widget _buildToggleButton({ required String label, required IconData icon, required bool selected, required VoidCallback onTap, }) { return Expanded( child: GestureDetector( onTap: onTap, child: AnimatedContainer( duration: const Duration(milliseconds: 200), padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 10), decoration: BoxDecoration( color: selected ? Colors.red : Colors.transparent, borderRadius: BorderRadius.circular(8), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(icon, size: 16, color: selected ? Colors.white : Colors.grey), const SizedBox(width: 6), Text( label, style: TextStyle( color: selected ? Colors.white : Colors.grey, fontWeight: FontWeight.w600, fontSize: 13, ), ), ], ), ), ), ); } Widget _buildExpenseList() { return Obx(() { final filteredList = expenseList.where((expense) { return searchQuery.isEmpty || expense['title']! .toLowerCase() .contains(searchQuery.value.toLowerCase()); }).toList(); return _buildExpenseHistoryList(filteredList); }); } Widget _buildHistoryList() { final historyList = expenseList .where((item) => item['status'] == 'Paid' || item['status'] == 'Closed') .toList(); return _buildExpenseHistoryList(historyList); } Widget _buildExpenseHistoryList(List> list) { return ListView.builder( padding: const EdgeInsets.all(12), itemCount: list.length, itemBuilder: (context, index) { final item = list[index]; return GestureDetector( onTap: () => Get.to( () => const ExpenseDetailScreen(), arguments: {'expense': item}, ), child: _buildExpenseCard(item), ); }, ); } Widget _buildExpenseCard(Map item) { final statusColor = _getStatusColor(item['status']!); return Card( elevation: 3, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(14), ), margin: const EdgeInsets.symmetric(vertical: 8), child: Padding( padding: const EdgeInsets.all(14), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Title & Amount Row Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ const Icon(Icons.receipt_long, size: 20, color: Colors.red), const SizedBox(width: 8), Text( item['title']!, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.w600, ), ), ], ), Text( item['amount']!, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Colors.red, ), ), ], ), const SizedBox(height: 8), _buildInfoRow(Icons.calendar_today, item['date']!), const SizedBox(height: 6), _buildInfoRow(Icons.category_outlined, item['category']!), const SizedBox(height: 6), _buildInfoRow(Icons.payment, item['paymentMode']!), _buildInfoRow(Icons.confirmation_num_outlined, item['transactionId']!), const SizedBox(height: 10), Align( alignment: Alignment.centerRight, child: Container( padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 10), decoration: BoxDecoration( color: statusColor.withOpacity(0.1), borderRadius: BorderRadius.circular(20), ), child: Text( item['status']!, style: TextStyle( color: statusColor, fontWeight: FontWeight.bold, fontSize: 12, ), ), ), ), ], ), ), ); } Widget _buildInfoRow(IconData icon, String text) { return Row( children: [ Icon(icon, size: 14, color: Colors.grey), const SizedBox(width: 4), Text( text, style: TextStyle(color: Colors.grey[600], fontSize: 12), ), ], ); } void _openFilterBottomSheet() { Get.bottomSheet( Container( padding: const EdgeInsets.all(16), decoration: const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), child: Wrap( runSpacing: 10, children: [ const Text( 'Filter Expenses', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16), ), ListTile( leading: const Icon(Icons.date_range), title: const Text('Date Range'), onTap: () {}, ), ListTile( leading: const Icon(Icons.work_outline), title: const Text('Project'), onTap: () {}, ), ListTile( leading: const Icon(Icons.check_circle_outline), title: const Text('Status'), onTap: () {}, ), ], ), ), ); } }