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/home/widgets/categories_gird.dart'; import 'package:didvan/views/home/widgets/categories_list.dart'; import 'package:didvan/views/home/widgets/logo_app_bar.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 createState() => _StatisticState(); } class _StatisticState extends State { final ScrollController _scrollController = ScrollController(); bool _isAnimating = false; @override void initState() { _scrollController.addListener(() { _handleAnimations(); }); final state = context.read(); state.addListener(() { if (state.shouldColapse && mounted) { _handleAnimations(true); state.shouldColapse = false; } }); state.init(); super.initState(); } @override Widget build(BuildContext context) { return Consumer( builder: (context, state, child) => Stack( children: [ CustomScrollView( physics: _isAnimating ? const NeverScrollableScrollPhysics() : const ClampingScrollPhysics(), controller: _scrollController, slivers: [ const SliverToBoxAdapter(child: LogoAppBar()), if (state.appState != AppState.failed) const SliverToBoxAdapter( child: SizedBox(height: 180), ), 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.subtitle1, color: Theme.of(context).colorScheme.title, ), ), ), ), ), SliverStateHandler( 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: 144, rightPadding: 300, ), if (state.appState != AppState.failed) CategoriesList( 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(); state.selectedCategoryId = 0; if (category.id != 0) { state.selectedCategoryId = category.id; } state.getStatistic(); } void _handleAnimations([bool forceAnimate = false]) async { final state = context.read(); if (_isAnimating) return; final double position = _scrollController.offset; if (position > 5 && !state.isColapsed || forceAnimate) { state.isScrolled = true; _isAnimating = true; setState(() {}); await _scrollController.animateTo( 228, duration: DesignConfig.mediumAnimationDuration, curve: Curves.easeIn, ); _isAnimating = false; setState(() {}); } else if (position < min(_scrollController.position.maxScrollExtent, 228) && state.isColapsed) { state.isScrolled = false; _isAnimating = true; setState(() {}); await _scrollController.animateTo( 0, duration: DesignConfig.mediumAnimationDuration, curve: Curves.easeIn, ); _isAnimating = false; setState(() {}); } } @override void dispose() { _scrollController.dispose(); super.dispose(); } }