From aa01c941a7ae52b20de9dee712b4ac961227443e Mon Sep 17 00:00:00 2001 From: MohammadTaha Basiri Date: Tue, 11 Jan 2022 15:57:41 +0330 Subject: [PATCH] D1APP-39 news (dynamic) (beta) --- lib/pages/home/news/news.dart | 179 ++++++++++++++++++--- lib/pages/home/news/news_state.dart | 59 ++++++- lib/pages/home/news/widgets/news_item.dart | 102 +++++++----- 3 files changed, 269 insertions(+), 71 deletions(-) diff --git a/lib/pages/home/news/news.dart b/lib/pages/home/news/news.dart index 337c4a6..32f5375 100644 --- a/lib/pages/home/news/news.dart +++ b/lib/pages/home/news/news.dart @@ -1,14 +1,42 @@ -import 'package:didvan/constants/app_icons.dart'; -import 'package:didvan/pages/home/news/widgets/news_item.dart'; -import 'package:didvan/pages/home/radar/widgets/search_field.dart'; -import 'package:didvan/pages/home/widgets/logo_app_bar.dart'; -import 'package:flutter/material.dart'; +import 'dart:async'; -class News extends StatelessWidget { +import 'package:didvan/constants/app_icons.dart'; +import 'package:didvan/models/view/action_sheet_data.dart'; +import 'package:didvan/pages/home/news/news_state.dart'; +import 'package:didvan/pages/home/news/widgets/news_item.dart'; +import 'package:didvan/utils/action_sheet.dart'; +import 'package:didvan/widgets/date_picker_button.dart'; +import 'package:didvan/widgets/item_title.dart'; +import 'package:didvan/widgets/search_field.dart'; +import 'package:didvan/pages/home/widgets/logo_app_bar.dart'; +import 'package:didvan/widgets/didvan/card.dart'; +import 'package:didvan/widgets/didvan/divider.dart'; +import 'package:didvan/widgets/shimmer_placeholder.dart'; +import 'package:didvan/widgets/sliver_state_handler.dart'; +import 'package:flutter/material.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; + + @override + void initState() { + Future.delayed(Duration.zero, () { + context.read().getNews(page: 1); + }); + super.initState(); + } + @override Widget build(BuildContext context) { + final state = context.watch(); return Scaffold( body: CustomScrollView( slivers: [ @@ -16,34 +44,133 @@ class News extends StatelessWidget { SliverPadding( padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16), sliver: SliverToBoxAdapter( - child: Row( + child: SearchField( + title: 'اخبار', + onChanged: _onChanged, + onFilterButtonPressed: _showFilterBottomSheet, + ), + ), + ), + SliverStateHandler( + state: state, + builder: (context, state, index) => NewsItem( + news: state.news[index], + ), + childCount: state.news.length, + itemPadding: const EdgeInsets.only(left: 16, right: 16, bottom: 16), + placeholder: const _NewsItemPlaceholder(), + ), + ], + ), + ); + } + + 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.getNews(page: 1); + }); + } + + Future _showFilterBottomSheet() async { + final state = context.read(); + await ActionSheetUtils.showBottomSheet( + data: ActionSheetData( + title: 'فیلتر جستجو', + smallDismissButton: true, + titleIcon: DidvanIcons.filter_regular, + dismissTitle: 'حذف فیلتر', + confrimTitle: 'نمایش نتایج', + onDismissed: state.resetFilters, + onConfirmed: () => state.getNews(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: [ - Expanded( - child: SearchField( - title: 'اخبار', - onChanged: (value) {}, - ), + DatePickerButton( + initialValue: state.startDate, + emptyText: 'از تاریخ', + onPicked: (date) { + setState(() => state.startDate = date); + }, + lastDate: state.endDate, ), const SizedBox(width: 8), - GestureDetector( - onTap: () {}, - child: const Icon( - DidvanIcons.filter_regular, - size: 32, - ), + DatePickerButton( + initialValue: state.endDate, + emptyText: 'تا تاریخ', + onPicked: (date) => setState(() => state.endDate = date), + firstDate: state.startDate, ), ], ), ), - ), - SliverPadding( - padding: const EdgeInsets.symmetric(horizontal: 16), - sliver: SliverList( - delegate: SliverChildBuilderDelegate( - (context, index) => const NewsItem(), - childCount: 10, + ], + ), + ), + ); + } +} + +class _NewsItemPlaceholder extends StatelessWidget { + const _NewsItemPlaceholder({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return DidvanCard( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const ShimmerPlaceholder(height: 64, width: 64), + const SizedBox(width: 8), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: const [ + ShimmerPlaceholder(height: 18, width: 200), + SizedBox(height: 8), + ShimmerPlaceholder(height: 18, width: 100), + ], ), - ), + ], + ), + const SizedBox(height: 12), + const ShimmerPlaceholder( + height: 16, + width: double.infinity, + ), + const SizedBox(height: 8), + const ShimmerPlaceholder( + height: 16, + width: double.infinity, + ), + const SizedBox(height: 8), + const ShimmerPlaceholder( + height: 16, + width: 100, + ), + const DidvanDivider(verticalPadding: 8), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: const [ + ShimmerPlaceholder(height: 12, width: 150), + ShimmerPlaceholder(height: 24, width: 24), + ], ), ], ), diff --git a/lib/pages/home/news/news_state.dart b/lib/pages/home/news/news_state.dart index 6717c21..ab22631 100644 --- a/lib/pages/home/news/news_state.dart +++ b/lib/pages/home/news/news_state.dart @@ -1,3 +1,60 @@ +import 'package:didvan/models/enums.dart'; +import 'package:didvan/models/news_overview.dart'; import 'package:didvan/providers/core_provider.dart'; +import 'package:didvan/services/network/request.dart'; +import 'package:didvan/services/network/request_helper.dart'; -class NewsState extends CoreProvier {} +class NewsState extends CoreProvier { + bool isFiltering = false; + String? search; + String? lastSearch; + String? startDate; + String? endDate; + + final List news = []; + + void resetFilters() { + startDate = null; + endDate = null; + getNews(page: 1); + } + + Future getNews({ + required int page, + }) async { + if (search != '' && search != null || filterApplied) { + lastSearch = search; + isFiltering = true; + news.clear(); + } else { + isFiltering = false; + } + lastSearch = search; + appState = AppState.busy; + final service = RequestService( + RequestHelper.newsOverviews( + page: 1, + startDate: startDate?.split(' ').first, + endDate: endDate?.split(' ').first, + search: search, + ), + ); + await service.httpGet(); + if (service.isSuccess) { + final newsList = service.result['news']; + for (var i = 0; i < newsList.length; i++) { + news.add(NewsOverview.fromJson(newsList[i])); + } + appState = AppState.idle; + return; + } + appState = AppState.failed; + } + + Future markNews(int id) async { + final service = RequestService(RequestHelper.markNews(id)); + await service.post(); + } + + bool get filterApplied => startDate != null || endDate != null; +} diff --git a/lib/pages/home/news/widgets/news_item.dart b/lib/pages/home/news/widgets/news_item.dart index 4f722b1..87b8fd6 100644 --- a/lib/pages/home/news/widgets/news_item.dart +++ b/lib/pages/home/news/widgets/news_item.dart @@ -1,62 +1,76 @@ import 'package:didvan/constants/app_icons.dart'; +import 'package:didvan/models/news_overview.dart'; +import 'package:didvan/pages/home/news/news_state.dart'; import 'package:didvan/routes/routes.dart'; import 'package:didvan/widgets/didvan/card.dart'; import 'package:didvan/widgets/didvan/divider.dart'; +import 'package:didvan/widgets/didvan/icon_button.dart'; import 'package:didvan/widgets/didvan/text.dart'; import 'package:didvan/widgets/skeletun_image.dart'; import 'package:flutter/material.dart'; +import 'package:persian_number_utility/persian_number_utility.dart'; +import 'package:provider/provider.dart'; class NewsItem extends StatelessWidget { - const NewsItem({Key? key}) : super(key: key); + final NewsOverview news; + const NewsItem({Key? key, required this.news}) : super(key: key); @override Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.only(bottom: 16), - child: DidvanCard( - onTap: () => Navigator.of(context).pushNamed(Routes.newsDetails), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - const SkeletonImage( - imageUrl: 'https://wallpapercave.com/wp/wp9373116.jpg', - width: 64, + final state = context.read(); + return DidvanCard( + onTap: () => Navigator.of(context).pushNamed( + Routes.radarDetails, + arguments: { + 'state': state, + 'id': news.id, + }, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + SkeletonImage( + imageUrl: news.image, + width: 64, + height: 64, + ), + const SizedBox(width: 8), + Expanded( + child: SizedBox( height: 64, - ), - const SizedBox(width: 8), - Expanded( - child: SizedBox( - height: 64, - child: DidvanText( - 'بلاتکلیفی بازار فولاد به دلیل کاهش تقاضای جهانی', - style: Theme.of(context).textTheme.bodyText1, - ), + child: DidvanText( + news.title, + style: Theme.of(context).textTheme.bodyText1, ), ), - ], - ), - const SizedBox(height: 8), - const DidvanText( - 'صنعت فولاد جوادی مجد سلیمی است پس باید به آن توجه زیادی شود تا بازار به انفجار نرسد. پس جواد مهربانگو باشیم.', - maxLines: 3, - ), - const DidvanDivider(verticalPadding: 8), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - DidvanText( - 'پایگاه خبری فولاد ایران / 2 ساعت پیش', - style: Theme.of(context).textTheme.overline, - ), - const Icon( - DidvanIcons.bookmark_regular, - ), - ], - ), - ], - ), + ), + ], + ), + const SizedBox(height: 8), + DidvanText( + news.description, + maxLines: 3, + ), + const DidvanDivider(verticalPadding: 8), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + DidvanText( + '${news.reference} | ${DateTime.parse(news.createdAt).toPersianDateStr()}', + style: Theme.of(context).textTheme.overline, + ), + DidvanIconButton( + icon: news.marked + ? DidvanIcons.bookmark_solid + : DidvanIcons.bookmark_regular, + gestureSize: 32, + onPressed: () => state.markNews(news.id), + ), + ], + ), + ], ), ); }