diff --git a/lib/config/theme_data.dart b/lib/config/theme_data.dart index 3aa89e7..3fb5127 100644 --- a/lib/config/theme_data.dart +++ b/lib/config/theme_data.dart @@ -12,7 +12,12 @@ class LightThemeConfig { textTheme: _TextThemeData.data, cardColor: _colorScheme.surface, checkboxTheme: CheckboxThemeData( - fillColor: MaterialStateProperty.all(_colorScheme.primary), + fillColor: MaterialStateProperty.resolveWith((states) { + if (!states.contains(MaterialState.selected)) { + return Colors.transparent; + } + return _colorScheme.primary; + }), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(4), ), @@ -26,7 +31,7 @@ class LightThemeConfig { static const ColorScheme _colorScheme = ColorScheme( primary: _primary, primaryContainer: _white, - secondary: Color(0xFFD61515), + secondary: Color(0xFFB20436), secondaryContainer: _white, surface: _white, background: _background, @@ -53,7 +58,12 @@ class DarkThemeConfig { ), checkboxTheme: CheckboxThemeData( materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - fillColor: MaterialStateProperty.all(_colorScheme.primary), + fillColor: MaterialStateProperty.resolveWith((states) { + if (!states.contains(MaterialState.selected)) { + return Colors.transparent; + } + return _colorScheme.primary; + }), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(4), ), @@ -65,7 +75,7 @@ class DarkThemeConfig { static const ColorScheme _colorScheme = ColorScheme( primary: _primary, primaryContainer: _white, - secondary: Color(0xFFE53939), + secondary: Color(0xFFB21542), secondaryContainer: _white, surface: Color(0xFF181B1F), background: _background, @@ -138,8 +148,8 @@ class _TextThemeData { extension DidvanColorScheme on ColorScheme { // Secondary colors Color get secondaryDisabled => brightness == Brightness.dark - ? const Color(0xFF703838) - : const Color(0xFFFFC8C8); + ? const Color(0xFF703848) + : const Color(0xFFFFC8D7); Color get white => const Color(0xFFFFFFFF); Color get focused => brightness == Brightness.dark diff --git a/lib/models/home_page_content/home_page_content.dart b/lib/models/home_page_content/home_page_content.dart index 3b664e1..c7c6f84 100644 --- a/lib/models/home_page_content/home_page_content.dart +++ b/lib/models/home_page_content/home_page_content.dart @@ -3,7 +3,7 @@ import 'package:didvan/models/home_page_content/banner.dart'; import 'home_page_list.dart'; class MainPageContent { - final List banners; + final List> banners; final List lists; final int unread; @@ -12,9 +12,13 @@ class MainPageContent { factory MainPageContent.fromJson(Map json) { return MainPageContent( - banners: List.from(json['banners'].map( - (x) => MainPageBannerType.fromJson(x), - )), + banners: List>.from( + json['banners'].map( + (list) => List.from( + list.map((e) => MainPageBannerType.fromJson(e)).toList(), + ), + ), + ), lists: List.from( json['lists'].map( (x) => MainPageList.fromJson(x), diff --git a/lib/models/main_category.dart b/lib/models/main_category.dart new file mode 100644 index 0000000..6e37b54 --- /dev/null +++ b/lib/models/main_category.dart @@ -0,0 +1,9 @@ +import 'package:flutter/material.dart'; + +class MainCategoryType { + final int id; + final String label; + final IconData icon; + + MainCategoryType({required this.id, required this.label, required this.icon}); +} diff --git a/lib/views/comments/comments.dart b/lib/views/comments/comments.dart index 753c35d..1488eff 100644 --- a/lib/views/comments/comments.dart +++ b/lib/views/comments/comments.dart @@ -59,6 +59,7 @@ class _CommentsState extends State { child: Stack( children: [ DidvanScaffold( + hidePlayer: true, physics: const BouncingScrollPhysics(), backgroundColor: Theme.of(context).colorScheme.surface, appBarData: _isPage diff --git a/lib/views/direct/direct.dart b/lib/views/direct/direct.dart index 7c57e12..c5d7918 100644 --- a/lib/views/direct/direct.dart +++ b/lib/views/direct/direct.dart @@ -56,6 +56,7 @@ class _DirectState extends State { left: 0, right: 0, child: DidvanScaffold( + hidePlayer: true, padding: EdgeInsets.zero, reverse: true, backgroundColor: Theme.of(context).colorScheme.surface, diff --git a/lib/views/home/home.dart b/lib/views/home/home.dart index e1bfe68..1dbea66 100644 --- a/lib/views/home/home.dart +++ b/lib/views/home/home.dart @@ -44,7 +44,7 @@ class _HomeState extends State with SingleTickerProviderStateMixin { @override Widget build(BuildContext context) { return Scaffold( - appBar: LogoAppBar(), + appBar: const LogoAppBar(), body: Consumer( builder: (context, state, child) => AnimatedCrossFade( duration: DesignConfig.lowAnimationDuration, diff --git a/lib/views/home/home_state.dart b/lib/views/home/home_state.dart index cdcde50..0023d57 100644 --- a/lib/views/home/home_state.dart +++ b/lib/views/home/home_state.dart @@ -26,7 +26,6 @@ class HomeState extends CoreProvier { int _currentPageIndex = 0; String search = ''; String lastSearch = ''; - bool _showSearchPage = false; Timer? timer; String? startDate; String? endDate; @@ -48,13 +47,6 @@ class HomeState extends CoreProvier { } } - set showSearchPage(bool value) { - _showSearchPage = value; - notifyListeners(); - } - - bool get showSearchPage => _showSearchPage; - set currentPageIndex(int value) { _currentPageIndex = value; notifyListeners(); @@ -123,7 +115,7 @@ class HomeState extends CoreProvier { await service.httpGet(); if (service.isSuccess) { lastPage = service.result['lastPage']; - unreadCount = service.result['unread']; + unreadCount = service.result['unread'] ?? unreadCount; results.addAll( List.from( service.result['contents'].map( @@ -138,10 +130,19 @@ class HomeState extends CoreProvier { appState = AppState.failed; } + final categoryFilters = [ + CategoryData(id: 1, label: 'پویش افق'), + CategoryData(id: 2, label: 'دنیای فولاد'), + CategoryData(id: 3, label: 'ویدئوکست'), + CategoryData(id: 4, label: 'پادکست'), + CategoryData(id: 5, label: 'تحلیل‌های راداری'), + CategoryData(id: 6, label: 'سها'), + ]; + void refresh() { menuItems.clear(); categories.clear(); - menuItems.addAll([ + menuItems = [ MenuItemType( label: 'دنیای فولاد', asset: Assets.fooladWorld, @@ -160,7 +161,7 @@ class HomeState extends CoreProvier { MenuItemType( label: 'سها', asset: Assets.saha, - link: 'https://didvan.app', + link: 'https://saha.didvan.app', ), MenuItemType( label: 'رادار استارتاپ', @@ -192,9 +193,9 @@ class HomeState extends CoreProvier { asset: Assets.podcast, link: Routes.podcasts, ), - ]); + ]; - categories.addAll([ + categories = [ CategoryData( id: 1, label: 'اقتصادی', @@ -245,7 +246,6 @@ class HomeState extends CoreProvier { label: 'کسب و کار', asset: Assets.businessCategoryIcon, ), - ]); - Future.delayed(Duration.zero, notifyListeners); + ]; } } diff --git a/lib/views/home/main/main_page_state.dart b/lib/views/home/main/main_page_state.dart index 600cf7b..d800497 100644 --- a/lib/views/home/main/main_page_state.dart +++ b/lib/views/home/main/main_page_state.dart @@ -82,7 +82,10 @@ class MainPageState extends CoreProvier { return; } if (link.startsWith('http')) { - launchUrlString('$link?accessToken=${RequestService.token}'); + launchUrlString( + '$link?accessToken=${RequestService.token}', + mode: LaunchMode.inAppWebView, + ); return; } Navigator.of(ActionSheetUtils.context).pushNamed(link, arguments: args); diff --git a/lib/views/home/main/widgets/banner.dart b/lib/views/home/main/widgets/banner.dart index 699c6f0..91729b9 100644 --- a/lib/views/home/main/widgets/banner.dart +++ b/lib/views/home/main/widgets/banner.dart @@ -3,6 +3,7 @@ import 'package:didvan/views/widgets/didvan/slider.dart'; import 'package:didvan/views/widgets/skeleton_image.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:url_launcher/url_launcher_string.dart'; class MainPageBanner extends StatelessWidget { final bool isFirst; @@ -12,15 +13,18 @@ class MainPageBanner extends StatelessWidget { Widget build(BuildContext context) { final state = context.read(); return DidvanSlider( - itemBuilder: (context, index, realIndex) => Padding( - padding: const EdgeInsets.symmetric(horizontal: 4), - child: GestureDetector( - onTap: () => state.content.banners[index].link, - child: SkeletonImage( - imageUrl: state.content.banners[index].image, + itemBuilder: (context, index, realIndex) { + final item = state.content.banners[isFirst ? 0 : 1][index]; + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: GestureDetector( + onTap: item.link == null ? null : () => launchUrlString(item.link!), + child: SkeletonImage( + imageUrl: item.image, + ), ), - ), - ), + ); + }, itemCount: state.content.banners.length, viewportFraction: 1, enableIndicator: true, diff --git a/lib/views/home/search/search.dart b/lib/views/home/search/search.dart index 0a0591b..5cd3c99 100644 --- a/lib/views/home/search/search.dart +++ b/lib/views/home/search/search.dart @@ -1,6 +1,8 @@ +import 'package:collection/collection.dart'; import 'package:didvan/models/enums.dart'; import 'package:didvan/views/home/home_state.dart'; import 'package:didvan/views/home/search/widgets/search_result_item.dart'; +import 'package:didvan/views/widgets/categories_list.dart'; import 'package:didvan/views/widgets/state_handlers/empty_list.dart'; import 'package:didvan/views/widgets/state_handlers/state_handler.dart'; import 'package:flutter/material.dart'; @@ -15,22 +17,42 @@ class SearchPage extends StatelessWidget { return SizedBox( height: MediaQuery.of(context).size.height, width: MediaQuery.of(context).size.width, - child: StateHandler( - state: state, - enableEmptyState: - state.appState == AppState.idle && state.results.isEmpty, - emptyState: const EmptyList(), - onRetry: () => state.searchAll(page: state.page), - builder: (context, state) => ListView.builder( - padding: const EdgeInsets.all(16), - itemBuilder: (context, index) => Padding( - padding: const EdgeInsets.only(bottom: 16), - child: SearchResultItem( - item: state.results[index], + child: Stack( + children: [ + StateHandler( + state: state, + enableEmptyState: + state.appState == AppState.idle && state.results.isEmpty, + emptyState: const EmptyList(), + onRetry: () => state.searchAll(page: state.page), + builder: (context, state) => ListView.builder( + padding: const EdgeInsets.all(16) + .copyWith(top: state.selectedCats.length <= 1 ? 72 : 16), + itemBuilder: (context, index) => Padding( + padding: const EdgeInsets.only(bottom: 8), + child: SearchResultItem( + item: state.results[index], + ), + ), + itemCount: state.results.length, ), ), - itemCount: state.results.length, - ), + CategoriesList( + isColapsed: state.selectedCats.length <= 1, + selectedCats: state.selectedCats, + categories: state.categoryFilters, + onSelected: (id) { + state.selectedCats.clear(); + final cat = state.categoryFilters + .firstWhereOrNull((element) => element.id == id); + if (cat != null) { + state.selectedCats.add(cat); + } + state.searchAll(page: 1); + }, + top: 0, + ), + ], ), ); } diff --git a/lib/views/home/widgets/categories.dart b/lib/views/home/widgets/categories.dart index 6ca12c6..be1ac92 100644 --- a/lib/views/home/widgets/categories.dart +++ b/lib/views/home/widgets/categories.dart @@ -1,5 +1,6 @@ import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; +import 'package:didvan/services/network/request.dart'; import 'package:didvan/views/home/home_state.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:flutter/material.dart'; @@ -12,7 +13,10 @@ class MainCategories extends StatelessWidget { void _onTap(String link, BuildContext context) { if (link.startsWith('http')) { - launchUrlString(link); + launchUrlString( + '$link?accessToken=${RequestService.token}', + mode: LaunchMode.inAppWebView, + ); } else if (link.startsWith('tab-')) { final state = context.read(); state.currentPageIndex = 1; diff --git a/lib/views/profile/direct_list/direct_list.dart b/lib/views/profile/direct_list/direct_list.dart index 4b2a81b..fe66eeb 100644 --- a/lib/views/profile/direct_list/direct_list.dart +++ b/lib/views/profile/direct_list/direct_list.dart @@ -31,6 +31,7 @@ class _DirectListState extends State { Widget build(BuildContext context) { return Consumer( builder: (context, state, child) => DidvanScaffold( + padding: const EdgeInsets.symmetric(vertical: 16), appBarData: AppBarData( hasBack: true, title: 'پیام‌ها', diff --git a/lib/views/profile/edit_profile/edit_profile.dart b/lib/views/profile/edit_profile/edit_profile.dart index f2a849a..51d2de5 100644 --- a/lib/views/profile/edit_profile/edit_profile.dart +++ b/lib/views/profile/edit_profile/edit_profile.dart @@ -51,7 +51,12 @@ class _EditProfileState extends State { Form( key: _formKey, child: DidvanScaffold( - padding: const EdgeInsets.only(left: 16, right: 16, bottom: 92), + padding: const EdgeInsets.only( + top: 16, + left: 16, + right: 16, + bottom: 92, + ), appBarData: AppBarData(title: 'ویرایش پروفایل'), children: [ const ProfilePhoto(), diff --git a/lib/views/profile/general_settings/settings.dart b/lib/views/profile/general_settings/settings.dart index 578f187..3fb52ea 100644 --- a/lib/views/profile/general_settings/settings.dart +++ b/lib/views/profile/general_settings/settings.dart @@ -48,6 +48,7 @@ class _GeneralSettingsState extends State { onRetry: () {}, state: context.read(), builder: (context, state) => DidvanScaffold( + padding: const EdgeInsets.all(16), appBarData: AppBarData(hasBack: true, title: 'تنظیمات'), children: [ DidvanCard( diff --git a/lib/views/profile/profile.dart b/lib/views/profile/profile.dart index 7825eac..dbf4fbc 100644 --- a/lib/views/profile/profile.dart +++ b/lib/views/profile/profile.dart @@ -19,6 +19,7 @@ class ProfilePage extends StatelessWidget { @override Widget build(BuildContext context) { return DidvanScaffold( + padding: const EdgeInsets.all(16), appBarData: AppBarData( title: 'تنظیمات', ), diff --git a/lib/views/radar/radar.dart b/lib/views/radar/radar.dart index 8e3e9f7..f746c35 100644 --- a/lib/views/radar/radar.dart +++ b/lib/views/radar/radar.dart @@ -193,7 +193,7 @@ class _RadarState extends State { child: SearchField( focusNode: _focusNode, isFiltered: state.filtering, - title: 'رادار', + title: 'افق', onChanged: _onChanged, onFilterButtonPressed: _showFilterBottomSheet, ), diff --git a/lib/views/widgets/categories_list.dart b/lib/views/widgets/categories_list.dart index c23fce4..2f1af44 100644 --- a/lib/views/widgets/categories_list.dart +++ b/lib/views/widgets/categories_list.dart @@ -88,9 +88,17 @@ class _CategoriesListState extends State { firstChild: const SizedBox(), secondChild: Container( height: 60 + d.padding.top, + margin: const EdgeInsets.only(bottom: 20), decoration: BoxDecoration( color: Theme.of(context).colorScheme.surface, - boxShadow: DesignConfig.defaultShadow, + boxShadow: [ + BoxShadow( + color: const Color(0XFF1B3C59).withOpacity(0.15), + blurRadius: 8, + spreadRadius: 0, + offset: const Offset(0, 8), + ) + ], ), child: AnimatedVisibility( isVisible: widget.isColapsed, diff --git a/lib/views/widgets/didvan/scaffold.dart b/lib/views/widgets/didvan/scaffold.dart index 7e8ec28..7d77a3b 100644 --- a/lib/views/widgets/didvan/scaffold.dart +++ b/lib/views/widgets/didvan/scaffold.dart @@ -32,6 +32,7 @@ class DidvanScaffold extends StatefulWidget { final ScrollPhysics? physics; final ScrollController? scrollController; final bool showSliversFirst; + final bool hidePlayer; const DidvanScaffold({ Key? key, @@ -44,6 +45,7 @@ class DidvanScaffold extends StatefulWidget { this.reverse = false, this.scrollController, this.showSliversFirst = false, + this.hidePlayer = false, }) : super(key: key); @override @@ -138,12 +140,13 @@ class _DidvanScaffoldState extends State { appBarData: widget.appBarData!, scrollController: _scrollController, ), - const Positioned( - bottom: 20, - left: 0, - right: 0, - child: _PlayerNavBar(), - ), + if (!widget.hidePlayer) + const Positioned( + bottom: 20, + left: 0, + right: 0, + child: _PlayerNavBar(), + ), ], ), ), diff --git a/lib/views/widgets/logo_app_bar.dart b/lib/views/widgets/logo_app_bar.dart index 05b5690..22dd11e 100644 --- a/lib/views/widgets/logo_app_bar.dart +++ b/lib/views/widgets/logo_app_bar.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/app_icons.dart'; -import 'package:didvan/models/category.dart'; import 'package:didvan/models/view/action_sheet_data.dart'; import 'package:didvan/routes/routes.dart'; import 'package:didvan/utils/action_sheet.dart'; @@ -12,14 +11,12 @@ import 'package:didvan/views/widgets/didvan/checkbox.dart'; import 'package:didvan/views/widgets/item_title.dart'; import 'package:didvan/views/widgets/search_field.dart'; import 'package:didvan/views/widgets/didvan/icon_button.dart'; -import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/logos/didvan_vertical_logo.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_svg/svg.dart'; import 'package:provider/provider.dart'; class LogoAppBar extends StatelessWidget implements PreferredSizeWidget { - LogoAppBar({Key? key}) : super(key: key); + const LogoAppBar({Key? key}) : super(key: key); @override Size get preferredSize => const Size(double.infinity, 144); @@ -33,14 +30,16 @@ class LogoAppBar extends StatelessWidget implements PreferredSizeWidget { decoration: BoxDecoration( borderRadius: const BorderRadius.only(bottomLeft: Radius.circular(20)), color: Theme.of(context).colorScheme.surface, - boxShadow: [ - BoxShadow( - color: const Color(0XFF1B3C59).withOpacity(0.15), - blurRadius: 8, - spreadRadius: 0, - offset: const Offset(0, 8), - ) - ], + boxShadow: state.currentPageIndex == 1 || state.filtering + ? null + : [ + BoxShadow( + color: const Color(0XFF1B3C59).withOpacity(0.15), + blurRadius: 8, + spreadRadius: 0, + offset: const Offset(0, 8), + ) + ], ), margin: EdgeInsets.only(top: d.padding.top), padding: const EdgeInsets.all(16), @@ -125,15 +124,6 @@ class LogoAppBar extends StatelessWidget implements PreferredSizeWidget { }); } - final categoryFilters = [ - CategoryData(id: 1, label: 'پویش افق'), - CategoryData(id: 2, label: 'دنیای فولاد'), - CategoryData(id: 3, label: 'ویدئوکست'), - CategoryData(id: 4, label: 'پادکست'), - CategoryData(id: 1, label: 'تحلیل‌های راداری'), - CategoryData(id: 1, label: 'سها'), - ]; - Future _showFilterBottomSheet(BuildContext context) async { final state = context.read(); await ActionSheetUtils.showBottomSheet( @@ -185,18 +175,19 @@ class LogoAppBar extends StatelessWidget implements PreferredSizeWidget { const SizedBox(height: 12), Wrap( children: [ - for (var i = 0; i < categoryFilters.length; i++) + for (var i = 0; i < state.categoryFilters.length; i++) SizedBox( width: (MediaQuery.of(context).size.width - 40) / 2, child: DidvanCheckbox( - title: categoryFilters[i].label, - value: state.selectedCats.contains(state.categories[i]), + title: state.categoryFilters[i].label, + value: + state.selectedCats.contains(state.categoryFilters[i]), onChanged: (value) { if (value) { - state.selectedCats.add(state.categories[i]); + state.selectedCats.add(state.categoryFilters[i]); return; } - state.selectedCats.remove(state.categories[i]); + state.selectedCats.remove(state.categoryFilters[i]); }, ), ), @@ -273,45 +264,45 @@ class LogoAppBar extends StatelessWidget implements PreferredSizeWidget { // } } -class _BottomSheetItem extends StatelessWidget { - final String icon; - final String title; - final bool enabled; - final VoidCallback onTap; - const _BottomSheetItem({ - required this.icon, - required this.title, - this.enabled = false, - required this.onTap, - }); +// class _BottomSheetItem extends StatelessWidget { +// final String icon; +// final String title; +// final bool enabled; +// final VoidCallback onTap; +// const _BottomSheetItem({ +// required this.icon, +// required this.title, +// this.enabled = false, +// required this.onTap, +// }); - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: enabled ? onTap : null, - child: Container( - color: Colors.transparent, - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SvgPicture.asset( - icon, - theme: SvgTheme( - currentColor: enabled - ? Theme.of(context).colorScheme.title - : Theme.of(context).colorScheme.disabledText, - ), - ), - const SizedBox(width: 8), - DidvanText( - title, - color: enabled - ? Theme.of(context).colorScheme.title - : Theme.of(context).colorScheme.disabledText, - ), - ], - ), - ), - ); - } -} +// @override +// Widget build(BuildContext context) { +// return GestureDetector( +// onTap: enabled ? onTap : null, +// child: Container( +// color: Colors.transparent, +// child: Row( +// crossAxisAlignment: CrossAxisAlignment.center, +// children: [ +// SvgPicture.asset( +// icon, +// theme: SvgTheme( +// currentColor: enabled +// ? Theme.of(context).colorScheme.title +// : Theme.of(context).colorScheme.disabledText, +// ), +// ), +// const SizedBox(width: 8), +// DidvanText( +// title, +// color: enabled +// ? Theme.of(context).colorScheme.title +// : Theme.of(context).colorScheme.disabledText, +// ), +// ], +// ), +// ), +// ); +// } +// } diff --git a/lib/views/widgets/overview/multitype.dart b/lib/views/widgets/overview/multitype.dart index 58ecdd4..e7c03bd 100644 --- a/lib/views/widgets/overview/multitype.dart +++ b/lib/views/widgets/overview/multitype.dart @@ -75,6 +75,7 @@ class MultitypeOverview extends StatelessWidget { item.id, args: const StudioRequestArgs(page: 0, type: 'podcast'), ); + MediaService.currentPodcast = state.studio; MediaService.handleAudioPlayback( audioSource: item.link, id: item.id,