206 lines
7.0 KiB
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();
|
|
}
|
|
}
|