Files
apskel-owner-flutter/lib/presentation/pages/profit_sharing/profit_sharing_page.dart
T
efrilm 2e4c77888e
Build & Deploy iOS to TestFlight / build-and-deploy (push) Canceled after 0s
feat: profit sharing
2026-08-21 22:31:54 +07:00

204 lines
6.8 KiB
Dart

import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:shimmer/shimmer.dart';
import '../../../application/analytic/profit_sharing_loader/profit_sharing_loader_bloc.dart';
import '../../../common/theme/theme.dart';
import '../../../injection.dart';
import '../../components/field/date_range_picker_field.dart';
import '../../components/spacer/spacer.dart';
import '../../router/app_router.gr.dart';
import 'widgets/profit_sharing_allocation.dart';
import 'widgets/profit_sharing_categories.dart';
import 'widgets/profit_sharing_header.dart';
import 'widgets/profit_sharing_periods.dart';
@RoutePage()
class ProfitSharingPage extends StatefulWidget implements AutoRouteWrapper {
const ProfitSharingPage({super.key});
@override
State<ProfitSharingPage> createState() => _ProfitSharingPageState();
@override
Widget wrappedRoute(BuildContext context) => BlocProvider(
create: (_) =>
getIt<ProfitSharingLoaderBloc>()
..add(ProfitSharingLoaderEvent.fetched()),
child: this,
);
}
class _ProfitSharingPageState extends State<ProfitSharingPage>
with SingleTickerProviderStateMixin {
late AnimationController _fadeController;
late Animation<double> _fadeAnimation;
@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<ProfitSharingLoaderBloc, ProfitSharingLoaderState>(
listenWhen: (previous, current) =>
previous.dateFrom != current.dateFrom ||
previous.dateTo != current.dateTo,
listener: (context, state) {
context.read<ProfitSharingLoaderBloc>().add(
const ProfitSharingLoaderEvent.fetched(),
);
},
child: BlocBuilder<ProfitSharingLoaderBloc, ProfitSharingLoaderState>(
builder: (context, state) {
return RefreshIndicator(
backgroundColor: AppColor.white,
color: AppColor.primary,
onRefresh: () async {
context.read<ProfitSharingLoaderBloc>().add(
const ProfitSharingLoaderEvent.fetched(),
);
await context.read<ProfitSharingLoaderBloc>().stream.firstWhere(
(s) => !s.isFetching,
);
},
child: CustomScrollView(
physics: const AlwaysScrollableScrollPhysics(),
slivers: [
SliverToBoxAdapter(
child: FadeTransition(
opacity: _fadeAnimation,
child: ProfitSharingHeader(state: state),
),
),
SliverToBoxAdapter(
child: FadeTransition(
opacity: _fadeAnimation,
child: Padding(
padding: const EdgeInsets.all(16),
child: DateRangePickerField(
startDate: state.dateFrom,
endDate: state.dateTo,
onChanged: (startDate, endDate) {
if (startDate == null || endDate == null) return;
context.read<ProfitSharingLoaderBloc>().add(
ProfitSharingLoaderEvent.rangeDateChanged(
startDate,
endDate,
),
);
},
),
),
),
),
if (state.isFetching)
SliverToBoxAdapter(child: _buildShimmer())
else ...[
SliverToBoxAdapter(
child: FadeTransition(
opacity: _fadeAnimation,
child: ProfitSharingCategories(
categories: state.profitSharing.categories,
onCategoryTap: (category) => context.router.push(
ProfitSharingDetailRoute(
parentCategoryId: category.parentCategoryId,
parentCategoryName: category.parentCategoryName,
dateFrom: state.dateFrom,
dateTo: state.dateTo,
),
),
),
),
),
const SliverToBoxAdapter(child: SpaceHeight(16)),
SliverToBoxAdapter(
child: FadeTransition(
opacity: _fadeAnimation,
child: ProfitSharingAllocation(
budget: state.profitSharing.budget,
),
),
),
const SliverToBoxAdapter(child: SpaceHeight(16)),
SliverToBoxAdapter(
child: FadeTransition(
opacity: _fadeAnimation,
child: ProfitSharingPeriods(
state: state,
onPeriodTypeChanged: (type) {
context.read<ProfitSharingLoaderBloc>().add(
ProfitSharingLoaderEvent.periodTypeChanged(type),
);
},
),
),
),
],
const SliverToBoxAdapter(child: SizedBox(height: 100)),
],
),
);
},
),
),
);
}
Widget _buildShimmer() {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
children: [
_shimmerBox(height: 240),
const SpaceHeight(16),
_shimmerBox(height: 300),
],
),
);
}
Widget _shimmerBox({required double height}) {
return Shimmer.fromColors(
baseColor: Colors.grey[300]!,
highlightColor: Colors.grey[100]!,
child: Container(
height: height,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
),
),
);
}
}