Some checks are pending
Build & Deploy iOS to TestFlight / build-and-deploy (push) Waiting to run
146 lines
4.3 KiB
Dart
146 lines
4.3 KiB
Dart
import 'package:auto_route/auto_route.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
|
|
import '../../../application/analytic/profit_loss_loader/profit_loss_loader_bloc.dart';
|
|
import '../../../common/theme/theme.dart';
|
|
import '../../../injection.dart';
|
|
import 'widgets/cost_breakdown.dart';
|
|
import 'widgets/profit_loss_header.dart';
|
|
import 'widgets/profit_loss_report.dart';
|
|
|
|
@RoutePage()
|
|
class FinancePage extends StatefulWidget implements AutoRouteWrapper {
|
|
const FinancePage({super.key});
|
|
|
|
@override
|
|
State<FinancePage> createState() => _FinancePageState();
|
|
|
|
@override
|
|
Widget wrappedRoute(BuildContext context) => BlocProvider(
|
|
create: (_) =>
|
|
getIt<ProfitLossLoaderBloc>()..add(ProfitLossLoaderEvent.fetched()),
|
|
child: this,
|
|
);
|
|
}
|
|
|
|
class _FinancePageState extends State<FinancePage>
|
|
with SingleTickerProviderStateMixin {
|
|
late AnimationController _fadeController;
|
|
late Animation<double> _fadeAnimation;
|
|
|
|
int _selectedTabIndex = 0;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
_fadeController = AnimationController(
|
|
duration: const Duration(milliseconds: 1000),
|
|
vsync: this,
|
|
);
|
|
|
|
_fadeAnimation = Tween<double>(
|
|
begin: 0.0,
|
|
end: 1.0,
|
|
).animate(CurvedAnimation(parent: _fadeController, curve: Curves.easeIn));
|
|
|
|
_fadeController.forward();
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_fadeController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
backgroundColor: AppColor.background,
|
|
body: BlocListener<ProfitLossLoaderBloc, ProfitLossLoaderState>(
|
|
listenWhen: (previous, current) =>
|
|
previous.dateFrom != current.dateFrom ||
|
|
previous.dateTo != current.dateTo,
|
|
listener: (context, state) {
|
|
context.read<ProfitLossLoaderBloc>().add(
|
|
ProfitLossLoaderEvent.fetched(),
|
|
);
|
|
},
|
|
child: BlocBuilder<ProfitLossLoaderBloc, ProfitLossLoaderState>(
|
|
builder: (context, state) {
|
|
return CustomScrollView(
|
|
slivers: [
|
|
// Header with gradient background, tabs, and summary
|
|
SliverToBoxAdapter(
|
|
child: FadeTransition(
|
|
opacity: _fadeAnimation,
|
|
child: ProfitLossHeader(
|
|
state: state,
|
|
selectedTabIndex: _selectedTabIndex,
|
|
onTabChanged: (index) {
|
|
setState(() {
|
|
_selectedTabIndex = index;
|
|
});
|
|
_onTabChanged(context, index);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
|
|
// Profit Loss Report Table
|
|
SliverToBoxAdapter(
|
|
child: FadeTransition(
|
|
opacity: _fadeAnimation,
|
|
child: ProfitLossReport(
|
|
mainSummary: state.profitLoss.mainSummary,
|
|
summary: state.profitLoss.summary,
|
|
selectedTabIndex: _selectedTabIndex,
|
|
),
|
|
),
|
|
),
|
|
|
|
// Cost Breakdown
|
|
SliverToBoxAdapter(
|
|
child: FadeTransition(
|
|
opacity: _fadeAnimation,
|
|
child: CostBreakdown(
|
|
purchasing: state.profitLoss.purchasing,
|
|
selectedTabIndex: _selectedTabIndex,
|
|
dateFrom: state.dateFrom,
|
|
dateTo: state.dateTo,
|
|
),
|
|
),
|
|
),
|
|
|
|
// Bottom spacing
|
|
const SliverToBoxAdapter(child: SizedBox(height: 100)),
|
|
],
|
|
);
|
|
},
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
void _onTabChanged(BuildContext context, int index) {
|
|
final now = DateTime.now();
|
|
DateTime dateFrom;
|
|
DateTime dateTo;
|
|
|
|
if (index == 0) {
|
|
// Today
|
|
dateFrom = DateTime(now.year, now.month, now.day);
|
|
dateTo = DateTime(now.year, now.month, now.day, 23, 59, 59);
|
|
} else {
|
|
// MTD (Month-to-Date)
|
|
dateFrom = DateTime(now.year, now.month, 1);
|
|
dateTo = now;
|
|
}
|
|
|
|
context.read<ProfitLossLoaderBloc>().add(
|
|
ProfitLossLoaderEvent.rangeDateChanged(dateFrom, dateTo),
|
|
);
|
|
}
|
|
}
|