redesign infography and fooladinfo
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16.8199 2H7.17995C5.04995 2 3.31995 3.74 3.31995 5.86V19.95C3.31995 21.75 4.60995 22.51 6.18995 21.64L11.0699 18.93C11.5899 18.64 12.4299 18.64 12.9399 18.93L17.8199 21.64C19.3999 22.52 20.6899 21.76 20.6899 19.95V5.86C20.6799 3.74 18.9499 2 16.8199 2Z" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 450 B |
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="18" height="19" viewBox="0 0 18 19" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.6825 15.0292L11.5725 10.1392C12.15 9.56167 12.15 8.61667 11.5725 8.03917L6.6825 3.14917" stroke="#2196F3" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 312 B |
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="18" height="20" viewBox="0 0 18 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.5114 0H3.85862C1.737 0 0 1.737 0 3.85862V17.9531C0 19.7522 1.29034 20.5214 2.86605 19.6405L7.74206 16.9233C8.26316 16.638 9.10684 16.638 9.61554 16.9233L14.4915 19.6405C16.0797 20.509 17.37 19.7522 17.37 17.9531V3.85862C17.37 1.737 15.633 0 13.5114 0Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 384 B |
|
|
@ -0,0 +1,7 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M22 8.27V4.23C22 2.64 21.36 2 19.77 2H15.73C14.14 2 13.5 2.64 13.5 4.23V8.27C13.5 9.86 14.14 10.5 15.73 10.5H19.77C21.36 10.5 22 9.86 22 8.27Z" stroke="#F8F8FA" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M10.5 8.52V3.98C10.5 2.57 9.86 2 8.27 2H4.23C2.64 2 2 2.57 2 3.98V8.51C2 9.93 2.64 10.49 4.23 10.49H8.27C9.86 10.5 10.5 9.93 10.5 8.52Z" stroke="#F8F8FA" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M10.5 19.77V15.73C10.5 14.14 9.86 13.5 8.27 13.5H4.23C2.64 13.5 2 14.14 2 15.73V19.77C2 21.36 2.64 22 4.23 22H8.27C9.86 22 10.5 21.36 10.5 19.77Z" stroke="#F8F8FA" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M14.5 17.5H20.5" stroke="#F8F8FA" stroke-width="1.5" stroke-linecap="round"/>
|
||||
<path d="M17.5 20.5V14.5" stroke="#F8F8FA" stroke-width="1.5" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 987 B |
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.465 15.6075C9.21 15.6975 8.79 15.6975 8.535 15.6075C6.36 14.865 1.5 11.7675 1.5 6.51745C1.5 4.19995 3.3675 2.32495 5.67 2.32495C7.035 2.32495 8.2425 2.98495 9 4.00495C9.7575 2.98495 10.9725 2.32495 12.33 2.32495C14.6325 2.32495 16.5 4.19995 16.5 6.51745C16.5 11.7675 11.64 14.865 9.465 15.6075Z" stroke="#2196F3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 477 B |
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16.44 3.1001C14.63 3.1001 13.01 3.9801 12 5.3301C10.99 3.9801 9.37 3.1001 7.56 3.1001C4.49 3.1001 2 5.6001 2 8.6901C2 9.8801 2.19 10.9801 2.52 12.0001C4.1 17.0001 8.97 19.9901 11.38 20.8101C11.72 20.9301 12.28 20.9301 12.62 20.8101C15.03 19.9901 19.9 17.0001 21.48 12.0001C21.81 10.9801 22 9.8801 22 8.6901C22 5.6001 19.51 3.1001 16.44 3.1001Z" fill="#292D32"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 475 B |
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.25 7.02V2.98C9.25 1.39 8.61 0.75 7.02 0.75H2.98C1.39 0.75 0.75 1.39 0.75 2.98V7.02C0.75 8.61 1.39 9.25 2.98 9.25H7.02C8.61 9.25 9.25 8.61 9.25 7.02Z" stroke="#F8F8FA" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 350 B |
|
|
@ -0,0 +1,4 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.40005 6.32015L15.8901 3.49015C19.7001 2.22015 21.7701 4.30015 20.5101 8.11015L17.6801 16.6002C15.7801 22.3102 12.6601 22.3102 10.7601 16.6002L9.92005 14.0802L7.40005 13.2402C1.69005 11.3402 1.69005 8.23015 7.40005 6.32015Z" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M10.11 13.6501L13.69 10.0601" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 544 B |
|
|
@ -205,8 +205,8 @@ class RequestHelper {
|
|||
static String deleteComment(int id) => '$baseUrl/comment/$id';
|
||||
static String reportComment(int id) => '$baseUrl/comment/$id/report';
|
||||
static String widgetNews() => '$baseUrl/user/widget';
|
||||
static String aiChats() => '$baseUrl/ai/chat/v2';
|
||||
static String aiArchived() => '$baseUrl/ai/chat/v2${_urlConcatGenerator([
|
||||
static String aiChats() => '$baseUrl/ai/aichat';
|
||||
static String aiArchived() => '$baseUrl/ai/aichat${_urlConcatGenerator([
|
||||
const MapEntry('archived', true),
|
||||
])}';
|
||||
static String aiBots() => '$baseUrl/ai/bot/v2';
|
||||
|
|
|
|||
|
|
@ -8,14 +8,13 @@ import 'package:didvan/routes/routes.dart';
|
|||
import 'package:didvan/views/widgets/animated_visibility.dart';
|
||||
import 'package:didvan/views/widgets/bookmark_button.dart';
|
||||
import 'package:didvan/views/widgets/didvan/card.dart';
|
||||
import 'package:didvan/views/widgets/didvan/divider.dart';
|
||||
import 'package:didvan/views/widgets/didvan/icon_button.dart';
|
||||
import 'package:didvan/views/widgets/didvan/text.dart';
|
||||
import 'package:didvan/views/widgets/infography_tag.dart';
|
||||
import 'package:didvan/views/widgets/ink_wrapper.dart';
|
||||
import 'package:didvan/views/widgets/liked_button.dart';
|
||||
import 'package:didvan/views/widgets/skeleton_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
|
||||
class _BackButton extends StatefulWidget {
|
||||
const _BackButton({Key? key}) : super(key: key);
|
||||
|
|
@ -117,77 +116,106 @@ class InfographyItem extends StatelessWidget {
|
|||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
padding: const EdgeInsets.symmetric(vertical: 0.0),
|
||||
child: GestureDetector(
|
||||
onTap: () => _openInteractiveViewer(context, image),
|
||||
child: SkeletonImage(
|
||||
imageUrl: image,
|
||||
aspectRatio: 16 / 9,
|
||||
),
|
||||
imageUrl: image,
|
||||
aspectRatio: 16 / 9,
|
||||
borderRadius: const BorderRadius.only(
|
||||
bottomLeft: Radius.circular(0.0),
|
||||
bottomRight: Radius.circular(0.0),
|
||||
topLeft: Radius.circular(13.0),
|
||||
topRight: Radius.circular(13.0),
|
||||
)),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
decoration: const BoxDecoration(
|
||||
color: Color.fromARGB(255, 0, 69, 92),
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomRight: Radius.circular(13.0),
|
||||
bottomLeft: Radius.circular(13.0)),
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
InfoCat(
|
||||
category: category,
|
||||
createdAt: createdAt,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => Navigator.of(context).pushNamed(
|
||||
Routes.mentions,
|
||||
arguments: {
|
||||
'id': id,
|
||||
'type': 'banner',
|
||||
'title': title,
|
||||
},
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
'lib/assets/icons/send-2.svg',
|
||||
width: 25,
|
||||
height: 25,
|
||||
colorFilter: const ColorFilter.mode(
|
||||
Colors.white,
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
),
|
||||
),
|
||||
BookmarkButton(
|
||||
itemId: id,
|
||||
type: 'infography',
|
||||
gestureSize: 22,
|
||||
value: marked,
|
||||
onMarkChanged: (value) => onMarkChanged(id, value, true),
|
||||
svgIconOn: 'lib/assets/icons/bookmark_fill.svg',
|
||||
svgIconOff: 'lib/assets/icons/archive-tick.svg',
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8),
|
||||
child: LikedButton(
|
||||
itemId: id,
|
||||
type: 'infography',
|
||||
gestureSize: 32,
|
||||
value: liked,
|
||||
onMarkChanged: (value) => onLikedChanged(id, value, true),
|
||||
likes: likes,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Wrap(
|
||||
spacing: 4,
|
||||
runSpacing: 4,
|
||||
children: [
|
||||
for (var i = 0; i < tag.length; i++)
|
||||
InfographyTag(
|
||||
tag: tag[i],
|
||||
onMarkChanged: onMarkChanged,
|
||||
),
|
||||
],
|
||||
Expanded(
|
||||
child: Wrap(
|
||||
spacing: 4,
|
||||
runSpacing: 4,
|
||||
children: [
|
||||
for (var i = 0; i < tag.length; i++)
|
||||
InfographyTag(
|
||||
tag: tag[i],
|
||||
onMarkChanged: onMarkChanged,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const DidvanDivider(),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
InfoCat(
|
||||
category: category,
|
||||
createdAt: createdAt,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
LikedButton(
|
||||
itemId: id,
|
||||
type: 'infography',
|
||||
gestureSize: 32,
|
||||
value: liked,
|
||||
onMarkChanged: (value) => onLikedChanged(id, value, true),
|
||||
likes: likes,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 4.0,
|
||||
),
|
||||
DidvanIconButton(
|
||||
gestureSize: 32,
|
||||
onPressed: () => Navigator.of(context).pushNamed(
|
||||
Routes.mentions,
|
||||
arguments: {
|
||||
'id': id,
|
||||
'type': 'banner',
|
||||
'title': title,
|
||||
},
|
||||
),
|
||||
icon: DidvanIcons.mention_icon,
|
||||
),
|
||||
BookmarkButton(
|
||||
itemId: id,
|
||||
type: 'infography',
|
||||
gestureSize: 32,
|
||||
value: marked,
|
||||
onMarkChanged: (value) => onMarkChanged(id, value, true),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
import 'package:didvan/views/home/media/podcast_tab_page.dart';
|
||||
import 'package:didvan/views/home/media/videocast_tab_page.dart';
|
||||
import 'package:didvan/views/widgets/didvan/text.dart';
|
||||
import 'package:didvan/views/home/main/widgets/banner.dart';
|
||||
import 'package:didvan/views/home/media/widgets/media_banner_with_thumbnails.dart';
|
||||
import 'package:didvan/views/home/media/widgets/banner_grid_view.dart';
|
||||
import 'package:didvan/views/widgets/home_app_bar.dart';
|
||||
import 'package:didvan/views/widgets/custom_media_tab_bar.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:didvan/views/podcasts/podcasts_state.dart';
|
||||
import 'package:didvan/views/home/infography/infography_screen_state.dart';
|
||||
|
||||
class MediaPage extends StatefulWidget {
|
||||
const MediaPage({super.key});
|
||||
|
|
@ -101,25 +102,18 @@ class _MediaPageState extends State<MediaPage> {
|
|||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 16.0, vertical: 16.0),
|
||||
child: MainPageBanner(isFirst: true),
|
||||
child: MediaBannerWithThumbnails(isFirst: true),
|
||||
),
|
||||
),
|
||||
Center(
|
||||
key: const ValueKey('InfographyPage'),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.photo_library,
|
||||
size: 48,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
DidvanText(
|
||||
'صفحه اینفوگرافی',
|
||||
style: Theme.of(context).textTheme.titleSmall,
|
||||
),
|
||||
],
|
||||
ChangeNotifierProvider(
|
||||
create: (_) => InfographyScreenState(),
|
||||
child: const SingleChildScrollView(
|
||||
key: ValueKey('InfographyPage'),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 8.0, vertical: 16.0),
|
||||
child: BannerGridView(),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -0,0 +1,264 @@
|
|||
import 'package:didvan/views/home/infography/infography_screen_state.dart';
|
||||
import 'package:didvan/views/home/main/widgets/infography_item.dart';
|
||||
import 'package:didvan/views/widgets/state_handlers/state_handler.dart';
|
||||
import 'package:didvan/views/widgets/shimmer_placeholder.dart';
|
||||
import 'package:didvan/views/widgets/didvan/card.dart';
|
||||
import 'package:didvan/views/widgets/didvan/divider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class BannerGridView extends StatefulWidget {
|
||||
const BannerGridView({super.key});
|
||||
|
||||
@override
|
||||
State<BannerGridView> createState() => _BannerGridViewState();
|
||||
}
|
||||
|
||||
class _BannerGridViewState extends State<BannerGridView> {
|
||||
int _visibleItemCount = 4;
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
int pageNumber = 1;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
Future.microtask(() {
|
||||
context.read<InfographyScreenState>().init();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_scrollController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer<InfographyScreenState>(
|
||||
builder: (context, state, child) {
|
||||
return StateHandler<InfographyScreenState>(
|
||||
placeholder: Column(
|
||||
children: [
|
||||
_buildPlaceholder(),
|
||||
const SizedBox(height: 8.0),
|
||||
_buildPlaceholder(),
|
||||
],
|
||||
),
|
||||
topPadding: 0,
|
||||
onRetry: () => state.init(),
|
||||
state: state,
|
||||
builder: (context, state) {
|
||||
final contents = state.contents;
|
||||
|
||||
final itemsToShow = _visibleItemCount > contents.length
|
||||
? contents.length
|
||||
: _visibleItemCount;
|
||||
|
||||
final hasMoreItems = itemsToShow < contents.length;
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
_buildCategoryFilters(state),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
itemCount: itemsToShow,
|
||||
separatorBuilder: (context, index) =>
|
||||
const SizedBox(height: 8.0),
|
||||
itemBuilder: (context, index) {
|
||||
final item = contents[index];
|
||||
return InfographyItem(
|
||||
id: item.id,
|
||||
onMarkChanged: (id, value, _) =>
|
||||
state.changeMark(id, value),
|
||||
image: item.image,
|
||||
category: item.category,
|
||||
createdAt: item.createdAt,
|
||||
title: item.title,
|
||||
tag: item.tags,
|
||||
marked: item.marked,
|
||||
liked: item.liked,
|
||||
onLikedChanged: (id, value, _) =>
|
||||
state.changeLiked(id, value),
|
||||
likes: item.likes,
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
if (hasMoreItems)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16.0),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_visibleItemCount += 4;
|
||||
});
|
||||
},
|
||||
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',
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
const Text(
|
||||
'بارگذاری بیشتر',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCategoryFilters(InfographyScreenState state) {
|
||||
return SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: FilterChip(
|
||||
label: const Padding(
|
||||
padding: EdgeInsets.all(1.0),
|
||||
child: Text('همه'),
|
||||
),
|
||||
selected: state.selectedCats.isEmpty,
|
||||
onSelected: (selected) {
|
||||
if (selected) {
|
||||
state.selectedCats.clear();
|
||||
state.getInfographyContent(page: 1);
|
||||
setState(() {
|
||||
_visibleItemCount = 4;
|
||||
});
|
||||
}
|
||||
},
|
||||
selectedColor: const Color.fromARGB(255, 200, 224, 244),
|
||||
backgroundColor: state.selectedCats.isEmpty
|
||||
? const Color.fromARGB(255, 200, 224, 244)
|
||||
: const Color.fromARGB(255, 224, 224, 224),
|
||||
labelStyle: TextStyle(
|
||||
color: state.selectedCats.isEmpty
|
||||
? const Color.fromARGB(255, 0, 115, 153)
|
||||
: const Color.fromARGB(255, 102, 102, 102),
|
||||
fontWeight: state.selectedCats.isEmpty
|
||||
? FontWeight.bold
|
||||
: FontWeight.normal,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
),
|
||||
showCheckmark: false,
|
||||
),
|
||||
),
|
||||
|
||||
...state.categories.map((category) {
|
||||
final isSelected = state.selectedCats.contains(category);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: FilterChip(
|
||||
label: Padding(
|
||||
padding: const EdgeInsets.all(1.0),
|
||||
child: Text(category.label),
|
||||
),
|
||||
selected: isSelected,
|
||||
onSelected: (selected) {
|
||||
if (selected) {
|
||||
state.selectedCats.add(category);
|
||||
} else {
|
||||
state.selectedCats.remove(category);
|
||||
}
|
||||
state.getInfographyContent(page: 1);
|
||||
setState(() {
|
||||
_visibleItemCount = 4;
|
||||
});
|
||||
},
|
||||
selectedColor: const Color.fromARGB(255, 200, 224, 244),
|
||||
backgroundColor: isSelected
|
||||
? const Color.fromARGB(255, 200, 224, 244)
|
||||
: const Color.fromARGB(255, 224, 224, 224),
|
||||
labelStyle: TextStyle(
|
||||
color: isSelected
|
||||
? const Color.fromARGB(255, 0, 115, 153)
|
||||
: const Color.fromARGB(255, 102, 102, 102),
|
||||
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
),
|
||||
showCheckmark: false,
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPlaceholder() {
|
||||
return const DidvanCard(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ShimmerPlaceholder(
|
||||
height: 16,
|
||||
width: 240,
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
ShimmerPlaceholder(height: 200, width: 400),
|
||||
SizedBox(height: 12),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ShimmerPlaceholder(
|
||||
height: 32,
|
||||
width: 100,
|
||||
),
|
||||
],
|
||||
),
|
||||
DidvanDivider(
|
||||
verticalPadding: 12,
|
||||
),
|
||||
ShimmerPlaceholder(
|
||||
height: 16,
|
||||
width: double.infinity,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,235 @@
|
|||
import 'package:didvan/utils/action_sheet.dart';
|
||||
import 'package:didvan/views/home/main/main_page_state.dart';
|
||||
import 'package:didvan/views/widgets/skeleton_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
import 'package:carousel_slider/carousel_slider.dart';
|
||||
|
||||
class MediaBannerWithThumbnails extends StatefulWidget {
|
||||
final bool isFirst;
|
||||
const MediaBannerWithThumbnails({super.key, required this.isFirst});
|
||||
|
||||
@override
|
||||
State<MediaBannerWithThumbnails> createState() => _MediaBannerWithThumbnailsState();
|
||||
}
|
||||
|
||||
class _MediaBannerWithThumbnailsState extends State<MediaBannerWithThumbnails> {
|
||||
int _currentIndex = 0;
|
||||
int _thumbnailStartIndex = 0;
|
||||
final CarouselSliderController _carouselController = CarouselSliderController();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final state = context.read<MainPageState>();
|
||||
final banners = state.content!.banners[widget.isFirst ? 0 : 1];
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
CarouselSlider.builder(
|
||||
carouselController: _carouselController,
|
||||
itemCount: banners.length,
|
||||
itemBuilder: (context, index, realIndex) {
|
||||
final item = banners[index];
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 5),
|
||||
child: GestureDetector(
|
||||
onTap: () => item.link == null || item.link!.isEmpty
|
||||
? ActionSheetUtils(context)
|
||||
.openInteractiveViewer(context, item.image, false)
|
||||
: launchUrlString(item.link!, mode: LaunchMode.inAppWebView),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: SkeletonImage(
|
||||
imageUrl: item.image,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
options: CarouselOptions(
|
||||
height: (MediaQuery.of(context).size.width - 8) * 9.0 / 16.0,
|
||||
viewportFraction: 1.0,
|
||||
enableInfiniteScroll: true,
|
||||
autoPlay: true,
|
||||
autoPlayInterval: const Duration(seconds: 5),
|
||||
onPageChanged: (index, reason) {
|
||||
setState(() {
|
||||
_currentIndex = index;
|
||||
if (index >= _thumbnailStartIndex + 3) {
|
||||
_thumbnailStartIndex = index - 2;
|
||||
} else if (index < _thumbnailStartIndex) {
|
||||
_thumbnailStartIndex = index;
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 8),
|
||||
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: banners.asMap().entries.map((entry) {
|
||||
return AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
width: _currentIndex == entry.key ? 10 : 5,
|
||||
height: _currentIndex == entry.key ? 10 : 5,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 4),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(300),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: _currentIndex == entry.key
|
||||
? const Color.fromARGB(100, 0, 126, 167)
|
||||
: const Color.fromARGB(0, 0, 0, 0),
|
||||
spreadRadius: 2.5,
|
||||
offset: const Offset(0, 0),
|
||||
),
|
||||
],
|
||||
color: _currentIndex == entry.key
|
||||
? const Color.fromARGB(255, 0, 126, 167)
|
||||
: const Color.fromARGB(255, 200, 200, 200),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
Row(
|
||||
children: [
|
||||
IconButton(
|
||||
icon: SvgPicture.asset(
|
||||
'lib/assets/icons/arrow-right.svg',
|
||||
width: 24,
|
||||
height: 24,
|
||||
colorFilter: ColorFilter.mode(
|
||||
_thumbnailStartIndex > 0
|
||||
? const Color.fromARGB(255, 0, 126, 167)
|
||||
: const Color.fromARGB(255, 200, 200, 200),
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
),
|
||||
onPressed: _thumbnailStartIndex > 0
|
||||
? () {
|
||||
setState(() {
|
||||
_thumbnailStartIndex = (_thumbnailStartIndex - 1).clamp(0, banners.length - 3);
|
||||
});
|
||||
}
|
||||
: null,
|
||||
),
|
||||
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
height: 60,
|
||||
width: double.infinity,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: List.generate(3, (index) {
|
||||
if (_thumbnailStartIndex + index >= banners.length) {
|
||||
return const SizedBox(width: 0);
|
||||
}
|
||||
|
||||
final actualIndex = _thumbnailStartIndex + index;
|
||||
final item = banners[actualIndex];
|
||||
final isSelected = actualIndex == _currentIndex;
|
||||
|
||||
return Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
if (actualIndex == _currentIndex) return;
|
||||
|
||||
_carouselController.animateToPage(
|
||||
actualIndex,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeInOut,
|
||||
);
|
||||
|
||||
setState(() {
|
||||
_currentIndex = actualIndex;
|
||||
|
||||
if (actualIndex < _thumbnailStartIndex) {
|
||||
_thumbnailStartIndex = actualIndex;
|
||||
} else if (actualIndex >= _thumbnailStartIndex + 3) {
|
||||
_thumbnailStartIndex = (actualIndex - 2).clamp(0, banners.length - 3);
|
||||
}
|
||||
});
|
||||
},
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 4),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(
|
||||
color: isSelected
|
||||
? const Color.fromARGB(255, 0, 126, 167)
|
||||
: Colors.transparent,
|
||||
width: 2,
|
||||
),
|
||||
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: AnimatedOpacity(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
opacity: isSelected ? 1.0 : 0.6,
|
||||
child: ColorFiltered(
|
||||
colorFilter: isSelected
|
||||
? const ColorFilter.mode(
|
||||
Colors.transparent,
|
||||
BlendMode.multiply,
|
||||
)
|
||||
: const ColorFilter.matrix(<double>[
|
||||
0.2126, 0.7152, 0.0722, 0, 0,
|
||||
0.2126, 0.7152, 0.0722, 0, 0,
|
||||
0.2126, 0.7152, 0.0722, 0, 0,
|
||||
0, 0, 0, 1, 0,
|
||||
]),
|
||||
child: AspectRatio(
|
||||
aspectRatio: 19 / 13,
|
||||
child: SkeletonImage(
|
||||
imageUrl: item.image,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// فلش چپ (برای رفتن به جلو)
|
||||
IconButton(
|
||||
icon: SvgPicture.asset(
|
||||
'lib/assets/icons/arrow-left.svg',
|
||||
width: 24,
|
||||
height: 24,
|
||||
colorFilter: ColorFilter.mode(
|
||||
_thumbnailStartIndex < banners.length - 3
|
||||
? const Color.fromARGB(255, 0, 126, 167)
|
||||
: const Color.fromARGB(255, 200, 200, 200),
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
),
|
||||
onPressed: _thumbnailStartIndex < banners.length - 3
|
||||
? () {
|
||||
setState(() {
|
||||
_thumbnailStartIndex = (_thumbnailStartIndex + 1).clamp(0, banners.length - 3);
|
||||
});
|
||||
}
|
||||
: null,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
import 'package:didvan/config/design_config.dart';
|
||||
import 'package:didvan/constants/app_icons.dart';
|
||||
import 'package:didvan/models/view/action_sheet_data.dart';
|
||||
import 'package:didvan/providers/user.dart';
|
||||
|
|
@ -78,6 +77,9 @@ class _BookmarkButtonState extends State<BookmarkButton> {
|
|||
Widget build(BuildContext context) {
|
||||
print("BookmarkButton build - value: $_value");
|
||||
|
||||
final iconOn = widget.svgIconOn ?? 'lib/assets/icons/bookmark_on.svg';
|
||||
final iconOff = widget.svgIconOff ?? 'lib/assets/icons/bookmark_off.svg';
|
||||
|
||||
return IconButton(
|
||||
iconSize: widget.gestureSize,
|
||||
onPressed: _handleTap,
|
||||
|
|
@ -87,7 +89,7 @@ class _BookmarkButtonState extends State<BookmarkButton> {
|
|||
return ScaleTransition(scale: animation, child: child);
|
||||
},
|
||||
child: SvgPicture.asset(
|
||||
_value ? 'lib/assets/icons/bookmark_on.svg' : 'lib/assets/icons/bookmark_off.svg',
|
||||
_value ? iconOn : iconOff,
|
||||
key: ValueKey('bookmark_$_value'),
|
||||
width: widget.gestureSize,
|
||||
height: widget.gestureSize,
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@ import 'package:didvan/views/widgets/didvan/text.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
|
||||
import '../audio/player_navbar.dart';
|
||||
|
||||
class DidvanBNB extends StatelessWidget {
|
||||
final int currentTabIndex;
|
||||
final void Function(int index) onTabChanged;
|
||||
|
|
@ -17,28 +15,19 @@ class DidvanBNB extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
const PlayerNavBar(
|
||||
inHome: true,
|
||||
return Container(
|
||||
height: 72,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius:
|
||||
const BorderRadius.vertical(top: Radius.circular(0)),
|
||||
border: const Border(
|
||||
top: BorderSide(
|
||||
color: Color.fromARGB(255, 224, 224, 224),
|
||||
width: 1.5,
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Container(
|
||||
height: 72,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius:
|
||||
const BorderRadius.vertical(top: Radius.circular(0)),
|
||||
border: const Border(
|
||||
top: BorderSide(
|
||||
color: Color.fromARGB(255, 224, 224, 224),
|
||||
width: 1.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
child: Row(
|
||||
children: [
|
||||
|
|
@ -87,10 +76,7 @@ class DidvanBNB extends StatelessWidget {
|
|||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -229,7 +215,6 @@ class _NavBarItemState extends State<_NavBarItem>
|
|||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
// Ripple effect background
|
||||
AnimatedBuilder(
|
||||
animation: _rippleAnimation,
|
||||
builder: (context, child) {
|
||||
|
|
@ -245,7 +230,6 @@ class _NavBarItemState extends State<_NavBarItem>
|
|||
);
|
||||
},
|
||||
),
|
||||
// Main content
|
||||
Column(
|
||||
children: [
|
||||
const SizedBox(height: 4),
|
||||
|
|
@ -261,7 +245,6 @@ class _NavBarItemState extends State<_NavBarItem>
|
|||
padding: EdgeInsets.all(widget.isHomeButton ? 8 : 4),
|
||||
duration: DesignConfig.lowAnimationDuration,
|
||||
|
||||
//shadow
|
||||
decoration:
|
||||
BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
|
|
@ -302,7 +285,6 @@ class _NavBarItemState extends State<_NavBarItem>
|
|||
);
|
||||
},
|
||||
),
|
||||
// Animated text with fade and slide
|
||||
AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: (!widget.isHomeButton && !widget.isSelected)
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@ import 'package:didvan/models/view/app_bar_data.dart';
|
|||
import 'package:didvan/views/widgets/didvan/app_bar.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../audio/player_navbar.dart';
|
||||
|
||||
class DidvanScaffold extends StatefulWidget {
|
||||
final List<Widget>? slivers;
|
||||
final List<Widget>? children;
|
||||
|
|
@ -137,19 +135,6 @@ class _DidvanScaffoldState extends State<DidvanScaffold> {
|
|||
appBarData: widget.appBarData!,
|
||||
scrollController: _scrollController,
|
||||
),
|
||||
if (!widget.hidePlayer)
|
||||
const Positioned(
|
||||
bottom: 16,
|
||||
left: 16,
|
||||
right: 16,
|
||||
child: SizedBox(
|
||||
child: Center(
|
||||
child: PlayerNavBar(
|
||||
inHome: false,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -72,18 +72,22 @@ class _CarouselIndicator extends StatelessWidget {
|
|||
children: [
|
||||
for (int i = 0; i < count; i++)
|
||||
Container(
|
||||
width: 8,
|
||||
height: 8,
|
||||
width: 7,
|
||||
height: 7,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 2),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
width: 1,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: currentIndex == i ? const Color.fromARGB(255, 176, 215, 228) : Colors.transparent,
|
||||
blurRadius: 0,
|
||||
spreadRadius: 2.5,
|
||||
offset: const Offset(0, 0),
|
||||
),
|
||||
],
|
||||
color: currentIndex == i
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: Colors.transparent,
|
||||
? const Color.fromARGB(255, 0, 126, 167)
|
||||
: const Color.fromARGB(255, 173, 173, 173),
|
||||
),
|
||||
)
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
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/tag.dart';
|
||||
import 'package:didvan/routes/routes.dart';
|
||||
import 'package:didvan/views/widgets/didvan/text.dart';
|
||||
import 'package:didvan/views/widgets/ink_wrapper.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:persian_number_utility/persian_number_utility.dart';
|
||||
|
||||
class InfographyTag extends StatelessWidget {
|
||||
|
|
@ -34,23 +34,26 @@ class InfographyTag extends StatelessWidget {
|
|||
horizontal: 4,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: DesignConfig.lowBorderRadius,
|
||||
borderRadius: DesignConfig.mediumBorderRadius,
|
||||
border: Border.all(
|
||||
color: Theme.of(context).colorScheme.focusedBorder,
|
||||
color: const Color.fromARGB(255, 184, 184, 184),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
const Icon(
|
||||
DidvanIcons.hashtag_regular,
|
||||
color: Theme.of(context).colorScheme.focusedBorder,
|
||||
color: Color.fromARGB(255, 102, 102, 102),
|
||||
size: 16,
|
||||
),
|
||||
DidvanText(
|
||||
tag.label,
|
||||
color: Theme.of(context).colorScheme.focusedBorder,
|
||||
style: Theme.of(context).textTheme.labelLarge,
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(2.0),
|
||||
child: DidvanText(
|
||||
tag.label,
|
||||
color: const Color.fromARGB(255, 102, 102, 102),
|
||||
style: Theme.of(context).textTheme.labelMedium,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
@ -73,13 +76,16 @@ class InfoCat extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
const SizedBox(width: 15,),
|
||||
SvgPicture.asset('lib/assets/icons/calendar.svg',color: Colors.white,height: 22,),
|
||||
const SizedBox(width: 5,),
|
||||
// DidvanText(
|
||||
// category,
|
||||
// style: Theme.of(context).textTheme.bodySmall,
|
||||
// ),
|
||||
DidvanText(
|
||||
category,
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
DidvanText(
|
||||
' / ${DateTime.parse(createdAt).toPersianDateStr()}',
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
DateTime.parse(createdAt).toPersianDateStr(),
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ import 'package:didvan/models/view/action_sheet_data.dart';
|
|||
import 'package:didvan/providers/user.dart';
|
||||
import 'package:didvan/utils/action_sheet.dart';
|
||||
import 'package:didvan/views/widgets/didvan/text.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
|
||||
class LikedButton extends StatefulWidget {
|
||||
final bool value;
|
||||
|
|
@ -59,6 +59,7 @@ class _LikedButtonState extends State<LikedButton> {
|
|||
child: DidvanText(
|
||||
likes.toString(),
|
||||
fontSize: 14,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
InkWell(
|
||||
|
|
@ -90,11 +91,11 @@ class _LikedButtonState extends State<LikedButton> {
|
|||
UserProvider.changeItemLiked(widget.type, widget.itemId, _value);
|
||||
}
|
||||
},
|
||||
child: Icon(
|
||||
_value ? CupertinoIcons.heart_fill : CupertinoIcons.heart,
|
||||
size: 24,
|
||||
child: SvgPicture.asset(
|
||||
_value ? 'lib/assets/icons/heart_fill.svg':'lib/assets/icons/heart.svg',
|
||||
height: 24,
|
||||
color: widget.color ??
|
||||
(!_value ? null : Theme.of(context).colorScheme.error),
|
||||
(!_value ? Colors.white : Theme.of(context).colorScheme.error),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
|||