251 lines
9.9 KiB
Dart
251 lines
9.9 KiB
Dart
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: [
|
|
// Top Banner - only show on first section
|
|
if (widget.isFirst && state.topBanner != null) ...[
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 5),
|
|
child: ClipRRect(
|
|
borderRadius: BorderRadius.circular(16),
|
|
child: SkeletonImage(
|
|
imageUrl: state.topBanner!.image,
|
|
borderRadius: BorderRadius.circular(16),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 12),
|
|
],
|
|
|
|
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,
|
|
),
|
|
],
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|