// ignore_for_file: deprecated_member_use import 'dart:async'; import 'dart:math'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/models/category.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/views/radar/radar_state.dart'; import 'package:didvan/views/widgets/date_picker_button.dart'; import 'package:didvan/views/widgets/didvan/scaffold.dart'; import 'package:didvan/utils/action_sheet.dart'; import 'package:didvan/views/widgets/overview/radar.dart'; import 'package:didvan/views/widgets/item_title.dart'; import 'package:didvan/views/widgets/state_handlers/empty_result.dart'; import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart'; import 'package:didvan/views/widgets/home_app_bar.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:provider/provider.dart'; class Radar extends StatefulWidget { const Radar({Key? key}) : super(key: key); @override State createState() => _RadarStateView(); } class _RadarStateView extends State { Timer? _timer; final _focusNode = FocusNode(); @override void initState() { context.read().init(); super.initState(); } String _getCategoryIcon(int categoryId, bool isDark) { switch (categoryId) { case 1: return 'lib/assets/icons/Economic3.svg'; case 2: return 'lib/assets/icons/Siais2.svg'; case 3: return 'lib/assets/icons/Fanavari2.svg'; case 4: return 'lib/assets/icons/KasboKar2.svg'; case 5: return 'lib/assets/icons/ZistMohit2.svg'; case 6: return 'lib/assets/icons/Ejtemaei2.svg'; default: return 'lib/assets/icons/Kasb o Kar.svg'; } } @override Widget build(BuildContext context) { final state = context.watch(); final theme = Theme.of(context); final colorScheme = theme.colorScheme; final isDark = theme.brightness == Brightness.dark; final showCategories = !state.filtering && !state.searching; return DidvanScaffold( padding: EdgeInsets.zero, appBarData: null, slivers: [ SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.only(top: 12.0), child: HomeAppBar( showBackButton: false, title: 'پویش افق', showSearchField: state.appState != AppState.failed, onSearchChanged: _onChanged, onFilterPressed: _showFilterBottomSheet, searchFocusNode: _focusNode, isFiltered: state.filtering, searchValue: state.search, ), ), ), SliverAppBar( pinned: true, automaticallyImplyLeading: false, backgroundColor: colorScheme.surface, elevation: 0, centerTitle: false, titleSpacing: 16, title: Text( 'پویش‌ افق', style: theme.textTheme.headlineSmall?.copyWith( color: isDark ? Colors.white : const Color.fromARGB(255, 0, 53, 70), fontWeight: FontWeight.bold, fontSize: 19), ), actions: [ IconButton( onPressed: () => Navigator.of(context).pop(), icon: SvgPicture.asset( 'lib/assets/icons/arrow-left.svg', width: 30, height: 30, colorFilter: ColorFilter.mode( isDark ? Colors.white70 : Theme.of(context).colorScheme.caption, BlendMode.srcIn), ), ), const SizedBox(width: 8), ], ), SliverToBoxAdapter( child: AnimatedSwitcher( duration: const Duration(milliseconds: 500), switchInCurve: Curves.easeOutCubic, switchOutCurve: Curves.easeInCubic, transitionBuilder: (Widget child, Animation animation) { return SizeTransition( sizeFactor: animation, axisAlignment: -1.0, child: FadeTransition( opacity: animation, child: child, ), ); }, child: showCategories ? Column( key: const ValueKey('categories_column'), children: [ if (state.categories.length >= 3) Padding( padding: const EdgeInsets.only( top: 12, left: 10, right: 10), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: state.categories .sublist(0, 3) .map((category) => _buildCoolCategoryItem( category, isDark, state, theme)) .toList(), ), ), if (state.categories.length >= 6) Padding( padding: const EdgeInsets.only( top: 12, left: 10, right: 10), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: state.categories .sublist(3, 6) .map((category) => _buildCoolCategoryItem( category, isDark, state, theme)) .toList(), ), ), const SizedBox(height: 20), ], ) : const SizedBox.shrink(key: ValueKey('empty_categories')), ), ), SliverStateHandler( centerEmptyState: false, onRetry: () => state.getRadars(page: state.page), state: state, childCount: min(state.visibleCount, state.radars.length) + (_hasMoreItems(state) ? 1 : 0), builder: (context, state, index) { final currentDisplayCount = min(state.visibleCount, state.radars.length); if (index == currentDisplayCount && _hasMoreItems(state)) { return _buildLoadMoreButton(context, state); } if (index >= state.radars.length) { return _SlideFadeTransition( index: index, child: RadarOverview.placeholder); } final radar = state.radars[index]; return _SlideFadeTransition( index: index, child: RadarOverview( radar: radar, onMarkChanged: state.changeMark, onLikedChanged: state.changeLike, 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, ), ), ); }, enableEmptyState: state.radars.isEmpty && state.appState != AppState.busy, emptyState: EmptyResult( onNewSearch: () => _focusNode.requestFocus(), ), itemPadding: const EdgeInsets.only( bottom: 20, left: 16, right: 16, ), placeholder: _SlideFadeTransition(index: 0, child: RadarOverview.placeholder), ), ], ); } bool _hasMoreItems(RadarState state) { return state.radars.length > state.visibleCount || state.page < state.lastPage; } Widget _buildLoadMoreButton(BuildContext context, RadarState state) { if (state.appState == AppState.busy && state.radars.length <= state.visibleCount) { return const Padding( padding: EdgeInsets.all(16.0), child: Center(child: CircularProgressIndicator()), ); } return Padding( padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 16.0), child: Center( child: GestureDetector( onTap: () { state.loadMore(); }, child: Container( padding: const EdgeInsets.symmetric( horizontal: 50.0, vertical: 12.0, ), decoration: BoxDecoration( color: const Color.fromARGB(255, 0, 126, 167), borderRadius: BorderRadius.circular(15), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ SvgPicture.asset( 'lib/assets/icons/element-plus.svg', colorFilter: const ColorFilter.mode(Colors.white, BlendMode.srcIn), width: 20, ), const SizedBox(width: 8), const Text( 'بارگذاری بیشتر', style: TextStyle( color: Colors.white, fontSize: 14, fontWeight: FontWeight.bold, ), ), ], ), ), ), ), ); } Widget _buildCoolCategoryItem( CategoryData category, bool isDark, RadarState state, ThemeData theme) { final isSelected = state.selectedCats.any((element) => element.id == category.id); category.asset = _getCategoryIcon(category.id, isDark); return Expanded( child: Padding( padding: const EdgeInsets.all(4.0), child: GestureDetector( onTap: () => _onCategorySelected(category), child: Column( mainAxisSize: MainAxisSize.min, children: [ AnimatedContainer( duration: const Duration(milliseconds: 300), curve: Curves.fastOutSlowIn, padding: EdgeInsets.zero, decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), border: Border.all( color: isSelected ? theme.colorScheme.primary : Colors.transparent, width: 2, ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), blurRadius: 8, offset: const Offset(0, 3), spreadRadius: 0, ), ], ), child: ClipRRect( borderRadius: BorderRadius.circular(20), child: SvgPicture.asset( category.asset!, width: 70, height: 70, ), ), ), const SizedBox(height: 3), Text( category.label, textAlign: TextAlign.center, maxLines: 1, overflow: TextOverflow.ellipsis, style: theme.textTheme.bodySmall?.copyWith( color: const Color.fromARGB(255, 27, 60, 89), fontWeight: isSelected ? FontWeight.bold : FontWeight.bold, fontSize: 13, ), ), ], ), ), ), ); } void _onChanged(String value) { final state = context.read(); if (value.length < 3 && value.isNotEmpty || state.lastSearch == value) { return; } _timer?.cancel(); _timer = Timer(const Duration(seconds: 1), () { state.search = value; state.getRadars(page: 1); }); } void _onCategorySelected(CategoryData category) { final state = context.read(); final isAlreadySelected = state.selectedCats.any((cat) => cat.id == category.id); state.selectedCats.clear(); if (!isAlreadySelected && category.id != 0) { state.selectedCats.add(category); } state.getRadars(page: 1); } Future _showFilterBottomSheet() async { final state = context.read(); await ActionSheetUtils(context).showBottomSheet( data: ActionSheetData( title: 'فیلتر جستجو', smallDismissButton: true, titleIcon: DidvanIcons.filter_regular, dismissTitle: 'حذف فیلتر', confrimTitle: 'نمایش نتایج', onDismissed: () => state.resetFilters(false), onConfirmed: () => state.getRadars(page: 1), content: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ ItemTitle( title: 'تاریخ ایجاد', style: Theme.of(context).textTheme.bodyMedium, 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.category_regular, // style: Theme.of(context).textTheme.bodyMedium, // ), // const SizedBox(height: 12), // StatefulBuilder( // builder: (context, setState) => Column( // children: [ // for (var row = 0; row < (state.categories.length + 1) ~/ 2; row++) // Padding( // padding: const EdgeInsets.only(bottom: 8), // child: Row( // children: [ // Expanded( // child: Align( // alignment: Alignment.centerRight, // child: DidvanCheckbox( // title: state.categories[row * 2].label, // value: state.selectedCats // .any((cat) => cat.id == state.categories[row * 2].id), // onChanged: (value) { // setState(() { // if (value) { // state.selectedCats.add(state.categories[row * 2]); // } else { // state.selectedCats.removeWhere( // (cat) => cat.id == state.categories[row * 2].id); // } // }); // }, // ), // ), // ), // const SizedBox(width: 8), // Expanded( // child: row * 2 + 1 < state.categories.length // ? Align( // alignment: Alignment.centerRight, // child: DidvanCheckbox( // title: state.categories[row * 2 + 1].label, // value: state.selectedCats.any( // (cat) => cat.id == state.categories[row * 2 + 1].id), // onChanged: (value) { // setState(() { // if (value) { // state.selectedCats.add(state.categories[row * 2 + 1]); // } else { // state.selectedCats.removeWhere((cat) => // cat.id == state.categories[row * 2 + 1].id); // } // }); // }, // ), // ) // : const SizedBox.shrink(), // ), // ], // ), // ), // ], // ), // ), ], ), ), ); } @override void dispose() { _focusNode.dispose(); super.dispose(); } } class _SlideFadeTransition extends StatefulWidget { final Widget child; final int index; const _SlideFadeTransition({ Key? key, required this.child, required this.index, }) : super(key: key); @override State<_SlideFadeTransition> createState() => _SlideFadeTransitionState(); } class _SlideFadeTransitionState extends State<_SlideFadeTransition> with SingleTickerProviderStateMixin { late final AnimationController _controller; late final Animation _opacity; late final Animation _offset; @override void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: const Duration(milliseconds: 600), ); _opacity = CurvedAnimation(parent: _controller, curve: Curves.easeOut); _offset = Tween(begin: const Offset(0, 0.1), end: Offset.zero) .animate( CurvedAnimation(parent: _controller, curve: Curves.easeOutCubic)); Future.delayed(Duration(milliseconds: 50 * (widget.index % 5)), () { if (mounted) { _controller.forward(); } }); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return FadeTransition( opacity: _opacity, child: SlideTransition( position: _offset, child: widget.child, ), ); } }