redesign videocast tab
This commit is contained in:
parent
f90d7eff6a
commit
37767c912d
|
|
@ -0,0 +1,12 @@
|
||||||
|
<svg width="19" height="18" viewBox="0 0 19 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M6.5 1.5V3.75" stroke="#666666" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M12.5 1.5V3.75" stroke="#666666" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M3.125 6.81738H15.875" stroke="#666666" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M16.25 6.375V12.75C16.25 15 15.125 16.5 12.5 16.5H6.5C3.875 16.5 2.75 15 2.75 12.75V6.375C2.75 4.125 3.875 2.625 6.5 2.625H12.5C15.125 2.625 16.25 4.125 16.25 6.375Z" stroke="#666666" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M12.271 10.2754H12.2778" stroke="#666666" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M12.271 12.5254H12.2778" stroke="#666666" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M9.49661 10.2754H9.50335" stroke="#666666" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M9.49661 12.5254H9.50335" stroke="#666666" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M6.72073 10.2754H6.72747" stroke="#666666" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M6.72073 12.5254H6.72747" stroke="#666666" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
|
|
@ -0,0 +1,4 @@
|
||||||
|
<svg width="18" height="19" viewBox="0 0 18 19" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M9 17.4625C4.5525 17.4625 0.9375 13.8475 0.9375 9.40002C0.9375 4.95252 4.5525 1.33752 9 1.33752C13.4475 1.33752 17.0625 4.95252 17.0625 9.40002C17.0625 13.8475 13.4475 17.4625 9 17.4625ZM9 2.46252C5.175 2.46252 2.0625 5.57502 2.0625 9.40002C2.0625 13.225 5.175 16.3375 9 16.3375C12.825 16.3375 15.9375 13.225 15.9375 9.40002C15.9375 5.57502 12.825 2.46252 9 2.46252Z" fill="#292D32"/>
|
||||||
|
<path d="M11.7822 12.3475C11.6847 12.3475 11.5872 12.325 11.4972 12.265L9.17224 10.8775C8.59474 10.5325 8.16724 9.77503 8.16724 9.10753V6.03253C8.16724 5.72503 8.42224 5.47003 8.72974 5.47003C9.03724 5.47003 9.29224 5.72503 9.29224 6.03253V9.10753C9.29224 9.37753 9.51724 9.77503 9.74974 9.91003L12.0747 11.2975C12.3447 11.455 12.4272 11.8 12.2697 12.07C12.1572 12.25 11.9697 12.3475 11.7822 12.3475Z" fill="#292D32"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 916 B |
|
|
@ -0,0 +1,4 @@
|
||||||
|
<svg width="25" height="25" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M12.47 22.5C17.9928 22.5 22.47 18.0228 22.47 12.5C22.47 6.97715 17.9928 2.5 12.47 2.5C6.94712 2.5 2.46997 6.97715 2.46997 12.5C2.46997 18.0228 6.94712 22.5 12.47 22.5Z" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M9.23999 12.7301V11.0601C9.23999 8.98012 10.71 8.13012 12.51 9.17012L13.96 10.0101L15.41 10.8501C17.21 11.8901 17.21 13.5901 15.41 14.6301L13.96 15.4701L12.51 16.3101C10.71 17.3501 9.23999 16.5001 9.23999 14.4201V12.7301Z" stroke="white" stroke-width="1.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 702 B |
|
|
@ -0,0 +1,4 @@
|
||||||
|
<svg width="18" height="19" viewBox="0 0 18 19" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M15.6667 9.50016C15.6667 13.1802 12.68 16.1668 9.00004 16.1668C5.32004 16.1668 2.33337 13.1802 2.33337 9.50016C2.33337 5.82016 5.32004 2.8335 9.00004 2.8335C12.68 2.8335 15.6667 5.82016 15.6667 9.50016Z" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M11.4733 11.6202L9.40663 10.3868C9.04663 10.1735 8.7533 9.66017 8.7533 9.24017V6.50684" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 541 B |
|
|
@ -1,5 +1,3 @@
|
||||||
// lib/routes/route_generator.dart
|
|
||||||
|
|
||||||
// ignore_for_file: avoid_print
|
// ignore_for_file: avoid_print
|
||||||
|
|
||||||
import 'package:didvan/models/ai/ai_chat_args.dart';
|
import 'package:didvan/models/ai/ai_chat_args.dart';
|
||||||
|
|
@ -30,6 +28,7 @@ import 'package:didvan/views/home/infography/infography_screen.dart';
|
||||||
import 'package:didvan/views/home/infography/infography_screen_state.dart';
|
import 'package:didvan/views/home/infography/infography_screen_state.dart';
|
||||||
import 'package:didvan/views/home/main/main_page_state.dart';
|
import 'package:didvan/views/home/main/main_page_state.dart';
|
||||||
import 'package:didvan/views/home/home_state.dart';
|
import 'package:didvan/views/home/home_state.dart';
|
||||||
|
import 'package:didvan/views/home/media/media_page.dart';
|
||||||
import 'package:didvan/views/home/new_statistic/new_statistics_state.dart';
|
import 'package:didvan/views/home/new_statistic/new_statistics_state.dart';
|
||||||
import 'package:didvan/views/home/new_statistic/statistics_details/stat_cats_general_screen.dart';
|
import 'package:didvan/views/home/new_statistic/statistics_details/stat_cats_general_screen.dart';
|
||||||
import 'package:didvan/views/home/new_statistic/statistics_details/stat_cats_general_state.dart';
|
import 'package:didvan/views/home/new_statistic/statistics_details/stat_cats_general_state.dart';
|
||||||
|
|
@ -43,6 +42,7 @@ import 'package:didvan/views/news/news_details/news_details_state.dart';
|
||||||
import 'package:didvan/views/news/news_state.dart';
|
import 'package:didvan/views/news/news_state.dart';
|
||||||
import 'package:didvan/views/notification_time/notification_time_state.dart';
|
import 'package:didvan/views/notification_time/notification_time_state.dart';
|
||||||
import 'package:didvan/views/podcasts/podcasts.dart';
|
import 'package:didvan/views/podcasts/podcasts.dart';
|
||||||
|
import 'package:didvan/views/podcasts/podcasts_state.dart';
|
||||||
import 'package:didvan/views/podcasts/studio_details/studio_details_state.dart';
|
import 'package:didvan/views/podcasts/studio_details/studio_details_state.dart';
|
||||||
import 'package:didvan/views/profile/profile.dart';
|
import 'package:didvan/views/profile/profile.dart';
|
||||||
import 'package:didvan/views/radar/radar.dart';
|
import 'package:didvan/views/radar/radar.dart';
|
||||||
|
|
@ -496,6 +496,14 @@ class RouteGenerator {
|
||||||
return _errorRoute(
|
return _errorRoute(
|
||||||
'Invalid arguments for ${settings.name}: Expected Map with stories and tappedIndex.');
|
'Invalid arguments for ${settings.name}: Expected Map with stories and tappedIndex.');
|
||||||
|
|
||||||
|
case Routes.media:
|
||||||
|
return MaterialPageRoute(
|
||||||
|
builder: (_) => ChangeNotifierProvider(
|
||||||
|
create: (context) => PodcastsState(),
|
||||||
|
child: const MediaPage(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return _errorRoute(settings.name ?? 'Unknown route');
|
return _errorRoute(settings.name ?? 'Unknown route');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,4 +41,5 @@ class Routes {
|
||||||
static const String newStatic = '/new-static';
|
static const String newStatic = '/new-static';
|
||||||
static const String web = '/web';
|
static const String web = '/web';
|
||||||
static const String storyViewer = '/story-viewer';
|
static const String storyViewer = '/story-viewer';
|
||||||
|
static const String media = '/media';
|
||||||
}
|
}
|
||||||
|
|
@ -65,48 +65,55 @@ class _MainPageState extends State<MainPage> {
|
||||||
onRetry: () => context.read<MainPageState>().init(),
|
onRetry: () => context.read<MainPageState>().init(),
|
||||||
state: context.watch<MainPageState>(),
|
state: context.watch<MainPageState>(),
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return ListView(
|
return Column(
|
||||||
padding: const EdgeInsets.only(top: 0, bottom: 16),
|
|
||||||
children: [
|
children: [
|
||||||
const HomeAppBar(
|
const HomeAppBar(
|
||||||
showBackButton: false,
|
showBackButton: false,
|
||||||
showSearchField: true,
|
showSearchField: true,
|
||||||
),
|
),
|
||||||
if (state.stories.isNotEmpty) ...[
|
Expanded(
|
||||||
const TextDivider(text: 'دیدهبان')
|
child: ListView(
|
||||||
.animate()
|
padding: const EdgeInsets.only(top: 0, bottom: 16),
|
||||||
.fadeIn(delay: 400.ms, duration: 500.ms),
|
children: [
|
||||||
const _DidvanSignalsTitle()
|
if (state.stories.isNotEmpty) ...[
|
||||||
.animate()
|
const TextDivider(text: 'دیدهبان')
|
||||||
.fadeIn(delay: 500.ms, duration: 500.ms),
|
.animate()
|
||||||
Padding(
|
.fadeIn(delay: 400.ms, duration: 500.ms),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
const _DidvanSignalsTitle()
|
||||||
child: StorySection(stories: state.stories),
|
.animate()
|
||||||
).animate().fadeIn(delay: 600.ms, duration: 500.ms),
|
.fadeIn(delay: 500.ms, duration: 500.ms),
|
||||||
],
|
Padding(
|
||||||
const SizedBox(height: 12),
|
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
const TextDivider(text: 'پیشخوان استراتژیک')
|
child: StorySection(stories: state.stories),
|
||||||
.animate()
|
).animate().fadeIn(delay: 600.ms, duration: 500.ms),
|
||||||
.fadeIn(delay: 700.ms, duration: 500.ms),
|
],
|
||||||
const Padding(
|
const SizedBox(height: 12),
|
||||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
const TextDivider(text: 'پیشخوان استراتژیک')
|
||||||
child: MainPageMainContent(),
|
.animate()
|
||||||
).animate().fadeIn(delay: 800.ms, duration: 500.ms),
|
.fadeIn(delay: 700.ms, duration: 500.ms),
|
||||||
if (state.content != null && state.content!.lists.isNotEmpty) ...[
|
const Padding(
|
||||||
const _ExploreLatestTitle()
|
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||||
.animate()
|
child: MainPageMainContent(),
|
||||||
.fadeIn(delay: 900.ms, duration: 500.ms),
|
).animate().fadeIn(delay: 800.ms, duration: 500.ms),
|
||||||
_ExploreLatestSlider(
|
if (state.content != null &&
|
||||||
lists: state.content!.lists,
|
state.content!.lists.isNotEmpty) ...[
|
||||||
swotItems: state.swotItems,
|
const _ExploreLatestTitle()
|
||||||
).animate().fadeIn(delay: 1000.ms, duration: 500.ms),
|
.animate()
|
||||||
],
|
.fadeIn(delay: 900.ms, duration: 500.ms),
|
||||||
const _IndustryPulseTitle()
|
_ExploreLatestSlider(
|
||||||
.animate()
|
lists: state.content!.lists,
|
||||||
.fadeIn(delay: 1100.ms, duration: 500.ms),
|
swotItems: state.swotItems,
|
||||||
const _IndustryPulseCards()
|
).animate().fadeIn(delay: 1000.ms, duration: 500.ms),
|
||||||
.animate()
|
],
|
||||||
.fadeIn(delay: 1200.ms, duration: 500.ms),
|
const _IndustryPulseTitle()
|
||||||
|
.animate()
|
||||||
|
.fadeIn(delay: 1100.ms, duration: 500.ms),
|
||||||
|
const _IndustryPulseCards()
|
||||||
|
.animate()
|
||||||
|
.fadeIn(delay: 1200.ms, duration: 500.ms),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
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/widgets/didvan/text.dart';
|
||||||
import 'package:didvan/views/home/main/widgets/banner.dart';
|
import 'package:didvan/views/home/main/widgets/banner.dart';
|
||||||
import 'package:didvan/views/widgets/home_app_bar.dart';
|
import 'package:didvan/views/widgets/home_app_bar.dart';
|
||||||
|
|
@ -66,7 +68,8 @@ class _MediaPageState extends State<MediaPage> {
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 7), // 10% فاصله از هر طرف برای 80%
|
padding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 7),
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 2,
|
height: 2,
|
||||||
color: const Color.fromRGBO(184, 184, 184, 1),
|
color: const Color.fromRGBO(184, 184, 184, 1),
|
||||||
|
|
@ -81,44 +84,12 @@ class _MediaPageState extends State<MediaPage> {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
// محتوای تب پادکستها
|
const PodcastTabPage(),
|
||||||
Center(
|
const VideoCastTabPage(),
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
Icons.podcasts,
|
|
||||||
size: 48,
|
|
||||||
color: Theme.of(context).colorScheme.primary,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
DidvanText(
|
|
||||||
'صفحه پادکستها',
|
|
||||||
style: Theme.of(context).textTheme.titleSmall,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Center(
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
Icons.video_library,
|
|
||||||
size: 48,
|
|
||||||
color: Theme.of(context).colorScheme.primary,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
DidvanText(
|
|
||||||
'صفحه ویدئوها',
|
|
||||||
style: Theme.of(context).textTheme.titleSmall,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SingleChildScrollView(
|
const SingleChildScrollView(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0),
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: 16.0, vertical: 16.0),
|
||||||
child: MainPageBanner(isFirst: true),
|
child: MainPageBanner(isFirst: true),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
import 'package:didvan/models/requests/studio.dart';
|
||||||
|
import 'package:didvan/views/podcasts/podcasts_state.dart';
|
||||||
|
import 'package:didvan/views/widgets/overview/podcast.dart';
|
||||||
|
import 'package:didvan/views/widgets/state_handlers/empty_result.dart';
|
||||||
|
import 'package:didvan/views/widgets/state_handlers/state_handler.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class PodcastTabPage extends StatefulWidget {
|
||||||
|
const PodcastTabPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<PodcastTabPage> createState() => _PodcastTabPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PodcastTabPageState extends State<PodcastTabPage> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
Future.microtask(() {
|
||||||
|
context.read<PodcastsState>().init(true);
|
||||||
|
context.read<PodcastsState>().getStudios(page: 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final state = context.watch<PodcastsState>();
|
||||||
|
|
||||||
|
return StateHandler<PodcastsState>(
|
||||||
|
state: state,
|
||||||
|
emptyState: EmptyResult(
|
||||||
|
onNewSearch: () {},
|
||||||
|
),
|
||||||
|
enableEmptyState: state.studios.isEmpty,
|
||||||
|
placeholder: PodcastOverview.placeholder,
|
||||||
|
builder: (context, state) {
|
||||||
|
return ListView.builder(
|
||||||
|
itemCount: state.studios.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final podcast = state.studios[index];
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
bottom: 8,
|
||||||
|
left: 16,
|
||||||
|
right: 16,
|
||||||
|
),
|
||||||
|
child: PodcastOverview(
|
||||||
|
podcast: podcast,
|
||||||
|
onMarkChanged: state.changeMark,
|
||||||
|
studioRequestArgs: StudioRequestArgs(
|
||||||
|
page: state.page,
|
||||||
|
order: state.order,
|
||||||
|
search: state.search,
|
||||||
|
type: state.type,
|
||||||
|
asc: state.selectedSortTypeIndex == 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onRetry: () => context.read<PodcastsState>().getStudios(page: 1),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
import 'package:didvan/routes/routes.dart';
|
||||||
|
import 'package:didvan/views/home/media/widgets/featured_video_card.dart';
|
||||||
|
import 'package:didvan/views/home/media/widgets/videocast_grid_card.dart';
|
||||||
|
import 'package:didvan/views/podcasts/podcasts_state.dart';
|
||||||
|
import 'package:didvan/views/widgets/overview/podcast.dart';
|
||||||
|
import 'package:didvan/views/widgets/state_handlers/empty_result.dart';
|
||||||
|
import 'package:didvan/views/widgets/state_handlers/state_handler.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class VideoCastTabPage extends StatefulWidget {
|
||||||
|
const VideoCastTabPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<VideoCastTabPage> createState() => _VideoCastTabPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _VideoCastTabPageState extends State<VideoCastTabPage> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
Future.microtask(() {
|
||||||
|
context.read<PodcastsState>().init(false);
|
||||||
|
context.read<PodcastsState>().getStudios(page: 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final state = context.watch<PodcastsState>();
|
||||||
|
|
||||||
|
return StateHandler<PodcastsState>(
|
||||||
|
state: state,
|
||||||
|
emptyState: EmptyResult(
|
||||||
|
onNewSearch: () {},
|
||||||
|
),
|
||||||
|
enableEmptyState: state.studios.isEmpty,
|
||||||
|
placeholder: PodcastOverview.placeholder,
|
||||||
|
builder: (context, state) {
|
||||||
|
if (state.studios.isEmpty) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
|
||||||
|
return SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
FeaturedVideoCard(
|
||||||
|
videocast: state.studios.first,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.pushNamed(
|
||||||
|
context,
|
||||||
|
Routes.studioDetails,
|
||||||
|
arguments: {
|
||||||
|
'id': state.studios.first.id,
|
||||||
|
'type': state.studios.first.type,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
if (state.studios.length > 1)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: GridView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount: 2,
|
||||||
|
crossAxisSpacing: 12,
|
||||||
|
mainAxisSpacing: 12,
|
||||||
|
childAspectRatio: 0.75,
|
||||||
|
),
|
||||||
|
itemCount: state.studios.length - 1,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final videocast = state.studios[index + 1];
|
||||||
|
return VideocastGridCard(
|
||||||
|
videocast: videocast,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.pushNamed(
|
||||||
|
context,
|
||||||
|
Routes.studioDetails,
|
||||||
|
arguments: {
|
||||||
|
'id': videocast.id,
|
||||||
|
'type': videocast.type,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onRetry: () => context.read<PodcastsState>().getStudios(page: 1),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,189 @@
|
||||||
|
import 'package:didvan/models/overview_data.dart';
|
||||||
|
import 'package:didvan/views/widgets/didvan/text.dart';
|
||||||
|
import 'package:didvan/views/widgets/skeleton_image.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
import 'package:persian_number_utility/persian_number_utility.dart';
|
||||||
|
|
||||||
|
class FeaturedVideoCard extends StatelessWidget {
|
||||||
|
final OverviewData videocast;
|
||||||
|
final VoidCallback onTap;
|
||||||
|
|
||||||
|
const FeaturedVideoCard({
|
||||||
|
super.key,
|
||||||
|
required this.videocast,
|
||||||
|
required this.onTap,
|
||||||
|
});
|
||||||
|
|
||||||
|
String _formatDuration(int? duration) {
|
||||||
|
if (duration == null) return '';
|
||||||
|
final minutes = duration ~/ 60;
|
||||||
|
final seconds = duration % 60;
|
||||||
|
return '”${seconds.toString().padLeft(2, '0')}:’${minutes.toString().padLeft(2, '0')}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
height: MediaQuery.of(context).size.height * 0.40,
|
||||||
|
margin: const EdgeInsets.all(16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withOpacity(0.1),
|
||||||
|
blurRadius: 10,
|
||||||
|
offset: const Offset(0, 4),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
Positioned.fill(
|
||||||
|
child: ColorFiltered(
|
||||||
|
colorFilter: 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: SkeletonImage(
|
||||||
|
imageUrl: videocast.image,
|
||||||
|
width: double.infinity,
|
||||||
|
height: double.infinity,
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
Positioned(
|
||||||
|
bottom: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
child: Container(
|
||||||
|
height: 200,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
begin: Alignment.bottomCenter,
|
||||||
|
end: Alignment.topCenter,
|
||||||
|
colors: [
|
||||||
|
Colors.black.withOpacity(0.8),
|
||||||
|
Colors.black.withOpacity(0.4),
|
||||||
|
Colors.transparent,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
Positioned(
|
||||||
|
bottom: 16,
|
||||||
|
left: 16,
|
||||||
|
right: 16,
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
IntrinsicWidth(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
DidvanText(
|
||||||
|
videocast.title,
|
||||||
|
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
|
||||||
|
color: const Color.fromARGB(255, 200, 224, 244),
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Container(
|
||||||
|
height: 2,
|
||||||
|
color: const Color.fromARGB(255, 200, 224, 244),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
|
||||||
|
DidvanText(
|
||||||
|
videocast.description,
|
||||||
|
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||||
|
color: Colors.white,
|
||||||
|
height: 1.4,
|
||||||
|
),
|
||||||
|
maxLines: 4,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 5),
|
||||||
|
|
||||||
|
Align(
|
||||||
|
alignment: AlignmentDirectional.centerEnd,
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
SvgPicture.asset("lib/assets/icons/timer.svg"),
|
||||||
|
const SizedBox(width: 6),
|
||||||
|
DidvanText(
|
||||||
|
_formatDuration(videocast.duration).toPersianDigit(),
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
child: ElevatedButton.icon(
|
||||||
|
onPressed: onTap,
|
||||||
|
icon: SvgPicture.asset('lib/assets/icons/play-circle.svg'),
|
||||||
|
label: const DidvanText(
|
||||||
|
'مشاهده ویدیو',
|
||||||
|
style: TextStyle(color: Colors.white),
|
||||||
|
),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: const Color.fromARGB(255, 178, 4, 54),
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 60.0),
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
child: SkeletonImage(
|
||||||
|
imageUrl: videocast.image,
|
||||||
|
width: 115,
|
||||||
|
height: 155,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,132 @@
|
||||||
|
import 'package:didvan/models/overview_data.dart';
|
||||||
|
import 'package:didvan/views/widgets/didvan/text.dart';
|
||||||
|
import 'package:didvan/views/widgets/skeleton_image.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
import 'package:persian_number_utility/persian_number_utility.dart';
|
||||||
|
|
||||||
|
class VideocastGridCard extends StatelessWidget {
|
||||||
|
final OverviewData videocast;
|
||||||
|
final VoidCallback onTap;
|
||||||
|
|
||||||
|
const VideocastGridCard({
|
||||||
|
super.key,
|
||||||
|
required this.videocast,
|
||||||
|
required this.onTap,
|
||||||
|
});
|
||||||
|
|
||||||
|
String _formatDuration(int? duration) {
|
||||||
|
if (duration == null) return '';
|
||||||
|
final minutes = duration ~/ 60;
|
||||||
|
final seconds = duration % 60;
|
||||||
|
return '”${seconds.toString().padLeft(2, '0')}:’${minutes.toString().padLeft(2, '0')}';
|
||||||
|
}
|
||||||
|
|
||||||
|
String _formatDate(String dateStr) {
|
||||||
|
try {
|
||||||
|
final date = DateTime.parse(dateStr);
|
||||||
|
return date.toPersianDateStr();
|
||||||
|
} catch (e) {
|
||||||
|
return dateStr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: onTap,
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: const Color.fromRGBO(235, 235, 235, 1),
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withOpacity(0.05),
|
||||||
|
blurRadius: 4,
|
||||||
|
offset: const Offset(0, 2),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
// Video cover
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(6.0),
|
||||||
|
child: AspectRatio(
|
||||||
|
aspectRatio: 17 / 14,
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
child: SkeletonImage(
|
||||||
|
imageUrl: videocast.image,
|
||||||
|
width: double.infinity,
|
||||||
|
height: double.infinity,
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
Expanded(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(12, 4, 12, 8),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
DidvanText(
|
||||||
|
videocast.title,
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Colors.black87,
|
||||||
|
),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
SvgPicture.asset('lib/assets/icons/calendar.svg'),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
DidvanText(
|
||||||
|
_formatDate(videocast.createdAt),
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodySmall
|
||||||
|
?.copyWith(
|
||||||
|
color: const Color.fromARGB(255, 102, 102, 102),
|
||||||
|
),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
SvgPicture.asset(
|
||||||
|
'lib/assets/icons/clock.svg',
|
||||||
|
color: const Color.fromARGB(255, 102, 102, 102),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
DidvanText(
|
||||||
|
_formatDuration(videocast.duration).toPersianDigit(),
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodySmall
|
||||||
|
?.copyWith(
|
||||||
|
color: const Color.fromARGB(255, 102, 102, 102),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue