didvan-app/lib/views/home/statistic/statistic.dart

206 lines
7.0 KiB
Dart

import 'dart:math';
import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/models/category.dart';
import 'package:didvan/models/enums.dart';
import 'package:didvan/models/statistic_data/statistic_data.dart';
import 'package:didvan/views/home/statistic/statistic_state.dart';
import 'package:didvan/views/home/statistic/widgets/statistic_overview.dart';
import 'package:didvan/views/widgets/categories_gird.dart';
import 'package:didvan/views/widgets/categories_list.dart';
import 'package:didvan/views/widgets/animated_visibility.dart';
import 'package:didvan/views/widgets/didvan/divider.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/state_handlers/empty_list.dart';
import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class Statistic extends StatefulWidget {
const Statistic({Key? key}) : super(key: key);
@override
State<Statistic> createState() => _StatisticState();
}
class _StatisticState extends State<Statistic> {
final ScrollController _scrollController = ScrollController();
bool _isAnimating = false;
@override
void initState() {
_scrollController.addListener(() {
_handleAnimations();
});
final state = context.read<StatisticState>();
state.addListener(() {
if (state.shouldColapse && mounted) {
_handleAnimations(true);
state.shouldColapse = false;
}
});
state.init();
super.initState();
}
@override
Widget build(BuildContext context) {
return Consumer<StatisticState>(
builder: (context, state, child) => Stack(
children: [
CustomScrollView(
physics: _isAnimating
? const NeverScrollableScrollPhysics()
: const ClampingScrollPhysics(),
controller: _scrollController,
slivers: [
if (state.appState != AppState.failed)
const SliverToBoxAdapter(
child: SizedBox(height: 120),
),
if (state.appState != AppState.failed &&
state.markedStatistics.isNotEmpty)
SliverPadding(
padding: const EdgeInsets.only(right: 16, bottom: 20),
sliver: SliverToBoxAdapter(
child: Align(
alignment: Alignment.centerRight,
child: AnimatedVisibility(
isVisible: !state.isColapsed,
duration: DesignConfig.lowAnimationDuration,
child: DidvanText(
'شاخص‌های منتخب',
style: Theme.of(context).textTheme.titleMedium,
color: Theme.of(context).colorScheme.title,
),
),
),
),
),
SliverStateHandler<StatisticState>(
onRetry: state.getStatistic,
state: state,
itemPadding: const EdgeInsets.only(
bottom: 20,
left: 16,
right: 16,
),
emptyState: const EmptyList(),
enableEmptyState: _itemCount(state) == 0,
placeholder: StatisticOverview.placeHolder,
builder: (context, state, index) {
if (index == state.markedStatistics.length) {
return index == 0
? const SizedBox()
: const DidvanDivider(verticalPadding: 8);
}
bool isMarked = false;
StatisticData statistic;
if (index < state.markedStatistics.length) {
isMarked = true;
statistic = state.markedStatistics[index];
} else {
index--;
statistic =
state.statistics[index - state.markedStatistics.length];
}
return StatisticOverview(
statistic: statistic,
isMarked: isMarked,
onMarkChanged: state.changeMark,
);
},
childCount: _itemCount(state) + 1,
),
SliverToBoxAdapter(
child: SizedBox(
height: state.appState == AppState.busy
? 300
: _itemCount(state) == 0
? 150
: max(
MediaQuery.of(context).size.height -
_itemCount(state) * 120,
0),
),
),
],
),
if (state.appState != AppState.failed)
CategoriesRow1(
onSelected: _onCategorySelected,
categories: List.from(state.categories)..removeAt(0),
isColapsed: state.isColapsed,
topPadding: 20,
rightPadding: 300,
),
if (state.appState != AppState.failed)
CategoriesList(
top: 0,
categories: state.categories,
isColapsed: state.isColapsed,
onSelected: (id) {
state.selectedCategoryId = id;
state.getStatistic();
},
selectedCats: state.selectedCategory == null
? []
: [state.selectedCategory!],
),
],
),
);
}
int _itemCount(state) =>
state.markedStatistics.length +
(state.selectedCategoryId == 1 ? 0 : state.statistics.length);
void _onCategorySelected(CategoryData category) {
final state = context.read<StatisticState>();
state.selectedCategoryId = 0;
if (category.id != 0) {
state.selectedCategoryId = category.id;
}
state.getStatistic();
}
void _handleAnimations([bool forceAnimate = false]) async {
final state = context.read<StatisticState>();
if (_isAnimating) return;
final double position = _scrollController.offset;
if (position > 5 && !state.isColapsed || forceAnimate) {
state.isScrolled = true;
_isAnimating = true;
setState(() {});
await _scrollController.animateTo(
60,
duration: DesignConfig.lowAnimationDuration,
curve: Curves.easeIn,
);
_isAnimating = false;
setState(() {});
} else if (position < min(_scrollController.position.maxScrollExtent, 40) &&
state.isColapsed) {
state.isScrolled = false;
_isAnimating = true;
setState(() {});
await _scrollController.animateTo(
0,
duration: DesignConfig.lowAnimationDuration,
curve: Curves.easeIn,
);
_isAnimating = false;
setState(() {});
}
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
}