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/news.dart'; import 'package:didvan/models/view/action_sheet_data.dart'; import 'package:didvan/utils/action_sheet.dart'; import 'package:didvan/views/news/news_state.dart'; import 'package:didvan/views/widgets/date_picker_button.dart'; import 'package:didvan/views/widgets/didvan/checkbox.dart'; import 'package:didvan/views/widgets/didvan/scaffold.dart'; import 'package:didvan/views/widgets/home_app_bar.dart'; import 'package:didvan/views/widgets/item_title.dart'; import 'package:didvan/views/widgets/overview/news.dart'; import 'package:didvan/views/widgets/state_handlers/empty_result.dart'; import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:provider/provider.dart'; class News extends StatefulWidget { const News({Key? key}) : super(key: key); @override State createState() => _NewsState(); } class _NewsState extends State { Timer? _timer; final _focusNode = FocusNode(); @override void initState() { context.read().init(); super.initState(); } @override Widget build(BuildContext context) { final state = context.watch(); final theme = Theme.of(context); final colorScheme = theme.colorScheme; return DidvanScaffold( padding: EdgeInsets.zero, appBarData: null, slivers: [ SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.only(top: 12.0), child: HomeAppBar( showBackButton: false, showSearchField: state.appState != AppState.failed, onSearchChanged: _onChanged, onFilterPressed: _showFilterBottomSheet, searchFocusNode: _focusNode, isFiltered: state.isFiltering, 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: DesignConfig.isDark? const Color.fromARGB(255, 0, 90, 119) : 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( Theme.of(context).colorScheme.caption, BlendMode.srcIn), ), ), const SizedBox(width: 8), ], ), const SliverToBoxAdapter( child: SizedBox( height: 16, ), ), SliverStateHandler( centerEmptyState: false, onRetry: () => state.getNews(page: state.page), state: state, childCount: min(state.visibleCount, state.news.length) + (_hasMoreItems(state) ? 1 : 0), builder: (context, state, index) { final currentDisplayCount = min(state.visibleCount, state.news.length); if (index == currentDisplayCount && _hasMoreItems(state)) { return _buildLoadMoreButton(context, state); } if (index >= state.news.length) { return NewsOverview.placeholder; } final news = state.news[index]; return NewsOverview( news: news, onMarkChanged: state.onMarkChanged, newsRequestArgs: NewsRequestArgs( page: state.page, endDate: state.endDate, startDate: state.startDate, search: state.search, ), onLikedChanged: state.onLikedChanged, ); }, enableEmptyState: state.news.isEmpty && state.appState != AppState.busy, emptyState: EmptyResult( onNewSearch: () => _focusNode.requestFocus(), ), itemPadding: const EdgeInsets.only(left: 16, right: 16, bottom: 16), placeholder: NewsOverview.placeholder, ), ], ); } bool _hasMoreItems(NewsState state) { return state.news.length > state.visibleCount || state.page < state.lastPage; } Widget _buildLoadMoreButton(BuildContext context, NewsState state) { if (state.appState == AppState.busy && state.news.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, ), ), ], ), ), ), ), ); } 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.getNews(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, onConfirmed: () => state.getNews(page: 1), content: StatefulBuilder( builder: (context, setState) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ ItemTitle( title: 'تاریخ', style: Theme.of(context).textTheme.bodyMedium, icon: DidvanIcons.calendar_range_regular, ), // const SizedBox(height: 8), 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), 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].label, value: state.selectedCats .any((cat) => cat.id == state.categories[i].id), onChanged: (value) { setState(() { if (value) { state.selectedCats.add(state.categories[i]); } else { state.selectedCats.removeWhere( (cat) => cat.id == state.categories[i].id); } }); }, ), ), ], ), ], ); }, ), ), ); } }