Files
TravelMate/lib/components/count/group_expenses_page.dart
Dayron ce754c1e6c feat: Add expense management features with tabs for expenses, balances, and settlements
- Implemented ExpensesTab to display a list of expenses with details.
- Created GroupExpensesPage to manage group expenses with a tabbed interface.
- Added SettlementsTab to show optimized settlements between users.
- Developed data models for Expense and Balance, including necessary methods for serialization.
- Introduced CountRepository for Firestore interactions related to expenses.
- Added CountService to handle business logic for expenses and settlements.
- Integrated image picker for receipt uploads.
- Updated main.dart to include CountBloc and CountRepository.
- Enhanced pubspec.yaml with new dependencies for image picking and Firebase storage.

Not Tested yet
2025-10-20 19:22:57 +02:00

117 lines
3.3 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../blocs/count/count_bloc.dart';
import '../../blocs/count/count_event.dart';
import '../../blocs/count/count_state.dart';
import '../../blocs/user/user_bloc.dart';
import '../../blocs/user/user_state.dart' as user_state;
import '../../data/models/group.dart';
import 'add_expense_dialog.dart';
import 'balances_tab.dart';
import 'expenses_tab.dart';
import 'settlements_tab.dart';
class GroupExpensesPage extends StatefulWidget {
final Group group;
const GroupExpensesPage({
super.key,
required this.group,
});
@override
State<GroupExpensesPage> createState() => _GroupExpensesPageState();
}
class _GroupExpensesPageState extends State<GroupExpensesPage>
with SingleTickerProviderStateMixin {
late TabController _tabController;
@override
void initState() {
super.initState();
_tabController = TabController(length: 3, vsync: this);
context.read<CountBloc>().add(LoadExpenses(widget.group.id));
}
@override
void dispose() {
_tabController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.group.name),
bottom: TabBar(
controller: _tabController,
tabs: const [
Tab(text: 'Dépenses', icon: Icon(Icons.receipt_long)),
Tab(text: 'Balances', icon: Icon(Icons.account_balance)),
Tab(text: 'Remboursements', icon: Icon(Icons.payments)),
],
),
),
body: BlocConsumer<CountBloc, CountState>(
listener: (context, state) {
if (state is CountError) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(state.message),
backgroundColor: Colors.red,
),
);
}
},
builder: (context, state) {
if (state is CountLoading) {
return const Center(child: CircularProgressIndicator());
}
if (state is ExpensesLoaded) {
return TabBarView(
controller: _tabController,
children: [
ExpensesTab(
expenses: state.expenses,
group: widget.group,
),
BalancesTab(balances: state.balances),
SettlementsTab(settlements: state.settlements),
],
);
}
return const Center(child: Text('Aucune donnée'));
},
),
floatingActionButton: BlocBuilder<UserBloc, user_state.UserState>(
builder: (context, userState) {
if (userState is! user_state.UserLoaded) return const SizedBox();
return FloatingActionButton.extended(
onPressed: () => _showAddExpenseDialog(context, userState.user),
icon: const Icon(Icons.add),
label: const Text('Dépense'),
);
},
),
);
}
void _showAddExpenseDialog(BuildContext context, user_state.UserModel currentUser) {
showDialog(
context: context,
builder: (dialogContext) => BlocProvider.value(
value: context.read<CountBloc>(),
child: AddExpenseDialog(
group: widget.group,
currentUser: currentUser,
),
),
);
}
}