Some checks are pending
Build & Deploy iOS to TestFlight / build-and-deploy (push) Waiting to run
213 lines
6.6 KiB
Dart
213 lines
6.6 KiB
Dart
import 'package:auto_route/auto_route.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
|
|
import '../../../application/analytic/inventory_analytic_loader/inventory_analytic_loader_bloc.dart';
|
|
import '../../../common/theme/theme.dart';
|
|
import '../../../injection.dart';
|
|
import '../../components/spacer/spacer.dart';
|
|
import 'widgets/inventory_header.dart';
|
|
import 'widgets/inventory_stock_report.dart';
|
|
|
|
@RoutePage()
|
|
class InventoryPage extends StatefulWidget implements AutoRouteWrapper {
|
|
const InventoryPage({super.key});
|
|
|
|
@override
|
|
State<InventoryPage> createState() => _InventoryPageState();
|
|
|
|
@override
|
|
Widget wrappedRoute(BuildContext context) => BlocProvider(
|
|
create: (_) =>
|
|
getIt<InventoryAnalyticLoaderBloc>()
|
|
..add(InventoryAnalyticLoaderEvent.fetched()),
|
|
child: this,
|
|
);
|
|
}
|
|
|
|
class _InventoryPageState extends State<InventoryPage>
|
|
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<
|
|
InventoryAnalyticLoaderBloc,
|
|
InventoryAnalyticLoaderState
|
|
>(
|
|
listenWhen: (previous, current) =>
|
|
previous.dateFrom != current.dateFrom ||
|
|
previous.dateTo != current.dateTo,
|
|
listener: (context, state) {
|
|
context.read<InventoryAnalyticLoaderBloc>().add(
|
|
InventoryAnalyticLoaderEvent.fetched(),
|
|
);
|
|
},
|
|
child:
|
|
BlocBuilder<
|
|
InventoryAnalyticLoaderBloc,
|
|
InventoryAnalyticLoaderState
|
|
>(
|
|
builder: (context, state) {
|
|
return CustomScrollView(
|
|
slivers: [
|
|
// Header with gradient background, tabs, and summary
|
|
SliverToBoxAdapter(
|
|
child: FadeTransition(
|
|
opacity: _fadeAnimation,
|
|
child: InventoryHeader(
|
|
state: state,
|
|
selectedTabIndex: _selectedTabIndex,
|
|
onTabChanged: (index) {
|
|
setState(() {
|
|
_selectedTabIndex = index;
|
|
});
|
|
},
|
|
onDateRangeChanged: (startDate, endDate) {
|
|
_onDateRangeChanged(
|
|
context,
|
|
startDate,
|
|
endDate,
|
|
);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
|
|
// Stock Report Table
|
|
SliverToBoxAdapter(
|
|
child: FadeTransition(
|
|
opacity: _fadeAnimation,
|
|
child: state.isFetching
|
|
? _buildLoadingReport()
|
|
: InventoryStockReport(
|
|
inventoryAnalytic: state.inventoryAnalytic,
|
|
selectedTabIndex: _selectedTabIndex,
|
|
),
|
|
),
|
|
),
|
|
|
|
// Bottom spacing
|
|
const SliverToBoxAdapter(child: SpaceHeight(100)),
|
|
],
|
|
);
|
|
},
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildLoadingReport() {
|
|
return Container(
|
|
margin: const EdgeInsets.all(16),
|
|
padding: const EdgeInsets.all(20),
|
|
decoration: BoxDecoration(
|
|
color: AppColor.white,
|
|
borderRadius: BorderRadius.circular(16),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: AppColor.textLight.withOpacity(0.08),
|
|
spreadRadius: 1,
|
|
blurRadius: 10,
|
|
offset: const Offset(0, 2),
|
|
),
|
|
],
|
|
),
|
|
child: Column(
|
|
children: List.generate(
|
|
5,
|
|
(index) => Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
|
child: Row(
|
|
children: [
|
|
Expanded(
|
|
flex: 4,
|
|
child: Container(
|
|
height: 14,
|
|
decoration: BoxDecoration(
|
|
color: AppColor.borderLight,
|
|
borderRadius: BorderRadius.circular(4),
|
|
),
|
|
),
|
|
),
|
|
const SpaceWidth(12),
|
|
Expanded(
|
|
flex: 2,
|
|
child: Container(
|
|
height: 14,
|
|
decoration: BoxDecoration(
|
|
color: AppColor.borderLight,
|
|
borderRadius: BorderRadius.circular(4),
|
|
),
|
|
),
|
|
),
|
|
const SpaceWidth(12),
|
|
Expanded(
|
|
flex: 2,
|
|
child: Container(
|
|
height: 14,
|
|
decoration: BoxDecoration(
|
|
color: AppColor.borderLight,
|
|
borderRadius: BorderRadius.circular(4),
|
|
),
|
|
),
|
|
),
|
|
const SpaceWidth(12),
|
|
Expanded(
|
|
flex: 2,
|
|
child: Container(
|
|
height: 14,
|
|
decoration: BoxDecoration(
|
|
color: AppColor.borderLight,
|
|
borderRadius: BorderRadius.circular(4),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
void _onDateRangeChanged(
|
|
BuildContext context,
|
|
DateTime startDate,
|
|
DateTime endDate,
|
|
) {
|
|
context.read<InventoryAnalyticLoaderBloc>().add(
|
|
InventoryAnalyticLoaderEvent.rangeDateChanged(startDate, endDate),
|
|
);
|
|
}
|
|
}
|