diff --git a/lib/pages/home/radar/radar.dart b/lib/pages/home/radar/radar.dart index 3f89f29..a081bc3 100644 --- a/lib/pages/home/radar/radar.dart +++ b/lib/pages/home/radar/radar.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:math'; import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; @@ -9,7 +10,8 @@ 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/radar/widgets/radar_item.dart'; -import 'package:didvan/pages/home/radar/widgets/search_field.dart'; +import 'package:didvan/widgets/animated_visibility.dart'; +import 'package:didvan/widgets/search_field.dart'; import 'package:didvan/pages/home/widgets/logo_app_bar.dart'; import 'package:didvan/utils/action_sheet.dart'; import 'package:didvan/widgets/date_picker_button.dart'; @@ -32,20 +34,25 @@ class Radar extends StatefulWidget { class _RadarState extends State { final ScrollController _scrollController = ScrollController(); + // final ScrollController _categoriesScrollController = ScrollController(); - bool _isColapsed = false; bool _isAnimating = false; Timer? _timer; @override void initState() { - _scrollController.addListener(() async { + _scrollController.addListener(() { _handleAnimations(); }); - Future.delayed(Duration.zero, () { - context.read().getRadarOverviews(page: 1); + final state = context.read(); + state.addListener(() { + if (state.shouldColapse && mounted) { + _handleAnimations(true); + state.shouldColapse = false; + } }); + state.init(); super.initState(); } @@ -53,26 +60,6 @@ class _RadarState extends State { Widget build(BuildContext context) { return Scaffold( body: Consumer( - child: SliverPadding( - padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16), - sliver: SliverToBoxAdapter( - child: Row( - children: [ - Expanded( - child: SearchField(title: 'رادار', onChanged: _onChanged), - ), - const SizedBox(width: 8), - GestureDetector( - onTap: _showFilterBottomSheet, - child: const Icon( - DidvanIcons.filter_regular, - size: 32, - ), - ), - ], - ), - ), - ), builder: (context, state, child) => Stack( children: [ CustomScrollView( @@ -83,22 +70,44 @@ class _RadarState extends State { controller: _scrollController, slivers: [ const SliverToBoxAdapter(child: LogoAppBar()), - child!, - if (!state.isFiltering) - const SliverToBoxAdapter( - child: SizedBox(height: 284), + SliverPadding( + padding: const EdgeInsets.only( + left: 16, + right: 16, + bottom: 16, ), - if (state.radars.isNotEmpty && !state.isFiltering) - SliverPadding( - padding: const EdgeInsets.only(right: 16, bottom: 20), - sliver: SliverToBoxAdapter( - child: DidvanText( - 'آخرین رصد', - style: Theme.of(context).textTheme.subtitle1, - color: Theme.of(context).colorScheme.title, + sliver: SliverToBoxAdapter( + child: SearchField( + isFiltered: state.filtering, + title: 'رادار', + onChanged: _onChanged, + onFilterButtonPressed: _showFilterBottomSheet, + ), + ), + ), + if (!state.filtering && !state.searching) + const SliverToBoxAdapter( + child: SizedBox(height: 276), + ), + 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( state: state, itemPadding: const EdgeInsets.only( @@ -112,11 +121,23 @@ class _RadarState extends State { ), childCount: state.radars.length, ), + if (state.radars.length == 1) + const SliverToBoxAdapter( + child: SizedBox(height: 320), + ), ], ), - CategoriesRow1(isColapsed: _isColapsed || state.isFiltering), - CategoriesRow2(isColapsed: _isColapsed || state.isFiltering), - if (!state.isFiltering) CategoriesList(isColapsed: _isColapsed), + CategoriesRow1( + isColapsed: + state.isColapsed || state.searching || state.filtering, + ), + CategoriesRow2( + isColapsed: + state.isColapsed || state.searching || state.filtering, + ), + CategoriesList( + isColapsed: state.isColapsed && !state.searching, + ), ], ), ), @@ -125,39 +146,41 @@ class _RadarState extends State { void _onChanged(String value) { final state = context.read(); - state.resetFilters(shoudUpdate: false); - if (value.length < 4 && value.isNotEmpty) return; + if (value.length < 4 && value.isNotEmpty || state.lastSearch == value) { + return; + } _timer?.cancel(); - _timer = Timer(const Duration(seconds: 2), () { - state.getRadarOverviews( - page: 1, - search: value, - ); + _timer = Timer(const Duration(seconds: 1), () { + state.search = value; + state.getRadarOverviews(page: 1); }); } - void _handleAnimations() async { - if (_isAnimating) return; - final double position = _scrollController.position.pixels; - if (position > 5 && !_isColapsed) { - _isColapsed = true; + 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.ease, + curve: Curves.easeIn, ); _isAnimating = false; setState(() {}); - } else if (position < 380 && _isColapsed) { - _isColapsed = false; + } 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.ease, + curve: Curves.easeIn, ); _isAnimating = false; setState(() {}); @@ -173,8 +196,8 @@ class _RadarState extends State { titleIcon: DidvanIcons.filter_regular, dismissTitle: 'حذف فیلتر', confrimTitle: 'نمایش نتایج', - onDismissed: state.resetFilters, - onConfirmed: () => state.getRadarOverviews(page: 1, filter: true), + onDismissed: () => state.resetFilters(false), + onConfirmed: () => state.getRadarOverviews(page: 1), content: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -234,6 +257,12 @@ class _RadarState extends State { ), ); } + + @override + void dispose() { + _scrollController.dispose(); + super.dispose(); + } } class _RadarItemPlaceholder extends StatelessWidget { @@ -252,7 +281,7 @@ class _RadarItemPlaceholder extends StatelessWidget { height: 16, ), const SizedBox(height: 8), - const ShimmerPlaceholder(height: 140), + const AspectRatio(aspectRatio: 16 / 9, child: ShimmerPlaceholder()), const SizedBox(height: 8), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, diff --git a/lib/pages/home/radar/radar_state.dart b/lib/pages/home/radar/radar_state.dart index 0907298..81f4cf4 100644 --- a/lib/pages/home/radar/radar_state.dart +++ b/lib/pages/home/radar/radar_state.dart @@ -1,50 +1,58 @@ +import 'package:collection/collection.dart'; import 'package:didvan/constants/assets.dart'; import 'package:didvan/models/enums.dart'; -import 'package:didvan/models/radar_category.dart'; -import 'package:didvan/models/radar_overview/radar_overview.dart'; +import 'package:didvan/models/view/radar_category.dart'; +import 'package:didvan/models/radar_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 RadarState extends CoreProvier { - bool isFiltering = false; - String? lastSearch; + String search = ''; + String lastSearch = ''; String? startDate; String? endDate; + bool isScrolled = false; + bool shouldColapse = false; + final List _markQueue = []; final List selectedCats = []; - + List categories = []; final List radars = []; - void resetFilters({bool shoudUpdate = true}) { + bool get filtering => + selectedCats.length > 1 || startDate != null || endDate != null; + + bool get searching => search.isNotEmpty; + + bool get isColapsed => + (selectedCats.length == 1 && !filtering && isScrolled) || isScrolled; + + bool get isCategorySelected => selectedCats.length == 1 && !filtering; + + void resetFilters(bool isInit) { startDate = null; endDate = null; selectedCats.clear(); - if (shoudUpdate) { - getRadarOverviews(page: 1); + search = ''; + lastSearch = ''; + isScrolled = false; + if (!isInit) { + notifyListeners(); } } Future getRadarOverviews({ required int page, - String? search, - bool filter = false, }) async { - if (search != null && search.isNotEmpty) { - if (lastSearch == search && !filter) { - return; - } - isFiltering = true; - lastSearch = search; - } else { - isFiltering = false; - } + radars.clear(); + lastSearch = search; appState = AppState.busy; final RequestService service = RequestService( - RequestHelper.getRadarOverviews( + RequestHelper.radarOverviews( page: page, startDate: startDate?.split(' ').first, endDate: endDate?.split(' ').first, - search: search, + search: search == '' ? null : search, categories: selectedCats.map((e) => e.id).toList(), ), ); @@ -53,42 +61,84 @@ class RadarState extends CoreProvier { for (var i = 0; i < service.result['radars'].length; i++) { radars.add(RadarOverview.fromJson(service.result['radars'][i])); } + if (searching || filtering || isColapsed || isCategorySelected) { + shouldColapse = true; + } appState = AppState.idle; return; } + appState = AppState.failed; } - final List categories = [ - RadarCategory( - id: 1, - title: 'اقتصادی', - asset: Assets.economicCategoryIcon, - ), - RadarCategory( - id: 2, - title: 'سیاسی', - asset: Assets.politicalCategoryIcon, - ), - RadarCategory( - id: 3, - title: 'فناوری', - asset: Assets.techCategoryIcon, - ), - RadarCategory( - id: 4, - title: 'کسب و کار', - asset: Assets.businessCategoryIcon, - ), - RadarCategory( - id: 5, - title: 'زیست محیطی', - asset: Assets.enviromentalCategoryIcon, - ), - RadarCategory( - id: 6, - title: 'اجتماعی', - asset: Assets.socialCategoryIcon, - ), - ]; + Future markRadar(int id) async { + radars.firstWhere((element) => element.id == id).marked = true; + notifyListeners(); + _markQueue.add(MapEntry(id, true)); + Future.delayed(const Duration(milliseconds: 500), () async { + final MapEntry? lastChange = + _markQueue.lastWhereOrNull((item) => item.key == id); + if (lastChange == null) return; + if (lastChange.value) { + final service = RequestService(RequestHelper.markRadar(id)); + await service.post(); + _markQueue.removeWhere((element) => element.key == id); + } + }); + } + + Future unMarkRadar(int id) async { + radars.firstWhere((element) => element.id == id).marked = false; + notifyListeners(); + _markQueue.add(MapEntry(id, false)); + Future.delayed(const Duration(milliseconds: 500), () async { + final MapEntry? lastChange = + _markQueue.lastWhereOrNull((item) => item.key == id); + if (lastChange == null) return; + if (!lastChange.value) { + final service = RequestService(RequestHelper.markRadar(id)); + await service.delete(); + _markQueue.removeWhere((element) => element.key == id); + } + }); + } + + void init() { + resetFilters(true); + Future.delayed(Duration.zero, () { + getRadarOverviews(page: 1); + }); + categories = [ + RadarCategory( + id: 1, + title: 'اقتصادی', + asset: Assets.economicCategoryIcon, + ), + RadarCategory( + id: 2, + title: 'سیاسی', + asset: Assets.politicalCategoryIcon, + ), + RadarCategory( + id: 3, + title: 'فناوری', + asset: Assets.techCategoryIcon, + ), + RadarCategory( + id: 4, + title: 'کسب و کار', + asset: Assets.businessCategoryIcon, + ), + RadarCategory( + id: 5, + title: 'زیست محیطی', + asset: Assets.enviromentalCategoryIcon, + ), + RadarCategory( + id: 6, + title: 'اجتماعی', + asset: Assets.socialCategoryIcon, + ), + ]; + } } diff --git a/lib/pages/home/radar/widgets/categories_gird.dart b/lib/pages/home/radar/widgets/categories_gird.dart index cb51cd4..44d217e 100644 --- a/lib/pages/home/radar/widgets/categories_gird.dart +++ b/lib/pages/home/radar/widgets/categories_gird.dart @@ -12,10 +12,11 @@ class CategoriesRow1 extends StatelessWidget { Widget build(BuildContext context) { final MediaQueryData d = MediaQuery.of(context); return AnimatedPositioned( + curve: Curves.easeIn, duration: DesignConfig.mediumAnimationDuration, - top: isColapsed ? 12 : 176 + d.padding.top, - left: isColapsed ? -d.size.width : 0, - right: isColapsed ? d.size.width : 0, + top: isColapsed ? -60 : 300 + d.padding.top, + left: isColapsed ? -80 : 0, + right: isColapsed ? 124 : 0, child: Row( children: context .read() @@ -26,8 +27,7 @@ class CategoriesRow1 extends StatelessWidget { child: Padding( padding: const EdgeInsets.symmetric(horizontal: 6), child: CategoryItem( - title: category.title, - asset: category.asset, + category: category, isColapsed: isColapsed, ), ), @@ -51,10 +51,11 @@ class CategoriesRow2 extends StatelessWidget { Widget build(BuildContext context) { final MediaQueryData d = MediaQuery.of(context); return AnimatedPositioned( + curve: Curves.easeIn, duration: DesignConfig.mediumAnimationDuration, - top: isColapsed ? -60 : 300 + d.padding.top, - left: isColapsed ? -80 : 0, - right: isColapsed ? 124 : 0, + top: isColapsed ? 12 : 176 + d.padding.top, + left: isColapsed ? -d.size.width : 0, + right: isColapsed ? d.size.width : 0, child: Row( children: context .read() @@ -63,8 +64,7 @@ class CategoriesRow2 extends StatelessWidget { .map( (category) => Expanded( child: CategoryItem( - title: category.title, - asset: category.asset, + category: category, isColapsed: isColapsed, ), ), diff --git a/lib/pages/home/radar/widgets/categories_list.dart b/lib/pages/home/radar/widgets/categories_list.dart index 0db5e7d..8bdf7c0 100644 --- a/lib/pages/home/radar/widgets/categories_list.dart +++ b/lib/pages/home/radar/widgets/categories_list.dart @@ -7,25 +7,52 @@ import 'package:didvan/widgets/didvan/text.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -class CategoriesList extends StatelessWidget { +class CategoriesList extends StatefulWidget { final bool isColapsed; - const CategoriesList({Key? key, required this.isColapsed}) : super(key: key); + const CategoriesList({ + Key? key, + required this.isColapsed, + }) : super(key: key); + + @override + State createState() => _CategoriesListState(); +} + +class _CategoriesListState extends State { + final _scrollController = ScrollController(); + + int _lastSelectedCategoryId = 0; + + @override + void didUpdateWidget(covariant CategoriesList oldWidget) { + final RadarState state = context.read(); + if (state.selectedCats.isNotEmpty && + _lastSelectedCategoryId != state.selectedCats.first.id) { + _lastSelectedCategoryId = state.selectedCats.first.id; + _scrollController.animateTo( + state.selectedCats.first.id * 100, + duration: DesignConfig.lowAnimationDuration, + curve: Curves.easeIn, + ); + } + super.didUpdateWidget(oldWidget); + } @override Widget build(BuildContext context) { final MediaQueryData d = MediaQuery.of(context); final RadarState state = context.read(); - final List categories = []; - categories.addAll(state.categories.sublist(3, 6)); - categories.addAll(state.categories.sublist(0, 3)); return Positioned( top: 0, left: 0, right: 0, child: AnimatedCrossFade( - crossFadeState: - isColapsed ? CrossFadeState.showSecond : CrossFadeState.showFirst, + crossFadeState: widget.isColapsed + ? CrossFadeState.showSecond + : CrossFadeState.showFirst, duration: DesignConfig.mediumAnimationDuration, + reverseDuration: DesignConfig.lowAnimationDuration, + sizeCurve: Curves.easeIn, firstChild: const SizedBox(), secondChild: Container( height: 60 + d.padding.top, @@ -33,27 +60,28 @@ class CategoriesList extends StatelessWidget { color: Theme.of(context).colorScheme.surface, boxShadow: DesignConfig.defaultShadow, ), - child: SingleChildScrollView( - physics: const BouncingScrollPhysics(), - scrollDirection: Axis.horizontal, - padding: EdgeInsets.only( - top: d.padding.top + 12, - bottom: 12, - right: 12, - ), - child: Row( - children: [ - AnimatedVisibility( - isVisible: isColapsed, - duration: DesignConfig.mediumAnimationDuration, - child: _itemBuilder( + child: AnimatedVisibility( + isVisible: widget.isColapsed, + duration: DesignConfig.mediumAnimationDuration, + child: SingleChildScrollView( + controller: _scrollController, + // physics: const BouncingScrollPhysics(), + scrollDirection: Axis.horizontal, + padding: EdgeInsets.only( + top: d.padding.top + 12, + bottom: 12, + right: 12, + ), + child: Row( + children: [ + _itemBuilder( RadarCategory(title: 'همه', asset: '', id: 0), context, ), - ), - for (var category in categories) - _itemBuilder(category, context), - ], + for (var i = 0; i < state.categories.length; i++) + _itemBuilder(state.categories[i], context), + ], + ), ), ), ), @@ -62,24 +90,44 @@ class CategoriesList extends StatelessWidget { } Widget _itemBuilder(RadarCategory category, BuildContext context) { - return Container( - margin: const EdgeInsets.only(left: 12), - width: 100, - padding: const EdgeInsets.all(4), - alignment: Alignment.center, - child: FittedBox( - fit: BoxFit.scaleDown, - child: DidvanText( - category.title, - fontWeight: FontWeight.w600, - color: Theme.of(context).colorScheme.focusedBorder, + final state = context.read(); + return GestureDetector( + onTap: () async { + if (state.selectedCats.isNotEmpty && + state.selectedCats.first.id == category.id) return; + state.selectedCats.clear(); + if (category.id != 0) state.selectedCats.add(category); + await _scrollController.animateTo( + category.id * 100, + duration: DesignConfig.lowAnimationDuration, + curve: Curves.easeIn, + ); + state.getRadarOverviews(page: 1); + }, + child: Container( + margin: const EdgeInsets.only(left: 12), + width: 100, + padding: const EdgeInsets.all(4), + alignment: Alignment.center, + child: FittedBox( + fit: BoxFit.scaleDown, + child: DidvanText( + category.title, + fontWeight: FontWeight.w600, + color: Theme.of(context).colorScheme.focusedBorder, + ), ), - ), - decoration: BoxDecoration( - border: Border.all( - color: Theme.of(context).colorScheme.focusedBorder, + decoration: BoxDecoration( + color: state.selectedCats.length == 1 && + state.selectedCats.contains(category) || + category.id == 0 && state.selectedCats.isEmpty + ? Theme.of(context).colorScheme.focused + : null, + border: Border.all( + color: Theme.of(context).colorScheme.focusedBorder, + ), + borderRadius: DesignConfig.lowBorderRadius, ), - borderRadius: DesignConfig.lowBorderRadius, ), ); } diff --git a/lib/pages/home/radar/widgets/category_item.dart b/lib/pages/home/radar/widgets/category_item.dart index 9eebcea..7ec8494 100644 --- a/lib/pages/home/radar/widgets/category_item.dart +++ b/lib/pages/home/radar/widgets/category_item.dart @@ -1,63 +1,74 @@ import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; +import 'package:didvan/models/view/radar_category.dart'; +import 'package:didvan/pages/home/radar/radar_state.dart'; import 'package:didvan/widgets/animated_visibility.dart'; import 'package:didvan/widgets/didvan/text.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:provider/provider.dart'; class CategoryItem extends StatelessWidget { - final String title; - final String asset; + final RadarCategory category; final bool isColapsed; const CategoryItem({ Key? key, - required this.title, - required this.asset, required this.isColapsed, + required this.category, }) : super(key: key); @override Widget build(BuildContext context) { final Size ds = MediaQuery.of(context).size; return Center( - child: AnimatedContainer( - duration: DesignConfig.mediumAnimationDuration, - padding: isColapsed ? const EdgeInsets.all(4) : EdgeInsets.zero, - width: isColapsed ? 100 : ds.width / 3, - alignment: Alignment.center, - decoration: BoxDecoration( - borderRadius: DesignConfig.lowBorderRadius, - border: isColapsed - ? Border.all(color: Theme.of(context).colorScheme.focusedBorder) - : null, - ), - child: Column( - children: [ - AnimatedVisibility( - duration: DesignConfig.mediumAnimationDuration, - isVisible: !isColapsed, - child: Container( - width: ds.width / 5, - height: ds.width / 5, - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surface, - boxShadow: DesignConfig.defaultShadow, - borderRadius: DesignConfig.mediumBorderRadius, + child: GestureDetector( + onTap: () { + final state = context.read(); + state.selectedCats.clear(); + if (category.id != 0) { + state.selectedCats.add(category); + } + state.getRadarOverviews(page: 1); + }, + child: AnimatedContainer( + duration: DesignConfig.mediumAnimationDuration, + padding: isColapsed ? const EdgeInsets.all(4) : EdgeInsets.zero, + width: isColapsed ? 100 : ds.width / 3, + alignment: Alignment.center, + decoration: BoxDecoration( + borderRadius: DesignConfig.lowBorderRadius, + border: isColapsed + ? Border.all(color: Theme.of(context).colorScheme.focusedBorder) + : null, + ), + child: Column( + children: [ + AnimatedVisibility( + duration: DesignConfig.mediumAnimationDuration, + isVisible: !isColapsed, + child: Container( + width: ds.width / 5, + height: ds.width / 5, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + boxShadow: DesignConfig.defaultShadow, + borderRadius: DesignConfig.mediumBorderRadius, + ), + padding: const EdgeInsets.all(8), + child: SvgPicture.asset(category.asset), ), - padding: const EdgeInsets.all(8), - child: SvgPicture.asset(asset), ), - ), - const SizedBox( - height: 8, - ), - DidvanText( - title, - style: Theme.of(context).textTheme.subtitle2, - color: Theme.of(context).colorScheme.title, - ), - ], + const SizedBox( + height: 8, + ), + DidvanText( + category.title, + style: Theme.of(context).textTheme.subtitle2, + color: Theme.of(context).colorScheme.title, + ), + ], + ), ), ), ); diff --git a/lib/pages/home/radar/widgets/radar_item.dart b/lib/pages/home/radar/widgets/radar_item.dart index c8751f8..ffa6a1e 100644 --- a/lib/pages/home/radar/widgets/radar_item.dart +++ b/lib/pages/home/radar/widgets/radar_item.dart @@ -1,83 +1,118 @@ -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/radar_overview/radar_overview.dart'; +import 'package:didvan/models/radar_overview.dart'; +import 'package:didvan/pages/home/radar/radar_state.dart'; import 'package:didvan/routes/routes.dart'; +import 'package:didvan/widgets/bookmark_button.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:didvan/widgets/skeleton_image.dart'; import 'package:flutter/material.dart'; +import 'package:persian_number_utility/persian_number_utility.dart'; +import 'package:provider/provider.dart'; class RadarItem extends StatelessWidget { - final RadarOverview? radar; + final RadarOverview radar; const RadarItem({Key? key, required this.radar}) : super(key: key); @override Widget build(BuildContext context) { + final state = context.read(); return DidvanCard( - onTap: () => Navigator.of(context).pushNamed(Routes.radarDetails), + onTap: () => Navigator.of(context).pushNamed( + Routes.radarDetails, + arguments: { + 'state': state, + 'id': radar.id, + }, + ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Container( - padding: const EdgeInsets.all(4), - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.secondary, - borderRadius: DesignConfig.highBorderRadius, - ), - child: DidvanText( - 'برای مدیران', - style: Theme.of(context).textTheme.overline, - color: Theme.of(context).colorScheme.white, - ), - ), const SizedBox(height: 8), - const DidvanText( - 'نقش مهم فولاد در اقتصاد جهانی', + DidvanText( + radar.title, fontWeight: FontWeight.w600, ), const SizedBox(height: 8), - const SkeletonImage( - imageUrl: 'https://wallpapercave.com/wp/wp9373116.jpg', - width: double.infinity, - height: 140, + Stack( + children: [ + SkeletonImage( + imageUrl: radar.image, + aspectRatio: 16 / 9, + ), + if (radar.forManagers) + Positioned( + top: 0, + right: 0, + child: Container( + padding: const EdgeInsets.all(4), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.secondary, + borderRadius: const BorderRadius.only( + bottomLeft: Radius.circular(8), + topRight: Radius.circular(8), + ), + ), + child: DidvanText( + 'برای مدیران', + style: Theme.of(context).textTheme.overline, + color: Theme.of(context).colorScheme.white, + ), + ), + ), + ], ), const SizedBox(height: 8), Row( children: [ DidvanText( - 'رادار کسب و کار', + radar.categories.first.label, style: Theme.of(context).textTheme.overline, color: Theme.of(context).colorScheme.caption, ), const Spacer(), DidvanText( - 'هفته پیش | خواندن 5 دقیقه', + '${DateTime.parse(radar.createdAt).toPersianDateStr()} | خواندن ${radar.timeToRead} دقیقه', style: Theme.of(context).textTheme.overline, color: Theme.of(context).colorScheme.caption, ), ], ), const SizedBox(height: 8), - const DidvanText( - 'صنعت فولاد جوادی مجد سلیمی است پس باید به آن توجه زیادی شود تا بازار به انفجار نرسد. پس جواد مهربانگو باشیم...', + DidvanText( + radar.description, maxLines: 3, ), const DidvanDivider(), Row( - children: const [ - Icon( - DidvanIcons.bookmark_regular, + children: [ + BookmarkButton( + value: radar.marked, + onMark: () => state.markRadar(radar.id), + onUnmark: () => state.unMarkRadar(radar.id), ), - Spacer(), - DidvanText('2'), - SizedBox(width: 4), - Icon(DidvanIcons.directs_regular), - SizedBox(width: 16), - DidvanText('10'), - SizedBox(width: 4), - Icon(DidvanIcons.evaluation_regular), + const Spacer(), + if (radar.comments != 0) DidvanText(radar.comments.toString()), + const SizedBox(width: 4), + DidvanIconButton( + icon: DidvanIcons.chats_regular, + gestureSize: 24, + onPressed: () => Navigator.of(context).pushNamed( + Routes.comments, + arguments: { + 'isRadar': true, + 'title': radar.title, + 'id': radar.id, + }, + ), + ), + const SizedBox(width: 16), + // const DidvanText('10'), + // const SizedBox(width: 4), + // const Icon(DidvanIcons.evaluation_regular), ], ), ],