home page v1
This commit is contained in:
parent
0349a8939b
commit
1fe0e8def5
|
|
@ -0,0 +1,13 @@
|
||||||
|
class HomePageBannerType {
|
||||||
|
final String image;
|
||||||
|
final String? link;
|
||||||
|
|
||||||
|
HomePageBannerType({required this.image, required this.link});
|
||||||
|
|
||||||
|
factory HomePageBannerType.fromJson(Map<String, dynamic> json) {
|
||||||
|
return HomePageBannerType(
|
||||||
|
image: json['image'],
|
||||||
|
link: json['link'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,26 +1,30 @@
|
||||||
class Content {
|
class HomePageContentType {
|
||||||
final int? id;
|
final int id;
|
||||||
final String title;
|
final String title;
|
||||||
final String image;
|
final String image;
|
||||||
final String link;
|
final String link;
|
||||||
final bool marked;
|
final bool marked;
|
||||||
final List<String> subtitles;
|
final List<String> subtitles;
|
||||||
|
final int? duration;
|
||||||
|
|
||||||
const Content({
|
const HomePageContentType({
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.title,
|
required this.title,
|
||||||
required this.image,
|
required this.image,
|
||||||
required this.link,
|
required this.link,
|
||||||
required this.marked,
|
required this.marked,
|
||||||
required this.subtitles,
|
required this.subtitles,
|
||||||
|
this.duration,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory Content.fromJson(Map<String, dynamic> json) => Content(
|
factory HomePageContentType.fromJson(Map<String, dynamic> json) =>
|
||||||
|
HomePageContentType(
|
||||||
id: json['id'],
|
id: json['id'],
|
||||||
title: json['title'],
|
title: json['title'],
|
||||||
image: json['image'],
|
image: json['image'],
|
||||||
link: json['link'],
|
link: json['link'],
|
||||||
marked: json['marked'],
|
marked: json['marked'],
|
||||||
subtitles: List<String>.from(json['subtitles']),
|
subtitles: List<String>.from(json['subtitles']),
|
||||||
|
duration: json['duration'],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
|
import 'package:didvan/models/home_page_content/banner.dart';
|
||||||
|
|
||||||
import 'home_page_list.dart';
|
import 'home_page_list.dart';
|
||||||
|
|
||||||
class HomePageContent {
|
class HomePageContent {
|
||||||
final List<dynamic> banners;
|
final List<HomePageBannerType> banners;
|
||||||
final List<HomePageList> lists;
|
final List<HomePageList> lists;
|
||||||
final int unread;
|
final int unread;
|
||||||
|
|
||||||
|
|
@ -10,7 +12,9 @@ class HomePageContent {
|
||||||
|
|
||||||
factory HomePageContent.fromJson(Map<String, dynamic> json) {
|
factory HomePageContent.fromJson(Map<String, dynamic> json) {
|
||||||
return HomePageContent(
|
return HomePageContent(
|
||||||
banners: json['banners'],
|
banners: List<HomePageBannerType>.from(json['banners'].map(
|
||||||
|
(x) => HomePageBannerType.fromJson(x),
|
||||||
|
)),
|
||||||
lists: List<HomePageList>.from(
|
lists: List<HomePageList>.from(
|
||||||
json['lists'].map(
|
json['lists'].map(
|
||||||
(x) => HomePageList.fromJson(x),
|
(x) => HomePageList.fromJson(x),
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ class HomePageList {
|
||||||
final String header;
|
final String header;
|
||||||
final String more;
|
final String more;
|
||||||
final String link;
|
final String link;
|
||||||
final List<Content> contents;
|
final List<HomePageContentType> contents;
|
||||||
|
|
||||||
const HomePageList({
|
const HomePageList({
|
||||||
required this.type,
|
required this.type,
|
||||||
|
|
@ -20,7 +20,7 @@ class HomePageList {
|
||||||
header: json['header'],
|
header: json['header'],
|
||||||
more: json['more'],
|
more: json['more'],
|
||||||
link: json['link'],
|
link: json['link'],
|
||||||
contents: List<Content>.from(json['contents'].map(
|
contents: List<HomePageContentType>.from(json['contents'].map(
|
||||||
(x) => Content.fromJson(x),
|
(x) => HomePageContentType.fromJson(x),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,9 @@ class RouteGenerator {
|
||||||
ChangeNotifierProvider<StatisticState>(
|
ChangeNotifierProvider<StatisticState>(
|
||||||
create: (context) => StatisticState(),
|
create: (context) => StatisticState(),
|
||||||
),
|
),
|
||||||
|
ChangeNotifierProvider<PodcastsState>(
|
||||||
|
create: (context) => PodcastsState(),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
child: const Home(),
|
child: const Home(),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,14 @@ import 'dart:io';
|
||||||
import 'package:assets_audio_player/assets_audio_player.dart';
|
import 'package:assets_audio_player/assets_audio_player.dart';
|
||||||
import 'package:didvan/config/theme_data.dart';
|
import 'package:didvan/config/theme_data.dart';
|
||||||
import 'package:didvan/constants/app_icons.dart';
|
import 'package:didvan/constants/app_icons.dart';
|
||||||
|
import 'package:didvan/models/requests/studio.dart';
|
||||||
import 'package:didvan/models/studio_details_data.dart';
|
import 'package:didvan/models/studio_details_data.dart';
|
||||||
import 'package:didvan/services/media/media.dart';
|
import 'package:didvan/services/media/media.dart';
|
||||||
import 'package:didvan/views/home/widgets/audio/audio_slider.dart';
|
import 'package:didvan/views/home/widgets/audio/audio_slider.dart';
|
||||||
|
import 'package:didvan/views/podcasts/studio_details/studio_details_state.dart';
|
||||||
import 'package:didvan/views/widgets/didvan/icon_button.dart';
|
import 'package:didvan/views/widgets/didvan/icon_button.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class AudioWidget extends StatelessWidget {
|
class AudioWidget extends StatelessWidget {
|
||||||
final String? audioUrl;
|
final String? audioUrl;
|
||||||
|
|
@ -34,7 +37,9 @@ class AudioWidget extends StatelessWidget {
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(top: 12),
|
padding: const EdgeInsets.only(top: 12),
|
||||||
child: AudioSlider(
|
child: AudioSlider(
|
||||||
tag: audioMetaData != null ? 'radar-$id' : 'message-$id',
|
tag: audioMetaData != null
|
||||||
|
? '${audioMetaData!.type}-$id'
|
||||||
|
: 'message-$id',
|
||||||
duration: audioMetaData?.duration,
|
duration: audioMetaData?.duration,
|
||||||
showTimer: true,
|
showTimer: true,
|
||||||
),
|
),
|
||||||
|
|
@ -70,7 +75,7 @@ class _AudioControllerButton extends StatelessWidget {
|
||||||
|
|
||||||
bool get _nowPlaying =>
|
bool get _nowPlaying =>
|
||||||
MediaService.audioPlayerTag ==
|
MediaService.audioPlayerTag ==
|
||||||
(audioMetaData != null ? 'radar-$id' : 'message-$id');
|
(audioMetaData != null ? '${audioMetaData!.type}-$id' : 'message-$id');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
@ -84,6 +89,15 @@ class _AudioControllerButton extends StatelessWidget {
|
||||||
gestureSize: 36,
|
gestureSize: 36,
|
||||||
color: Theme.of(context).colorScheme.focusedBorder,
|
color: Theme.of(context).colorScheme.focusedBorder,
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
if (audioMetaData?.type == 'podcast') {
|
||||||
|
final state = context.read<StudioDetailsState>();
|
||||||
|
if (MediaService.currentPodcast == null) {
|
||||||
|
await state.getStudioDetails(
|
||||||
|
id,
|
||||||
|
args: const StudioRequestArgs(page: 0, type: 'podcast'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (snapshot.data == null && _nowPlaying) {
|
if (snapshot.data == null && _nowPlaying) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,17 @@
|
||||||
import 'package:carousel_slider/carousel_slider.dart';
|
import 'package:didvan/config/design_config.dart';
|
||||||
import 'package:didvan/config/theme_data.dart';
|
import 'package:didvan/config/theme_data.dart';
|
||||||
import 'package:didvan/constants/app_icons.dart';
|
import 'package:didvan/constants/app_icons.dart';
|
||||||
import 'package:didvan/models/home_page_content/home_page_list.dart';
|
import 'package:didvan/models/home_page_content/home_page_list.dart';
|
||||||
import 'package:didvan/views/home/home/home_page_state.dart';
|
import 'package:didvan/views/home/home/home_page_state.dart';
|
||||||
import 'package:didvan/views/widgets/didvan/card.dart';
|
import 'package:didvan/views/home/home/widgets/banner.dart';
|
||||||
|
import 'package:didvan/views/home/home/widgets/general_item.dart';
|
||||||
|
import 'package:didvan/views/home/home/widgets/main_content.dart';
|
||||||
|
import 'package:didvan/views/home/home/widgets/news_item.dart';
|
||||||
|
import 'package:didvan/views/home/home/widgets/podcast_item.dart';
|
||||||
|
import 'package:didvan/views/home/home/widgets/radar_item.dart';
|
||||||
|
import 'package:didvan/views/home/home/widgets/videocast_item.dart';
|
||||||
|
import 'package:didvan/views/widgets/didvan/slider.dart';
|
||||||
import 'package:didvan/views/widgets/didvan/text.dart';
|
import 'package:didvan/views/widgets/didvan/text.dart';
|
||||||
import 'package:didvan/views/widgets/skeleton_image.dart';
|
|
||||||
import 'package:didvan/views/widgets/state_handlers/state_handler.dart';
|
import 'package:didvan/views/widgets/state_handlers/state_handler.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
@ -21,10 +27,7 @@ class HomePage extends StatefulWidget {
|
||||||
class _HomePageState extends State<HomePage> {
|
class _HomePageState extends State<HomePage> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
Future.delayed(
|
context.read<HomePageState>().init();
|
||||||
Duration.zero,
|
|
||||||
context.read<HomePageState>().getMainPageContent,
|
|
||||||
);
|
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,37 +36,42 @@ class _HomePageState extends State<HomePage> {
|
||||||
return StateHandler<HomePageState>(
|
return StateHandler<HomePageState>(
|
||||||
onRetry: () {},
|
onRetry: () {},
|
||||||
state: context.watch<HomePageState>(),
|
state: context.watch<HomePageState>(),
|
||||||
builder: (context, state) =>
|
builder: (context, state) => ListView.builder(
|
||||||
ListView.builder(itemBuilder: (context, index) {
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
return CarouselSlider(
|
return const HomePageMainContent();
|
||||||
items: state.content.banners
|
}
|
||||||
.map((e) => SkeletonImage(
|
index--;
|
||||||
imageUrl: e.imageUrl,
|
if (index == 4) {
|
||||||
))
|
return const Padding(
|
||||||
.toList(),
|
padding: EdgeInsets.only(top: 32),
|
||||||
options: CarouselOptions(
|
child: HomePageBanner(
|
||||||
viewportFraction: 1.0,
|
isFirst: false,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return const SizedBox();
|
if (index > 3) {
|
||||||
// state.content.banners.map((e) => const SizedBox()).toList(),
|
index--;
|
||||||
// for (int i = 0; i < state.content.lists.length; i += 2)
|
}
|
||||||
// ...state.content.lists
|
final list = state.content.lists[index];
|
||||||
// .sublist(i, i + 2)
|
return _HomePageSection(
|
||||||
// .map((e) => _HomePageItem(list: e))
|
list: list,
|
||||||
// .toList(),
|
isLast: index == state.content.lists.length - 1,
|
||||||
}),
|
);
|
||||||
|
},
|
||||||
|
itemCount: state.content.lists.length + 2,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _HomePageItem extends StatelessWidget {
|
class _HomePageSection extends StatelessWidget {
|
||||||
final HomePageList list;
|
final HomePageList list;
|
||||||
const _HomePageItem({required this.list});
|
final bool isLast;
|
||||||
|
const _HomePageSection({required this.list, required this.isLast});
|
||||||
|
|
||||||
void moreHandler(BuildContext context) {
|
void _moreHandler(BuildContext context) {
|
||||||
if (list.link.startsWith('http')) {
|
if (list.link.startsWith('http')) {
|
||||||
launchUrl(Uri.parse(list.link));
|
launchUrl(Uri.parse(list.link));
|
||||||
return;
|
return;
|
||||||
|
|
@ -71,8 +79,34 @@ class _HomePageItem extends StatelessWidget {
|
||||||
Navigator.of(context).pushNamed(list.link);
|
Navigator.of(context).pushNamed(list.link);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IconData? _generateIcon() {
|
||||||
|
switch (list.type) {
|
||||||
|
case 'news':
|
||||||
|
return DidvanIcons.news_solid;
|
||||||
|
case 'radar':
|
||||||
|
return DidvanIcons.radar_solid;
|
||||||
|
case 'video':
|
||||||
|
return DidvanIcons.video_solid;
|
||||||
|
case 'podcast':
|
||||||
|
return DidvanIcons.podcast_solid;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int _maxSublistCount() {
|
||||||
|
int max = 1;
|
||||||
|
for (var i = 0; i < list.contents.length; i++) {
|
||||||
|
if (list.contents[i].subtitles.length > max) {
|
||||||
|
max = list.contents[i].subtitles.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return max - 1;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final icon = _generateIcon();
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
|
|
@ -85,13 +119,19 @@ class _HomePageItem extends StatelessWidget {
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
if (icon != null) Icon(icon),
|
||||||
|
const SizedBox(width: 4),
|
||||||
DidvanText(
|
DidvanText(
|
||||||
list.header,
|
list.header,
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
color: Theme.of(context).colorScheme.title,
|
color: Theme.of(context).colorScheme.title,
|
||||||
),
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () => moreHandler(context),
|
onTap: () => _moreHandler(context),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
DidvanText(
|
DidvanText(
|
||||||
|
|
@ -108,63 +148,97 @@ class _HomePageItem extends StatelessWidget {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
CarouselSlider.builder(
|
if (list.type == 'news')
|
||||||
|
DidvanSlider(
|
||||||
|
height: 232,
|
||||||
itemCount: list.contents.length,
|
itemCount: list.contents.length,
|
||||||
itemBuilder: (context, index, realIndex) => DidvanCard(
|
viewportFraction: 0.45,
|
||||||
margin: const EdgeInsets.only(left: 8),
|
itemBuilder: (context, index, realIndex) => Padding(
|
||||||
padding: EdgeInsets.zero,
|
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||||
child: Column(
|
child: HomePageNewsItem(
|
||||||
children: [
|
content: list.contents[index],
|
||||||
SkeletonImage(
|
|
||||||
imageUrl: list.contents[index].image,
|
|
||||||
height: MediaQuery.of(context).size.height / 6,
|
|
||||||
width: double.infinity,
|
|
||||||
),
|
),
|
||||||
Container(
|
|
||||||
width: double.infinity,
|
|
||||||
padding:
|
|
||||||
const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
DidvanText(
|
|
||||||
list.contents[index].title,
|
|
||||||
style: Theme.of(context).textTheme.bodyLarge,
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
),
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
const Icon(
|
|
||||||
DidvanIcons.puzzle_light,
|
|
||||||
size: 16,
|
|
||||||
),
|
),
|
||||||
...list.contents[index].subtitles.map(
|
if (list.type == 'radar')
|
||||||
|
DidvanSlider(
|
||||||
|
height: 148,
|
||||||
|
itemCount: list.contents.length,
|
||||||
|
viewportFraction: 0.6,
|
||||||
|
itemBuilder: (context, index, realIndex) => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||||
|
child: HomePageRadarItem(
|
||||||
|
content: list.contents[index],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (list.type == 'video')
|
||||||
|
DidvanSlider(
|
||||||
|
height: 180,
|
||||||
|
itemCount: list.contents.length,
|
||||||
|
viewportFraction: 0.6,
|
||||||
|
itemBuilder: (context, index, realIndex) => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||||
|
child: HomePageVideocastItem(
|
||||||
|
content: list.contents[index],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (list.type == 'podcast')
|
||||||
|
Column(
|
||||||
|
children: list.contents
|
||||||
|
.map(
|
||||||
(e) => Padding(
|
(e) => Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
padding: const EdgeInsets.only(
|
||||||
child: DidvanText(
|
bottom: 12,
|
||||||
e,
|
left: 28,
|
||||||
maxLines: 1,
|
right: 28,
|
||||||
overflow: TextOverflow.ellipsis,
|
),
|
||||||
style: Theme.of(context).textTheme.bodySmall,
|
child: HomePagePodcastItem(
|
||||||
|
content: e,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
],
|
.toList(),
|
||||||
|
),
|
||||||
|
if (list.type != 'news' &&
|
||||||
|
list.type != 'radar' &&
|
||||||
|
list.type != 'video' &&
|
||||||
|
list.type != 'podcast')
|
||||||
|
DidvanSlider(
|
||||||
|
itemBuilder: (context, index, realIndex) => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||||
|
child: HomePageGeneralItem(
|
||||||
|
content: list.contents[index],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
itemCount: list.contents.length,
|
||||||
),
|
|
||||||
),
|
|
||||||
options: CarouselOptions(
|
|
||||||
autoPlay: true,
|
|
||||||
padEnds: false,
|
|
||||||
viewportFraction: 0.7,
|
viewportFraction: 0.7,
|
||||||
|
height: 196 + _maxSublistCount() * 20,
|
||||||
),
|
),
|
||||||
),
|
if (!isLast) const _HomePageDivider(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _HomePageDivider extends StatelessWidget {
|
||||||
|
const _HomePageDivider();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
height: 2,
|
||||||
|
margin: const EdgeInsets.only(
|
||||||
|
top: 8,
|
||||||
|
left: 20,
|
||||||
|
right: 20,
|
||||||
|
),
|
||||||
|
width: double.infinity,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: DesignConfig.highBorderRadius,
|
||||||
|
color: Theme.of(context).colorScheme.border,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import 'package:didvan/constants/assets.dart';
|
||||||
|
import 'package:didvan/models/category.dart';
|
||||||
import 'package:didvan/models/enums.dart';
|
import 'package:didvan/models/enums.dart';
|
||||||
import 'package:didvan/models/home_page_content/home_page_content.dart';
|
import 'package:didvan/models/home_page_content/home_page_content.dart';
|
||||||
import 'package:didvan/providers/core.dart';
|
import 'package:didvan/providers/core.dart';
|
||||||
|
|
@ -5,8 +7,9 @@ import 'package:didvan/services/network/request.dart';
|
||||||
import 'package:didvan/services/network/request_helper.dart';
|
import 'package:didvan/services/network/request_helper.dart';
|
||||||
|
|
||||||
class HomePageState extends CoreProvier {
|
class HomePageState extends CoreProvier {
|
||||||
late final HomePageContent content;
|
late HomePageContent content;
|
||||||
Future<void> getMainPageContent() async {
|
List<CategoryData> categories = [];
|
||||||
|
Future<void> _getMainPageContent() async {
|
||||||
final service = RequestService(RequestHelper.mainPageContent);
|
final service = RequestService(RequestHelper.mainPageContent);
|
||||||
await service.httpGet();
|
await service.httpGet();
|
||||||
if (service.isSuccess) {
|
if (service.isSuccess) {
|
||||||
|
|
@ -16,4 +19,64 @@ class HomePageState extends CoreProvier {
|
||||||
}
|
}
|
||||||
appState = AppState.failed;
|
appState = AppState.failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
if (categories.isEmpty) {
|
||||||
|
categories = [
|
||||||
|
CategoryData(
|
||||||
|
id: 1,
|
||||||
|
label: 'اقتصادی',
|
||||||
|
asset: Assets.economicCategoryIcon,
|
||||||
|
),
|
||||||
|
CategoryData(
|
||||||
|
id: 2,
|
||||||
|
label: 'سیاسی',
|
||||||
|
asset: Assets.politicalCategoryIcon,
|
||||||
|
),
|
||||||
|
CategoryData(
|
||||||
|
id: 3,
|
||||||
|
label: 'فناوری',
|
||||||
|
asset: Assets.techCategoryIcon,
|
||||||
|
),
|
||||||
|
CategoryData(
|
||||||
|
id: 4,
|
||||||
|
label: 'کسب و کار',
|
||||||
|
asset: Assets.businessCategoryIcon,
|
||||||
|
),
|
||||||
|
CategoryData(
|
||||||
|
id: 5,
|
||||||
|
label: 'زیست محیطی',
|
||||||
|
asset: Assets.enviromentalCategoryIcon,
|
||||||
|
),
|
||||||
|
CategoryData(
|
||||||
|
id: 6,
|
||||||
|
label: 'اجتماعی',
|
||||||
|
asset: Assets.socialCategoryIcon,
|
||||||
|
),
|
||||||
|
CategoryData(
|
||||||
|
id: 1,
|
||||||
|
label: 'اقتصادی',
|
||||||
|
asset: Assets.economicCategoryIcon,
|
||||||
|
),
|
||||||
|
CategoryData(
|
||||||
|
id: 2,
|
||||||
|
label: 'سیاسی',
|
||||||
|
asset: Assets.politicalCategoryIcon,
|
||||||
|
),
|
||||||
|
CategoryData(
|
||||||
|
id: 3,
|
||||||
|
label: 'فناوری',
|
||||||
|
asset: Assets.techCategoryIcon,
|
||||||
|
),
|
||||||
|
CategoryData(
|
||||||
|
id: 4,
|
||||||
|
label: 'کسب و کار',
|
||||||
|
asset: Assets.businessCategoryIcon,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
Future.delayed(Duration.zero, () {
|
||||||
|
_getMainPageContent();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import 'package:didvan/views/widgets/didvan/slider.dart';
|
||||||
|
import 'package:didvan/views/widgets/skeleton_image.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class HomePageBanner extends StatelessWidget {
|
||||||
|
final bool isFirst;
|
||||||
|
const HomePageBanner({super.key, required this.isFirst});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
// final state = context.read<HomePageState>();
|
||||||
|
final banners = [1, 2];
|
||||||
|
return DidvanSlider(
|
||||||
|
itemBuilder: (context, index, realIndex) => const Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 4),
|
||||||
|
child: SkeletonImage(
|
||||||
|
imageUrl: 'https://wallpapercave.com/fwp/wp12963122.jpg',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
itemCount: banners.length,
|
||||||
|
viewportFraction: 1,
|
||||||
|
enableIndicator: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
import 'package:didvan/constants/app_icons.dart';
|
||||||
|
import 'package:didvan/models/home_page_content/content.dart';
|
||||||
|
import 'package:didvan/views/widgets/didvan/card.dart';
|
||||||
|
import 'package:didvan/views/widgets/didvan/text.dart';
|
||||||
|
import 'package:didvan/views/widgets/skeleton_image.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class HomePageGeneralItem extends StatelessWidget {
|
||||||
|
final HomePageContentType content;
|
||||||
|
const HomePageGeneralItem({super.key, required this.content});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return DidvanCard(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
SkeletonImage(
|
||||||
|
imageUrl: content.image,
|
||||||
|
height: 124,
|
||||||
|
width: double.infinity,
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
DidvanText(
|
||||||
|
content.title,
|
||||||
|
style: Theme.of(context).textTheme.bodyLarge,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
children: content.subtitles
|
||||||
|
.map(
|
||||||
|
(e) => Row(
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
DidvanIcons.puzzle_light,
|
||||||
|
size: 16,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Expanded(
|
||||||
|
child: DidvanText(
|
||||||
|
e,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
import 'package:didvan/config/design_config.dart';
|
||||||
|
import 'package:didvan/config/theme_data.dart';
|
||||||
|
import 'package:didvan/views/home/home/home_page_state.dart';
|
||||||
|
import 'package:didvan/views/home/home/widgets/banner.dart';
|
||||||
|
import 'package:didvan/views/widgets/didvan/text.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class HomePageMainContent extends StatelessWidget {
|
||||||
|
const HomePageMainContent({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final state = context.read<HomePageState>();
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
const HomePageBanner(
|
||||||
|
isFirst: true,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Stack(
|
||||||
|
children: [
|
||||||
|
Positioned(
|
||||||
|
bottom: 13,
|
||||||
|
child: Container(
|
||||||
|
width: MediaQuery.of(context).size.width,
|
||||||
|
height: 2,
|
||||||
|
color: Theme.of(context).colorScheme.border,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Center(
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||||
|
color: Theme.of(context).colorScheme.background,
|
||||||
|
child: DidvanText(
|
||||||
|
'دیدوان در یک نگاه',
|
||||||
|
color: Theme.of(context).colorScheme.title,
|
||||||
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 20,
|
||||||
|
right: 20,
|
||||||
|
top: 16,
|
||||||
|
),
|
||||||
|
child: Wrap(
|
||||||
|
alignment: WrapAlignment.center,
|
||||||
|
children: state.categories
|
||||||
|
.map(
|
||||||
|
(e) => SizedBox(
|
||||||
|
width: (MediaQuery.of(context).size.width - 40) / 4,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: 56,
|
||||||
|
height: 56,
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: DesignConfig.lowBorderRadius,
|
||||||
|
border: Border.all(
|
||||||
|
color:
|
||||||
|
Theme.of(context).colorScheme.focusedBorder,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: SvgPicture.asset(e.asset!),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
DidvanText(
|
||||||
|
e.label,
|
||||||
|
color: Theme.of(context).colorScheme.title,
|
||||||
|
style: Theme.of(context).textTheme.labelSmall,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
import 'package:didvan/constants/app_icons.dart';
|
||||||
|
import 'package:didvan/models/home_page_content/content.dart';
|
||||||
|
import 'package:didvan/views/widgets/didvan/card.dart';
|
||||||
|
import 'package:didvan/views/widgets/didvan/text.dart';
|
||||||
|
import 'package:didvan/views/widgets/skeleton_image.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:persian_number_utility/persian_number_utility.dart';
|
||||||
|
|
||||||
|
class HomePageNewsItem extends StatelessWidget {
|
||||||
|
final HomePageContentType content;
|
||||||
|
const HomePageNewsItem({super.key, required this.content});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return DidvanCard(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
SkeletonImage(
|
||||||
|
imageUrl: content.image,
|
||||||
|
width: double.infinity,
|
||||||
|
height: 160,
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
DidvanText(
|
||||||
|
content.title,
|
||||||
|
style: Theme.of(context).textTheme.bodyLarge,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
DidvanIcons.calendar_day_light,
|
||||||
|
size: 16,
|
||||||
|
),
|
||||||
|
DidvanText(
|
||||||
|
DateTime.parse(content.subtitles[0]).toPersianDateStr(),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,105 @@
|
||||||
|
import 'package:didvan/constants/app_icons.dart';
|
||||||
|
import 'package:didvan/models/home_page_content/content.dart';
|
||||||
|
import 'package:didvan/models/studio_details_data.dart';
|
||||||
|
import 'package:didvan/views/home/direct/widgets/audio_widget.dart';
|
||||||
|
import 'package:didvan/views/widgets/didvan/card.dart';
|
||||||
|
import 'package:didvan/views/widgets/didvan/text.dart';
|
||||||
|
import 'package:didvan/views/widgets/skeleton_image.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:persian_number_utility/persian_number_utility.dart';
|
||||||
|
|
||||||
|
class HomePagePodcastItem extends StatelessWidget {
|
||||||
|
final HomePageContentType content;
|
||||||
|
const HomePagePodcastItem({super.key, required this.content});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
const SizedBox(
|
||||||
|
height: 180,
|
||||||
|
width: double.infinity,
|
||||||
|
),
|
||||||
|
Positioned.fill(
|
||||||
|
child: Center(
|
||||||
|
child: DidvanCard(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: MediaQuery.of(context).size.width / 3,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: MediaQuery.of(context).size.width * 2 / 3 - 90,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: MediaQuery.of(context).size.width * 2 / 3 - 90,
|
||||||
|
child: AudioWidget(
|
||||||
|
id: content.id,
|
||||||
|
audioUrl: content.link,
|
||||||
|
audioMetaData: StudioDetailsData(
|
||||||
|
id: content.id,
|
||||||
|
duration: content.duration!,
|
||||||
|
title: content.title,
|
||||||
|
description: '',
|
||||||
|
image: content.image,
|
||||||
|
link: content.link,
|
||||||
|
iframe: null,
|
||||||
|
createdAt: '',
|
||||||
|
order: 1,
|
||||||
|
marked: content.marked,
|
||||||
|
comments: 0,
|
||||||
|
tags: [],
|
||||||
|
type: 'podcast',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
DidvanText(
|
||||||
|
content.title,
|
||||||
|
style: Theme.of(context).textTheme.bodyLarge,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
DidvanIcons.calendar_day_light,
|
||||||
|
size: 16,
|
||||||
|
),
|
||||||
|
DidvanText(
|
||||||
|
DateTime.parse(content.subtitles[0])
|
||||||
|
.toPersianDateStr(),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style:
|
||||||
|
Theme.of(context).textTheme.bodySmall,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SkeletonImage(
|
||||||
|
width: MediaQuery.of(context).size.width / 3,
|
||||||
|
height: 180,
|
||||||
|
imageUrl: content.image,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
import 'package:didvan/constants/app_icons.dart';
|
||||||
|
import 'package:didvan/models/home_page_content/content.dart';
|
||||||
|
import 'package:didvan/utils/date_time.dart';
|
||||||
|
import 'package:didvan/views/home/home/home_page_state.dart';
|
||||||
|
import 'package:didvan/views/widgets/didvan/card.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/svg.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class HomePageRadarItem extends StatelessWidget {
|
||||||
|
final HomePageContentType content;
|
||||||
|
const HomePageRadarItem({super.key, required this.content});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return DidvanCard(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
SkeletonImage(
|
||||||
|
imageUrl: content.image,
|
||||||
|
width: double.infinity,
|
||||||
|
height: 76,
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
DidvanText(
|
||||||
|
content.title,
|
||||||
|
style: Theme.of(context).textTheme.bodyLarge,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
DidvanIcons.calendar_day_light,
|
||||||
|
size: 16,
|
||||||
|
),
|
||||||
|
DidvanText(
|
||||||
|
DateTimeUtils.momentGenerator(
|
||||||
|
content.subtitles[0]),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
DidvanIcons.timer_light,
|
||||||
|
size: 16,
|
||||||
|
),
|
||||||
|
DidvanText(
|
||||||
|
'${content.subtitles[1]} دقیقه',
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Center(
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.only(
|
||||||
|
right: MediaQuery.of(context).size.width * 0.4,
|
||||||
|
),
|
||||||
|
width: 36,
|
||||||
|
height: 36,
|
||||||
|
padding: const EdgeInsets.all(4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
),
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
context
|
||||||
|
.read<HomePageState>()
|
||||||
|
.categories
|
||||||
|
.firstWhere((element) =>
|
||||||
|
element.id.toString() == content.subtitles[2])
|
||||||
|
.asset ??
|
||||||
|
'',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
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/home_page_content/content.dart';
|
||||||
|
import 'package:didvan/views/widgets/didvan/card.dart';
|
||||||
|
import 'package:didvan/views/widgets/didvan/text.dart';
|
||||||
|
import 'package:didvan/views/widgets/skeleton_image.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:persian_number_utility/persian_number_utility.dart';
|
||||||
|
|
||||||
|
class HomePageVideocastItem extends StatelessWidget {
|
||||||
|
final HomePageContentType content;
|
||||||
|
const HomePageVideocastItem({super.key, required this.content});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return DidvanCard(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
SkeletonImage(
|
||||||
|
width: double.infinity,
|
||||||
|
height: double.infinity,
|
||||||
|
imageUrl: content.image,
|
||||||
|
),
|
||||||
|
Center(
|
||||||
|
child: Container(
|
||||||
|
height: 36,
|
||||||
|
width: 36,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: Theme.of(context).colorScheme.secondary.withOpacity(0.7),
|
||||||
|
),
|
||||||
|
child: Icon(
|
||||||
|
DidvanIcons.play_solid,
|
||||||
|
color: Theme.of(context).colorScheme.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
bottom: 0,
|
||||||
|
right: 0,
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: const BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(12),
|
||||||
|
bottomRight: Radius.circular(12),
|
||||||
|
),
|
||||||
|
boxShadow: DesignConfig.defaultShadow,
|
||||||
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
),
|
||||||
|
width: MediaQuery.of(context).size.width / 3,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
DidvanText(
|
||||||
|
content.title,
|
||||||
|
style: Theme.of(context).textTheme.bodyLarge,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
DidvanIcons.calendar_day_light,
|
||||||
|
size: 16,
|
||||||
|
),
|
||||||
|
DidvanText(
|
||||||
|
DateTime.parse(content.subtitles[0])
|
||||||
|
.toPersianDateStr(),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import 'package:didvan/providers/core.dart';
|
import 'package:didvan/providers/core.dart';
|
||||||
|
|
||||||
class HomeState extends CoreProvier {
|
class HomeState extends CoreProvier {
|
||||||
int _currentPageIndex = 2;
|
int _currentPageIndex = 0;
|
||||||
|
|
||||||
set currentPageIndex(int value) {
|
set currentPageIndex(int value) {
|
||||||
_currentPageIndex = value;
|
_currentPageIndex = value;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import 'package:didvan/views/home/statistic/statistic_state.dart';
|
||||||
import 'package:didvan/views/home/statistic/widgets/statistic_overview.dart';
|
import 'package:didvan/views/home/statistic/widgets/statistic_overview.dart';
|
||||||
import 'package:didvan/views/home/widgets/categories_gird.dart';
|
import 'package:didvan/views/home/widgets/categories_gird.dart';
|
||||||
import 'package:didvan/views/home/widgets/categories_list.dart';
|
import 'package:didvan/views/home/widgets/categories_list.dart';
|
||||||
import 'package:didvan/views/home/widgets/logo_app_bar.dart';
|
|
||||||
import 'package:didvan/views/widgets/animated_visibility.dart';
|
import 'package:didvan/views/widgets/animated_visibility.dart';
|
||||||
import 'package:didvan/views/widgets/didvan/divider.dart';
|
import 'package:didvan/views/widgets/didvan/divider.dart';
|
||||||
import 'package:didvan/views/widgets/didvan/text.dart';
|
import 'package:didvan/views/widgets/didvan/text.dart';
|
||||||
|
|
@ -57,10 +56,9 @@ class _StatisticState extends State<Statistic> {
|
||||||
: const ClampingScrollPhysics(),
|
: const ClampingScrollPhysics(),
|
||||||
controller: _scrollController,
|
controller: _scrollController,
|
||||||
slivers: [
|
slivers: [
|
||||||
const SliverToBoxAdapter(child: LogoAppBar()),
|
|
||||||
if (state.appState != AppState.failed)
|
if (state.appState != AppState.failed)
|
||||||
const SliverToBoxAdapter(
|
const SliverToBoxAdapter(
|
||||||
child: SizedBox(height: 180),
|
child: SizedBox(height: 120),
|
||||||
),
|
),
|
||||||
if (state.appState != AppState.failed &&
|
if (state.appState != AppState.failed &&
|
||||||
state.markedStatistics.isNotEmpty)
|
state.markedStatistics.isNotEmpty)
|
||||||
|
|
@ -135,7 +133,7 @@ class _StatisticState extends State<Statistic> {
|
||||||
onSelected: _onCategorySelected,
|
onSelected: _onCategorySelected,
|
||||||
categories: List.from(state.categories)..removeAt(0),
|
categories: List.from(state.categories)..removeAt(0),
|
||||||
isColapsed: state.isColapsed,
|
isColapsed: state.isColapsed,
|
||||||
topPadding: 144,
|
topPadding: 20,
|
||||||
rightPadding: 300,
|
rightPadding: 300,
|
||||||
),
|
),
|
||||||
if (state.appState != AppState.failed)
|
if (state.appState != AppState.failed)
|
||||||
|
|
@ -177,21 +175,20 @@ class _StatisticState extends State<Statistic> {
|
||||||
_isAnimating = true;
|
_isAnimating = true;
|
||||||
setState(() {});
|
setState(() {});
|
||||||
await _scrollController.animateTo(
|
await _scrollController.animateTo(
|
||||||
228,
|
60,
|
||||||
duration: DesignConfig.mediumAnimationDuration,
|
duration: DesignConfig.lowAnimationDuration,
|
||||||
curve: Curves.easeIn,
|
curve: Curves.easeIn,
|
||||||
);
|
);
|
||||||
_isAnimating = false;
|
_isAnimating = false;
|
||||||
setState(() {});
|
setState(() {});
|
||||||
} else if (position <
|
} else if (position < min(_scrollController.position.maxScrollExtent, 40) &&
|
||||||
min(_scrollController.position.maxScrollExtent, 228) &&
|
|
||||||
state.isColapsed) {
|
state.isColapsed) {
|
||||||
state.isScrolled = false;
|
state.isScrolled = false;
|
||||||
_isAnimating = true;
|
_isAnimating = true;
|
||||||
setState(() {});
|
setState(() {});
|
||||||
await _scrollController.animateTo(
|
await _scrollController.animateTo(
|
||||||
0,
|
0,
|
||||||
duration: DesignConfig.mediumAnimationDuration,
|
duration: DesignConfig.lowAnimationDuration,
|
||||||
curve: Curves.easeIn,
|
curve: Curves.easeIn,
|
||||||
);
|
);
|
||||||
_isAnimating = false;
|
_isAnimating = false;
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,8 @@ class AudioSlider extends StatelessWidget {
|
||||||
: Theme.of(context).colorScheme.primary,
|
: Theme.of(context).colorScheme.primary,
|
||||||
baseBarColor: Theme.of(context).colorScheme.border,
|
baseBarColor: Theme.of(context).colorScheme.border,
|
||||||
bufferedBarColor: Theme.of(context).colorScheme.splash,
|
bufferedBarColor: Theme.of(context).colorScheme.splash,
|
||||||
total: MediaService.duration ?? Duration(seconds: duration ?? 0),
|
total: Duration(
|
||||||
|
seconds: duration ?? MediaService.duration?.inSeconds ?? 0),
|
||||||
progress: snapshot.data ?? Duration.zero,
|
progress: snapshot.data ?? Duration.zero,
|
||||||
thumbRadius: disableThumb ? 0 : 6,
|
thumbRadius: disableThumb ? 0 : 6,
|
||||||
barHeight: 3,
|
barHeight: 3,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,26 @@
|
||||||
|
import 'package:assets_audio_player/assets_audio_player.dart';
|
||||||
|
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/enums.dart';
|
||||||
|
import 'package:didvan/models/requests/radar.dart';
|
||||||
import 'package:didvan/models/view/app_bar_data.dart';
|
import 'package:didvan/models/view/app_bar_data.dart';
|
||||||
|
import 'package:didvan/routes/routes.dart';
|
||||||
|
import 'package:didvan/services/media/media.dart';
|
||||||
|
import 'package:didvan/utils/action_sheet.dart';
|
||||||
|
import 'package:didvan/views/home/widgets/audio/audio_player_widget.dart';
|
||||||
|
import 'package:didvan/views/home/widgets/audio/audio_slider.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/widgets/studio_details_widget.dart';
|
||||||
import 'package:didvan/views/widgets/didvan/app_bar.dart';
|
import 'package:didvan/views/widgets/didvan/app_bar.dart';
|
||||||
|
import 'package:didvan/views/widgets/didvan/icon_button.dart';
|
||||||
|
import 'package:didvan/views/widgets/didvan/text.dart';
|
||||||
|
import 'package:didvan/views/widgets/skeleton_image.dart';
|
||||||
|
import 'package:expandable_bottom_sheet/expandable_bottom_sheet.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_spinkit/flutter_spinkit.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class DidvanScaffold extends StatefulWidget {
|
class DidvanScaffold extends StatefulWidget {
|
||||||
final List<Widget>? slivers;
|
final List<Widget>? slivers;
|
||||||
|
|
@ -42,12 +62,18 @@ class _DidvanScaffoldState extends State<DidvanScaffold> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final double statusBarHeight = MediaQuery.of(context).padding.top;
|
final double statusBarHeight = MediaQuery.of(context).padding.top;
|
||||||
|
final double systemNavigationBarHeight =
|
||||||
|
MediaQuery.of(context).padding.bottom;
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: widget.backgroundColor,
|
backgroundColor: widget.backgroundColor,
|
||||||
body: Padding(
|
body: Padding(
|
||||||
padding: widget.appBarData == null
|
padding: widget.appBarData == null
|
||||||
? EdgeInsets.zero
|
? EdgeInsets.zero
|
||||||
: EdgeInsets.only(top: statusBarHeight),
|
: EdgeInsets.only(top: statusBarHeight),
|
||||||
|
child: SizedBox(
|
||||||
|
height: MediaQuery.of(context).size.height -
|
||||||
|
statusBarHeight -
|
||||||
|
systemNavigationBarHeight,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
CustomScrollView(
|
CustomScrollView(
|
||||||
|
|
@ -110,9 +136,16 @@ class _DidvanScaffoldState extends State<DidvanScaffold> {
|
||||||
appBarData: widget.appBarData!,
|
appBarData: widget.appBarData!,
|
||||||
scrollController: _scrollController,
|
scrollController: _scrollController,
|
||||||
),
|
),
|
||||||
|
const Positioned(
|
||||||
|
bottom: 20,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
child: _PlayerNavBar(),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -160,3 +193,272 @@ class __AppBarState extends State<_AppBar> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _PlayerNavBar extends StatelessWidget {
|
||||||
|
const _PlayerNavBar({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
bool _enablePlayerController(StudioDetailsState state) =>
|
||||||
|
MediaService.currentPodcast != null ||
|
||||||
|
(MediaService.audioPlayerTag?.contains('podcast') ?? false);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return StreamBuilder<bool>(
|
||||||
|
stream: MediaService.audioPlayer.isPlaying,
|
||||||
|
builder: (context, snapshot) => GestureDetector(
|
||||||
|
onTap: () => MediaService.currentPodcast == null ||
|
||||||
|
MediaService.currentPodcast?.description == 'radar'
|
||||||
|
? Navigator.of(context).pushNamed(
|
||||||
|
Routes.radarDetails,
|
||||||
|
arguments: {
|
||||||
|
'onMarkChanged': (id, value) {},
|
||||||
|
'onCommentsChanged': (id, value) {},
|
||||||
|
'id': MediaService.currentPodcast?.id,
|
||||||
|
'args': const RadarRequestArgs(page: 0),
|
||||||
|
'hasUnmarkConfirmation': false,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: _showPlayerBottomSheet(context),
|
||||||
|
child: Consumer<StudioDetailsState>(
|
||||||
|
builder: (context, state, child) => AnimatedContainer(
|
||||||
|
padding: const EdgeInsets.only(top: 12),
|
||||||
|
duration: DesignConfig.lowAnimationDuration,
|
||||||
|
height: _enablePlayerController(state) ? 60 : 0,
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: DesignConfig.isDark
|
||||||
|
? Theme.of(context).colorScheme.focused
|
||||||
|
: Theme.of(context).colorScheme.navigation,
|
||||||
|
borderRadius: BorderRadius.circular(200),
|
||||||
|
),
|
||||||
|
alignment: Alignment.topCenter,
|
||||||
|
child: Builder(builder: (context) {
|
||||||
|
if (!_enablePlayerController(state)) return const SizedBox();
|
||||||
|
if (state.appState == AppState.failed) {
|
||||||
|
Future.delayed(const Duration(seconds: 2), () {
|
||||||
|
MediaService.resetAudioPlayer();
|
||||||
|
});
|
||||||
|
return DidvanText(
|
||||||
|
'اتصال اینترنت برقرار نمیباشد',
|
||||||
|
color: DesignConfig.isDark
|
||||||
|
? Theme.of(context).colorScheme.title
|
||||||
|
: Theme.of(context).colorScheme.secondCTA,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (MediaService.currentPodcast == null) {
|
||||||
|
return SizedBox(
|
||||||
|
height: 32,
|
||||||
|
child: Center(
|
||||||
|
child: SpinKitThreeBounce(
|
||||||
|
size: 18,
|
||||||
|
color: DesignConfig.isDark
|
||||||
|
? Theme.of(context).colorScheme.title
|
||||||
|
: Theme.of(context).colorScheme.secondCTA,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return SizedBox(
|
||||||
|
height: 56,
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
right: 12,
|
||||||
|
left: 8,
|
||||||
|
),
|
||||||
|
child: DidvanIconButton(
|
||||||
|
icon: DidvanIcons.close_regular,
|
||||||
|
color: DesignConfig.isDark
|
||||||
|
? null
|
||||||
|
: Theme.of(context).colorScheme.secondCTA,
|
||||||
|
gestureSize: 32,
|
||||||
|
onPressed: MediaService.resetAudioPlayer,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SkeletonImage(
|
||||||
|
imageUrl: MediaService.currentPodcast!.image,
|
||||||
|
width: 32,
|
||||||
|
height: 32,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
DidvanText(
|
||||||
|
MediaService.currentPodcast!.title,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
color: DesignConfig.isDark
|
||||||
|
? null
|
||||||
|
: Theme.of(context).colorScheme.secondCTA,
|
||||||
|
),
|
||||||
|
AudioSlider(
|
||||||
|
disableThumb: true,
|
||||||
|
tag: MediaService.audioPlayerTag!,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
StreamBuilder<PlayingAudio?>(
|
||||||
|
stream: MediaService.audioPlayer.onReadyToPlay,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.data == null ||
|
||||||
|
state.appState == AppState.busy &&
|
||||||
|
MediaService.currentPodcast?.description !=
|
||||||
|
'radar') {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
top: 4,
|
||||||
|
left: 16,
|
||||||
|
right: 16,
|
||||||
|
),
|
||||||
|
child: SizedBox(
|
||||||
|
height: 18,
|
||||||
|
width: 18,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
strokeWidth: 2,
|
||||||
|
color: DesignConfig.isDark
|
||||||
|
? Theme.of(context).colorScheme.title
|
||||||
|
: Theme.of(context).colorScheme.secondCTA,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return const SizedBox();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (state.appState != AppState.busy &&
|
||||||
|
snapshot.data != null ||
|
||||||
|
MediaService.currentPodcast?.description == 'radar')
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 12,
|
||||||
|
right: 12,
|
||||||
|
),
|
||||||
|
child: DidvanIconButton(
|
||||||
|
gestureSize: 32,
|
||||||
|
color: DesignConfig.isDark
|
||||||
|
? null
|
||||||
|
: Theme.of(context).colorScheme.secondCTA,
|
||||||
|
icon: snapshot.data!
|
||||||
|
? DidvanIcons.pause_solid
|
||||||
|
: DidvanIcons.play_solid,
|
||||||
|
onPressed: () {
|
||||||
|
if (state.args?.type == 'video') {
|
||||||
|
state.getStudioDetails(
|
||||||
|
MediaService.currentPodcast!.id,
|
||||||
|
args: state.podcastArgs,
|
||||||
|
fetchOnly: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
MediaService.handleAudioPlayback(
|
||||||
|
audioSource: MediaService.currentPodcast!.link,
|
||||||
|
id: MediaService.currentPodcast!.id,
|
||||||
|
isVoiceMessage: false,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showPlayerBottomSheet(BuildContext context) {
|
||||||
|
final sheetKey = GlobalKey<ExpandableBottomSheetState>();
|
||||||
|
bool isExpanded = false;
|
||||||
|
final detailsState = context.read<StudioDetailsState>();
|
||||||
|
if (detailsState.args?.type == 'video') {
|
||||||
|
detailsState.getStudioDetails(
|
||||||
|
MediaService.currentPodcast!.id,
|
||||||
|
args: detailsState.podcastArgs,
|
||||||
|
fetchOnly: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
final state = context.read<PodcastsState>();
|
||||||
|
showModalBottomSheet(
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
maxWidth: ActionSheetUtils.mediaQueryData.size.width,
|
||||||
|
),
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
builder: (context) => ChangeNotifierProvider<PodcastsState>.value(
|
||||||
|
value: state,
|
||||||
|
child: Consumer<StudioDetailsState>(
|
||||||
|
builder: (context, state, child) => MediaQuery(
|
||||||
|
data: ActionSheetUtils.mediaQueryData,
|
||||||
|
child: ExpandableBottomSheet(
|
||||||
|
key: sheetKey,
|
||||||
|
background: Align(
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
child: Container(
|
||||||
|
height: MediaQuery.of(context).size.height * 0.7,
|
||||||
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
persistentHeader: GestureDetector(
|
||||||
|
onVerticalDragUpdate: (details) {
|
||||||
|
if (details.delta.dy > 10) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
AudioPlayerWidget(
|
||||||
|
podcast: MediaService.currentPodcast!,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: MediaQuery.of(context).size.width,
|
||||||
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
DidvanIconButton(
|
||||||
|
size: 32,
|
||||||
|
icon: DidvanIcons.angle_up_regular,
|
||||||
|
onPressed: () {
|
||||||
|
if (!isExpanded) {
|
||||||
|
sheetKey.currentState?.expand();
|
||||||
|
isExpanded = true;
|
||||||
|
} else {
|
||||||
|
isExpanded = false;
|
||||||
|
sheetKey.currentState?.contract();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
expandableContent: state.appState == AppState.busy
|
||||||
|
? Container(
|
||||||
|
height: MediaQuery.of(context).size.height / 2,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: SpinKitSpinningLines(
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: StudioDetailsWidget(
|
||||||
|
onMarkChanged: (id, value) => context
|
||||||
|
.read<PodcastsState>()
|
||||||
|
.changeMark(id, value, true),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
import 'package:carousel_slider/carousel_slider.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class DidvanSlider extends StatefulWidget {
|
||||||
|
final Widget Function(BuildContext context, int index, int realIndex)
|
||||||
|
itemBuilder;
|
||||||
|
final int itemCount;
|
||||||
|
final double viewportFraction;
|
||||||
|
final bool? enableIndicator;
|
||||||
|
final double? height;
|
||||||
|
final void Function(int, CarouselPageChangedReason)? onPageChanged;
|
||||||
|
const DidvanSlider({
|
||||||
|
super.key,
|
||||||
|
required this.itemBuilder,
|
||||||
|
required this.itemCount,
|
||||||
|
required this.viewportFraction,
|
||||||
|
this.onPageChanged,
|
||||||
|
this.enableIndicator,
|
||||||
|
this.height,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<DidvanSlider> createState() => _DidvanSliderState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DidvanSliderState extends State<DidvanSlider> {
|
||||||
|
int _currentIndex = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
CarouselSlider.builder(
|
||||||
|
itemCount: widget.itemCount,
|
||||||
|
itemBuilder: widget.itemBuilder,
|
||||||
|
options: CarouselOptions(
|
||||||
|
height: widget.height,
|
||||||
|
autoPlay: true,
|
||||||
|
padEnds: false,
|
||||||
|
viewportFraction: widget.viewportFraction,
|
||||||
|
onPageChanged: (index, reason) {
|
||||||
|
widget.onPageChanged?.call(index, reason);
|
||||||
|
if (widget.enableIndicator == true) {
|
||||||
|
setState(() {
|
||||||
|
_currentIndex = index;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (widget.enableIndicator == true) ...[
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
_CarouselIndicator(
|
||||||
|
count: widget.itemCount,
|
||||||
|
currentIndex: _currentIndex,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CarouselIndicator extends StatelessWidget {
|
||||||
|
final int count;
|
||||||
|
final int currentIndex;
|
||||||
|
const _CarouselIndicator({required this.count, required this.currentIndex});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
Container(
|
||||||
|
width: 8,
|
||||||
|
height: 8,
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 2),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
border: Border.all(
|
||||||
|
width: 1,
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
color: currentIndex == i
|
||||||
|
? Theme.of(context).colorScheme.primary
|
||||||
|
: Colors.transparent,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -33,7 +33,9 @@ class SkeletonImage extends StatelessWidget {
|
||||||
httpHeaders: {'Authorization': 'Bearer ${RequestService.token}'},
|
httpHeaders: {'Authorization': 'Bearer ${RequestService.token}'},
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
imageUrl: RequestHelper.baseUrl + imageUrl,
|
imageUrl: imageUrl.startsWith('http')
|
||||||
|
? imageUrl
|
||||||
|
: RequestHelper.baseUrl + imageUrl,
|
||||||
placeholder: (context, _) => const ShimmerPlaceholder(),
|
placeholder: (context, _) => const ShimmerPlaceholder(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue