// ignore_for_file: prefer_const_constructors import 'dart:async'; import 'dart:math'; import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/models/enums.dart'; import 'package:didvan/models/requests/radar.dart'; import 'package:didvan/models/view/action_sheet_data.dart'; import 'package:didvan/pages/home/radar/radar_state.dart'; import 'package:didvan/pages/home/radar/widgets/categories_gird.dart'; import 'package:didvan/pages/home/radar/widgets/categories_list.dart'; import 'package:didvan/pages/home/widgets/radar_overview.dart'; import 'package:didvan/widgets/animated_visibility.dart'; import 'package:didvan/pages/home/widgets/search_field.dart'; import 'package:didvan/pages/home/widgets/logo_app_bar.dart'; import 'package:didvan/utils/action_sheet.dart'; import 'package:didvan/pages/home/widgets/date_picker_button.dart'; import 'package:didvan/widgets/didvan/checkbox.dart'; import 'package:didvan/widgets/didvan/text.dart'; import 'package:didvan/widgets/item_title.dart'; import 'package:didvan/widgets/state_handlers/empty_result.dart'; import 'package:didvan/widgets/state_handlers/sliver_state_handler.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; class Radar extends StatefulWidget { const Radar({Key? key}) : super(key: key); @override State createState() => _RadarState(); } class _RadarState extends State { final ScrollController _scrollController = ScrollController(); final _focusNode = FocusNode(); bool _isAnimating = false; Timer? _timer; @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 ScrollPhysics(), controller: _scrollController, slivers: [ const SliverToBoxAdapter(child: LogoAppBar()), if (state.appState != AppState.failed) SliverPadding( padding: const EdgeInsets.only( left: 16, right: 16, bottom: 16, ), sliver: SliverToBoxAdapter( child: SearchField( focusNode: _focusNode, isFiltered: state.filtering, title: 'رادار', onChanged: _onChanged, onFilterButtonPressed: _showFilterBottomSheet, ), ), ), if (!state.filtering && !state.searching && state.appState != AppState.failed) const SliverToBoxAdapter( child: SizedBox(height: 276), ), if (state.appState != AppState.failed) SliverPadding( padding: const EdgeInsets.only(right: 16, bottom: 20), sliver: SliverToBoxAdapter( child: Align( alignment: Alignment.centerRight, child: AnimatedVisibility( isVisible: !state.isColapsed && !state.searching && !state.filtering, duration: DesignConfig.lowAnimationDuration, child: DidvanText( 'آخرین رصد', style: Theme.of(context).textTheme.subtitle1, color: Theme.of(context).colorScheme.title, ), ), ), ), ), SliverStateHandler( onRetry: () => state.getRadarOverviewDatas(page: state.page), state: state, itemPadding: const EdgeInsets.only( bottom: 20, left: 16, right: 16, ), enableEmptyState: state.radars.isEmpty, emptyState: Padding( padding: const EdgeInsets.only(bottom: 120), child: EmptyResult(onNewSearch: () => _focusNode.requestFocus()), ), placeholder: RadarOverview.placeholder, builder: (context, state, index) { final radar = state.radars[index]; return RadarOverview( radar: radar, onMarkChanged: (id, value) => state.changeMark(id, value), onCommentsChanged: (id, count) => state.onCommentsChanged(id, count), radarRequestArgs: RadarRequestArgs( page: state.page, categories: List.from(state.selectedCats.map((cat) => cat.id)), endDate: state.endDate, isSingleItem: false, search: state.search, startDate: state.startDate, ), ); }, childCount: state.radars.length, ), if (state.radars.length == 1) const SliverToBoxAdapter( child: SizedBox(height: 320), ), ], ), if (state.appState != AppState.failed) CategoriesRow1(), if (state.appState != AppState.failed) CategoriesRow2(), if (state.appState != AppState.failed && !state.searching) CategoriesList(), ], ), ); } void _onChanged(String value) { final state = context.read(); if (value.length < 4 && value.isNotEmpty || state.lastSearch == value) { return; } _timer?.cancel(); _timer = Timer(const Duration(seconds: 1), () { state.search = value; state.getRadarOverviewDatas(page: 1); }); } void _handleAnimations([bool forceAnimate = false]) async { final state = context.read(); if (_isAnimating || state.filtering || state.searching) return; final double position = _scrollController.offset; if (position > 5 && !state.isColapsed || forceAnimate) { state.isScrolled = true; _isAnimating = true; setState(() {}); await _scrollController.animateTo( 380, duration: DesignConfig.mediumAnimationDuration, curve: Curves.easeIn, ); _isAnimating = false; setState(() {}); } else if (position < min(_scrollController.position.maxScrollExtent, 380) && state.isColapsed) { state.isScrolled = false; _isAnimating = true; setState(() {}); await _scrollController.animateTo( 0, duration: DesignConfig.mediumAnimationDuration, curve: Curves.easeIn, ); _isAnimating = false; setState(() {}); } } Future _showFilterBottomSheet() async { final state = context.read(); await ActionSheetUtils.showBottomSheet( data: ActionSheetData( title: 'فیلتر جستجو', smallDismissButton: true, titleIcon: DidvanIcons.filter_regular, dismissTitle: 'حذف فیلتر', confrimTitle: 'نمایش نتایج', onDismissed: () => state.resetFilters(false), onConfirmed: () => state.getRadarOverviewDatas(page: 1), content: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ ItemTitle( title: 'تاریخ رادار', style: Theme.of(context).textTheme.bodyText2, icon: DidvanIcons.calendar_range_regular, ), const SizedBox(height: 8), StatefulBuilder( builder: (context, setState) => Row( children: [ DatePickerButton( initialValue: state.startDate, emptyText: 'از تاریخ', onPicked: (date) => setState(() => state.startDate = date), lastDate: state.endDate, ), const SizedBox(width: 8), DatePickerButton( initialValue: state.endDate, emptyText: 'تا تاریخ', onPicked: (date) => setState(() => state.endDate = date), firstDate: state.startDate, ), ], ), ), const SizedBox(height: 28), ItemTitle( title: 'دسته بندی', icon: DidvanIcons.radar_regular, style: Theme.of(context).textTheme.bodyText2, ), const SizedBox(height: 12), Wrap( children: [ for (var i = 0; i < state.categories.length; i++) SizedBox( width: (MediaQuery.of(context).size.width - 40) / 2, child: DidvanCheckbox( title: state.categories[i].title, value: state.selectedCats.contains(state.categories[i]), onChanged: (value) { if (value) { state.selectedCats.add(state.categories[i]); return; } state.selectedCats.remove(state.categories[i]); }, ), ), ], ), ], ), ), ); } @override void dispose() { _focusNode.dispose(); _scrollController.dispose(); super.dispose(); } }