import 'package:didvan/config/theme_data.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/swot.dart'; import 'package:didvan/models/new_statistic/new_statistics_model.dart'; import 'package:didvan/providers/user.dart'; import 'package:didvan/routes/routes.dart'; import 'package:didvan/views/home/main/main_page_state.dart'; import 'package:didvan/views/home/main/widgets/main_content.dart'; import 'package:didvan/views/home/main/widgets/story_section.dart'; import 'package:didvan/views/home/main/widgets/simple_explore_card.dart'; import 'package:didvan/views/home/new_statistic/new_statistics_state.dart'; import 'package:didvan/views/widgets/didvan/card.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/state_handlers/state_handler.dart'; import 'package:didvan/views/widgets/carousel_3d.dart'; import 'package:didvan/views/home/home_state.dart'; import 'package:didvan/views/widgets/text_divider.dart'; import 'package:didvan/views/widgets/mini_chart.dart'; import 'package:didvan/views/widgets/home_app_bar.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:provider/provider.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:flutter/foundation.dart' show kIsWeb, defaultTargetPlatform; import 'package:universal_html/html.dart' as html; bool isAnyMobile() { if (kIsWeb) { final userAgent = html.window.navigator.userAgent.toLowerCase(); return userAgent.contains('mobile') || userAgent.contains('android') || userAgent.contains('ios'); } return defaultTargetPlatform == TargetPlatform.android || defaultTargetPlatform == TargetPlatform.iOS; } class MainPage extends StatefulWidget { const MainPage({ super.key, }); @override State createState() => _MainPageState(); } class _MainPageState extends State { @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { context.read().init(); context.read().init(); }); } @override Widget build(BuildContext context) { return StateHandler( onRetry: () => context.read().init(), state: context.watch(), builder: (context, state) { return Column( children: [ const HomeAppBar( showBackButton: false, showSearchField: true, ), Expanded( child: ListView( padding: const EdgeInsets.only(top: 0, bottom: 16), children: [ if (state.stories.isNotEmpty) ...[ const TextDivider(text: 'دیده‌بان') .animate() .fadeIn(delay: 400.ms, duration: 500.ms), const _DidvanSignalsTitle() .animate() .fadeIn(delay: 500.ms, duration: 500.ms), Padding( padding: const EdgeInsets.symmetric(horizontal: 8), child: StorySection(stories: state.stories), ).animate().fadeIn(delay: 600.ms, duration: 500.ms), ], const SizedBox(height: 12), const TextDivider(text: 'پیشخوان استراتژیک') .animate() .fadeIn(delay: 700.ms, duration: 500.ms), const Padding( padding: EdgeInsets.symmetric(horizontal: 16), child: MainPageMainContent(), ).animate().fadeIn(delay: 800.ms, duration: 500.ms), if (state.content != null && state.content!.lists.isNotEmpty) ...[ const _ExploreLatestTitle() .animate() .fadeIn(delay: 900.ms, duration: 500.ms), _ExploreLatestSlider( lists: state.content!.lists, swotItems: state.swotItems, ).animate().fadeIn(delay: 1000.ms, duration: 500.ms), ], const _IndustryPulseTitle() .animate() .fadeIn(delay: 1100.ms, duration: 500.ms), const _IndustryPulseCards() .animate() .fadeIn(delay: 1200.ms, duration: 500.ms), ], ), ), ], ); }, ); } } class _DidvanSignalsTitle extends StatelessWidget { const _DidvanSignalsTitle(); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.only( left: 16, right: 16, bottom: 16, top: 0, ), child: Row( children: [ SvgPicture.asset( 'lib/assets/icons/voice-square.svg', color: Theme.of(context).colorScheme.title, width: 30, height: 30, ), const SizedBox(width: 5), DidvanText( "سیگنال‌های دیدوان", style: Theme.of(context).textTheme.titleMedium, color: const Color.fromARGB(255, 0, 89, 119), fontSize: 13, ), ], ), ); } } class _ExploreLatestTitle extends StatelessWidget { const _ExploreLatestTitle(); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.only( left: 16, right: 16, bottom: 16, top: 0, ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ SvgPicture.asset( 'lib/assets/icons/discover.svg', color: Theme.of(context).colorScheme.title, width: 30, height: 30, ), const SizedBox(width: 5), DidvanText( "تازه‌ترین‌های کاوش", style: Theme.of(context).textTheme.titleMedium, color: const Color.fromARGB(255, 0, 89, 119), fontSize: 13, ), ], ), GestureDetector( onTap: () { context.read().tabController.animateTo(3); }, child: const Row( children: [ DidvanText( "مشاهده همه", color: Color.fromARGB(255, 0, 126, 167), fontWeight: FontWeight.normal, fontSize: 12, ), ], ), ) ], ), ); } } class _ExploreLatestSlider extends StatelessWidget { final List lists; final List swotItems; const _ExploreLatestSlider({required this.lists, required this.swotItems}); @override Widget build(BuildContext context) { final List items = []; for (var list in lists) { if (list.type == 'video' || list.type == 'podcast' || list.type == 'news' || list.type == 'radar') { continue; } if (list.contents.isNotEmpty) { final newestContent = list.contents.first; items.add( Padding( padding: const EdgeInsets.symmetric(horizontal: 4), child: SimpleExploreCard( content: newestContent, type: list.type, ), ), ); } } if (swotItems.isNotEmpty) { items.add( Padding( padding: const EdgeInsets.symmetric(horizontal: 4.0), child: SimpleExploreCard( swotItem: swotItems.first, ), ), ); } if (items.isEmpty) { return const SizedBox.shrink(); } return Carousel3D( items: items, height: 220, autoPlayDuration: const Duration(seconds: 5), showControls: true, onItemChanged: (index) { // Optional: Handle item change if needed }, ); } } class _IndustryPulseTitle extends StatelessWidget { const _IndustryPulseTitle(); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.only( left: 16, right: 16, bottom: 16, top: 16, ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ SvgPicture.asset( 'lib/assets/icons/chart 2.svg', color: Theme.of(context).colorScheme.title, width: 30, height: 30, ), const SizedBox(width: 5), DidvanText( "نبض صنعت", style: Theme.of(context).textTheme.titleMedium, color: const Color.fromARGB(255, 0, 89, 119), fontSize: 13, ), ], ), GestureDetector( onTap: () { context.read().tabController.animateTo(2); }, child: const Row( children: [ DidvanText( "مشاهده همه", color: Color.fromARGB(255, 0, 126, 167), fontWeight: FontWeight.normal, fontSize: 12, ), ], ), ) ], ), ); } } class _IndustryPulseCards extends StatefulWidget { const _IndustryPulseCards(); @override State<_IndustryPulseCards> createState() => _IndustryPulseCardsState(); } class _IndustryPulseCardsState extends State<_IndustryPulseCards> { late PageController _pageController; @override void initState() { super.initState(); _pageController = PageController(viewportFraction: 0.45); } @override void dispose() { _pageController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return StateHandler( state: context.watch(), placeholder: const Center(child: CircularProgressIndicator()), onRetry: () => context.read().init(), builder: (context, statisticState) { if (statisticState.contents.isEmpty) { return const SizedBox.shrink(); } final List allItems = []; statisticState.contents.forEach((category) { allItems.addAll(category.contents); }); final List desiredTitles = [ 'دلار', 'بیت کوین', 'نیکل', 'نفت خام' ]; final List itemsToShow = allItems .where((item) => desiredTitles.contains(item.title)) .toList(); if (itemsToShow.isEmpty) { return const SizedBox.shrink(); } return Padding( padding: const EdgeInsets.only(left: 16.0), child: Align( alignment: Alignment.centerLeft, child: SizedBox( height: 165, child: PageView.builder( padEnds: false, controller: _pageController, itemCount: itemsToShow.length, itemBuilder: (context, index) { return Padding( padding: const EdgeInsets.only(right: 8.0), child: AspectRatio( aspectRatio: 1, child: _IndustryPulseCard(statistic: itemsToShow[index]), ), ) .animate() .fadeIn(delay: (200 * index).ms, duration: 500.ms) .slideX( begin: -0.5, duration: 500.ms, curve: Curves.easeOut); }, ), ), ), ); }, ); } } class _IndustryPulseCard extends StatelessWidget { final Content statistic; const _IndustryPulseCard({required this.statistic}); Color _diffColor(BuildContext context) => statistic.data.dt == 'high' ? Theme.of(context).colorScheme.success : Theme.of(context).colorScheme.error; bool get _hasDiff => statistic.data.dp != 0; @override Widget build(BuildContext context) { final state = context.read(); return GestureDetector( onTap: () => Navigator.of(context).pushNamed(Routes.statisticDetails, arguments: { 'onMarkChanged': (value) => onMarkChanged(statistic.id, value), 'label': statistic.label, 'title': statistic.title, 'marked': statistic.marked, }).then( (value) => state.getStatistic(), ), child: DidvanCard( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 9), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 8.0), child: Row( children: [ Expanded( child: MiniChart( label: statistic.label, width: double.infinity, height: 38, lineColor: _hasDiff ? _diffColor(context) : Theme.of(context).colorScheme.primary, changePercent: statistic.data.dp.toDouble(), trend: statistic.data.dt, ), ), ], ), ), const SizedBox(height: 20), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ if (statistic.marked) Icon( Icons.star, color: Theme.of(context).colorScheme.yellow, size: 16, ), if (statistic.marked) const SizedBox(width: 4), DidvanText( statistic.title, style: Theme.of(context).textTheme.bodyMedium?.copyWith( fontWeight: FontWeight.w600, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ], ), const SizedBox(height: 6), DidvanText( statistic.data.p, style: Theme.of(context).textTheme.bodySmall?.copyWith( fontWeight: FontWeight.w500, ), ), if (_hasDiff) const SizedBox(height: 6), if (_hasDiff) Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), decoration: BoxDecoration( color: statistic.data.dt == 'high' ? const Color.fromRGBO(245, 255, 252, 1) : const Color.fromRGBO(255, 248, 248, 1), borderRadius: BorderRadius.circular(4), ), child: Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( statistic.data.dt == 'high' ? DidvanIcons.angle_up_regular : DidvanIcons.angle_down_regular, size: 16, color: _diffColor(context), ), const SizedBox(width: 4), DidvanText( '${statistic.data.dp}%', style: Theme.of(context).textTheme.bodySmall, color: _diffColor(context), ), ], ), ), ], ), ), ); } void onMarkChanged(int id, bool value) { UserProvider.changeStatisticMark(id, value); } }