import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/main.dart'; import 'package:didvan/models/home_page_content/home_page_list.dart'; import 'package:didvan/models/home_page_content/swot.dart'; import 'package:didvan/routes/routes.dart'; import 'package:didvan/services/app_initalizer.dart'; import 'package:didvan/views/home/main/main_page_state.dart'; import 'package:didvan/views/home/main/widgets/podcast_item.dart'; import 'package:didvan/views/widgets/didvan/slider.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/state_handlers/state_handler.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:provider/provider.dart'; import 'package:didvan/services/network/request.dart'; import 'package:url_launcher/url_launcher_string.dart'; import 'package:didvan/views/home/main/widgets/swot_item_card.dart'; import 'package:flutter/foundation.dart' show kIsWeb, defaultTargetPlatform, kDebugMode; import 'package:universal_html/html.dart' as html; import 'package:didvan/views/widgets/home_app_bar.dart'; import 'package:didvan/views/widgets/skeleton_image.dart'; import 'package:persian_number_utility/persian_number_utility.dart'; import 'package:didvan/views/widgets/text_divider.dart'; 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 ExplorePage extends StatelessWidget { const ExplorePage({super.key}); @override Widget build(BuildContext context) { return Consumer( builder: (context, state, child) { return StateHandler( onRetry: () => context.read().init(), state: state, builder: (context, state) { final List pageContent = []; pageContent.add(const StaggeredEntry( index: 0, child: HomeAppBar( showSearchField: true, ), )); if (state.content != null && state.content!.lists.isNotEmpty) { final List lists = List.from(state.content!.lists); MainPageList? monthlyItem, trendItem, riskItem, startupItem, techItem; try { monthlyItem = lists.firstWhere((e) => e.type == 'monthly'); } catch (_) {} try { trendItem = lists.firstWhere((e) => e.type == 'trend'); } catch (_) {} try { riskItem = lists.firstWhere((e) => e.type == 'risk'); } catch (_) {} try { startupItem = lists.firstWhere((e) => e.type == 'startup'); } catch (_) {} try { techItem = lists.firstWhere((e) => e.type == 'technology'); } catch (_) {} lists.removeWhere((e) => e.type == 'monthly' || e.type == 'risk' || e.type == 'startup' || e.type == 'technology'); if (monthlyItem != null) { lists.insert(0, monthlyItem); } if (trendItem != null) { final newTrendIndex = lists.indexOf(trendItem); final itemsToInsert = []; if (riskItem != null) itemsToInsert.add(riskItem); if (startupItem != null) itemsToInsert.add(startupItem); if (techItem != null) itemsToInsert.add(techItem); if (newTrendIndex != -1) { lists.insertAll(newTrendIndex + 1, itemsToInsert); } } for (int i = 0; i < lists.length; i++) { final currentList = lists[i]; if (currentList.type == 'video' || currentList.type == 'podcast') { continue; } pageContent.add(StaggeredEntry( index: pageContent.length, child: MainPageSection( list: currentList, isLast: i == lists.length - 1, ), )); if (currentList.type == 'radar') { pageContent.add(StaggeredEntry( index: pageContent.length, child: const Column( children: [ SizedBox( height: 10, ), TextDivider(text: 'رادارهای استراتژیک'), ], ), )); } if (currentList.type == 'technology') { pageContent.add(StaggeredEntry( index: pageContent.length, child: SwotSection(swotItems: state.swotItems), )); if (lists.any((e) => e.type == 'survey')) { pageContent.add(StaggeredEntry( index: pageContent.length, child: const Column( children: [ SizedBox(height: 10), TextDivider(text: 'سامانه هم‌اندیشی آنلاین'), ], ), )); } } else if (currentList.type == 'startup' && techItem == null) { pageContent.add(StaggeredEntry( index: pageContent.length, child: SwotSection(swotItems: state.swotItems), )); if (lists.any((e) => e.type == 'survey')) { pageContent.add(StaggeredEntry( index: pageContent.length, child: const Column( children: [ SizedBox(height: 10), TextDivider(text: 'سامانه هم‌اندیشی آنلاین'), ], ), )); } } } } return ListView( padding: const EdgeInsets.only(bottom: 30), children: pageContent, ); }, ); }, ); } } class StaggeredEntry extends StatefulWidget { final Widget child; final int index; final Duration duration; final double verticalOffset; const StaggeredEntry({ super.key, required this.child, required this.index, this.duration = const Duration(milliseconds: 250), this.verticalOffset = 50.0, }); @override State createState() => _StaggeredEntryState(); } class _StaggeredEntryState extends State with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation _fadeAnimation; late Animation _slideAnimation; @override void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: widget.duration, ); _fadeAnimation = CurvedAnimation( parent: _controller, curve: Curves.easeOut, ); _slideAnimation = Tween( begin: Offset(0, widget.verticalOffset / 100), end: Offset.zero, ).animate(CurvedAnimation( parent: _controller, curve: Curves.easeOutCubic, )); final delay = (widget.index * 100).clamp(0, 1000); Future.delayed(Duration(milliseconds: delay), () { if (mounted) { _controller.forward(); } }); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return FadeTransition( opacity: _fadeAnimation, child: SlideTransition( position: _slideAnimation, child: widget.child, ), ); } } class SwotSection extends StatelessWidget { final List swotItems; final double? headerSize; final double? moreSize; const SwotSection({super.key, required this.swotItems, this.headerSize, this.moreSize}); @override Widget build(BuildContext context) { if (swotItems.isEmpty) { return const SizedBox.shrink(); } return Padding( padding: const EdgeInsets.all(0.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.only(right: 20, top: 30), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ SvgPicture.asset( "lib/assets/icons/Swot_New.svg", color: const Color.fromARGB(255, 0, 126, 167), ), const SizedBox(width: 5), DidvanText( "ماژول بایدها و نبایدها", style: headerSize == null ? Theme.of(context).textTheme.titleSmall : TextStyle( fontSize: headerSize, fontWeight: FontWeight.bold), color: DesignConfig.isDark ? const Color.fromARGB(255, 0, 125, 166) : const Color.fromARGB(255, 0, 89, 119), ), ], ), GestureDetector( onTap: () { AppInitializer.openWebLink( navigatorKey.currentContext!, 'http://opportunity-threat.didvan.com/?accessToken=${RequestService.token}', mode: LaunchMode.inAppWebView, ); }, child: Padding( padding: const EdgeInsets.only(left: 20), child: Row( children: [ DidvanText( "مشاهده همه", style: moreSize == null ? Theme.of(context).textTheme.titleSmall : TextStyle(fontSize: moreSize), color: Theme.of(context).colorScheme.primary, fontWeight: moreSize == null ? FontWeight.bold : FontWeight.normal, ), ], ), ), ), ], ), ), const SizedBox(height: 16), /// Swot Items Slider DidvanSlider( height: 200, itemCount: swotItems.length, viewportFraction: isAnyMobile() ? 0.80 : 0.75, itemBuilder: (context, index, realIndex) => Padding( padding: const EdgeInsets.symmetric(horizontal: 0.0), child: Padding( padding: const EdgeInsets.all(5.0), child: SwotItemCard(item: swotItems[index]), ), ), ), const SizedBox(height: 16), ], ), ); } } class MainPageSection extends StatelessWidget { final MainPageList list; final bool isLast; const MainPageSection({super.key, required this.list, required this.isLast}); void _moreHandler(BuildContext context) { if (list.type == 'monthly') { Navigator.of(context).pushNamed(Routes.monthlyList); return; } if (list.link.startsWith('http')) { AppInitializer.openWebLink( context, '${list.link}?accessToken=${RequestService.token}', mode: LaunchMode.inAppWebView, ); return; } Navigator.of(context).pushNamed(list.link); } String? _generateIcon() { switch (list.type) { case 'monthly': return 'lib/assets/icons/Monthly.svg'; case 'news': return 'lib/assets/icons/Donye_Foolad.svg'; case 'radar': return 'lib/assets/icons/Pouyesh_Ofogh_New.svg'; case 'video': return 'lib/assets/icons/video-play.svg'; case 'podcast': return 'lib/assets/icons/play-circle.svg'; case 'trend': return 'lib/assets/icons/Ravand.svg'; case 'technology': return 'lib/assets/icons/Technology.svg'; case 'risk': return 'lib/assets/icons/RiskRadar.svg'; case 'startup': return 'lib/assets/icons/Startup.svg'; case 'survey': return 'lib/assets/icons/Saha.svg'; default: return null; } } double _calculateSliderHeight() { switch (list.type) { case 'monthly': return 150; case 'news': return 225; case 'radar': return 225; case 'trend': return 150; case 'technology': return 230; case 'risk': return 225; case 'startup': return 145; case 'delphi': return 220; default: return 220; } } double _calculateFixedViewportFraction(BuildContext context) { final screenWidth = MediaQuery.of(context).size.width; double targetWidth; switch (list.type) { case 'monthly': targetWidth = 320.0; break; case 'news': targetWidth = 265.0; break; case 'radar': targetWidth = 310.0; break; case 'trend': targetWidth = 320.0; break; case 'technology': targetWidth = 250.0; break; case 'risk': targetWidth = 310.0; break; case 'startup': targetWidth = 330.0; break; case 'delphi': targetWidth = 250.0; break; default: targetWidth = 250.0; } return (targetWidth / screenWidth).clamp(0.1, 1.0); } @override Widget build(BuildContext context) { final iconPath = _generateIcon(); if (list.contents.isEmpty) { return const SizedBox(); } return Column( children: [ _buildSectionHeader(context, iconPath), _buildSectionSlider(context), ], ); } Padding _buildSectionHeader(BuildContext context, String? iconPath) { String headerText = list.header; if (list.type == 'news') { headerText = 'دنیای فولاد'; } if (list.type == 'radar') { headerText = 'پویش افق'; } if (list.type == 'survey') { headerText = 'سها'; } return Padding( padding: const EdgeInsets.only( left: 16, right: 16, bottom: 16, top: 28, ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ if (iconPath != null) SvgPicture.asset( iconPath, width: 24, height: 24, color: const Color.fromRGBO(0, 126, 167, 1), ), const SizedBox(width: 4), DidvanText( headerText, style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w500), color: DesignConfig.isDark ? const Color.fromARGB(255, 0, 125, 166) : const Color.fromARGB(255, 0, 89, 119), ), ], ), GestureDetector( onTap: () => _moreHandler(context), child: Row( children: [ DidvanText( "مشاهده همه", color: Theme.of(context).colorScheme.primary, fontWeight: FontWeight.bold, ), ], ), ) ], ), ); } Widget _buildSectionSlider(BuildContext context) { if (list.type == 'podcast') { return Padding( padding: const EdgeInsets.only(top: 28), child: Column( children: list.contents .map( (e) => Padding( padding: const EdgeInsets.only( bottom: 12, left: 28, right: 28, ), child: MainPagePodcastItem( content: e, ), ), ) .toList(), ), ); } return DidvanSlider( key: ValueKey('${list.type}_slider'), height: _calculateSliderHeight(), viewportFraction: _calculateFixedViewportFraction(context), itemCount: list.contents.length, itemBuilder: (context, index, realIndex) { Widget cardContent; final item = list.contents[index]; switch (list.type) { case 'monthly': cardContent = GestureDetector( key: ValueKey('monthly_$index'), onTap: () { // Pattern: https://api.didvan.app/uploads/monthly/{id}/file.pdf String pdfUrl = item.link.toString(); if (kDebugMode) { print('=== Monthly Item Debug ==='); print('ID: ${item.id}'); print('Title: ${item.title}'); print('Constructed PDF URL: $pdfUrl'); print('======================='); } Navigator.of(context).pushNamed( Routes.pdfViewer, arguments: { 'pdfUrl': pdfUrl, 'title': item.title, }, ); }, child: Padding( padding: const EdgeInsets.all(8.0), child: Container( margin: const EdgeInsets.symmetric(vertical: 3, horizontal: 5), decoration: BoxDecoration( color: DesignConfig.isDark ? const Color.fromARGB(255, 36, 36, 36) : Colors.white, borderRadius: BorderRadius.circular(16), border: Border.all( color: const Color.fromARGB(255, 184, 184, 184), ), ), child: ClipRRect( borderRadius: BorderRadius.circular(12), child: Row( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ SkeletonImage( imageUrl: item.image, aspectRatio: 1 / 1.3, borderRadius: const BorderRadius.only( topLeft: Radius.circular(0), topRight: Radius.circular(12), bottomLeft: Radius.circular(0), bottomRight: Radius.circular(12)), height: 120, ), Expanded( child: Padding( padding: const EdgeInsets.all(12.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Text( item.title, style: TextStyle( color: DesignConfig.isDark ? Colors.white : Colors.black, fontWeight: FontWeight.bold, fontSize: 15), maxLines: 1, overflow: TextOverflow.ellipsis, ), Row( mainAxisAlignment: MainAxisAlignment.start, children: [ SvgPicture.asset( 'lib/assets/icons/calendar.svg'), const SizedBox( width: 5, ), Expanded( child: Text( DateTime.parse(item.subtitles.last) .toPersianDateStr(), style: const TextStyle( color: Color.fromARGB( 255, 102, 102, 102), fontSize: 12), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), ], ), Row( children: [ SvgPicture.asset( 'lib/assets/icons/PDF_Page.svg'), const SizedBox(width: 6), Expanded( child: Text( '${item.subtitles.first.toPersianDigit()} صفحه', style: const TextStyle( color: Color.fromARGB( 255, 102, 102, 102), fontSize: 12, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), ], ), ], ), ), ), ], ), ), ), ), ); break; case 'news': cardContent = GestureDetector( key: ValueKey('news_$index'), onTap: () => context.read().navigationHandler( list.type, item.id, item.link, description: item.description, ), child: Padding( padding: const EdgeInsets.all(8.0), child: Container( margin: const EdgeInsets.symmetric(horizontal: 4), decoration: BoxDecoration( color: DesignConfig.isDark ? const Color.fromARGB(255, 36, 36, 36) : Colors.white, borderRadius: BorderRadius.circular(16), border: Border.all( color: const Color.fromARGB(255, 184, 184, 184), width: 1), ), clipBehavior: Clip.antiAlias, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ SkeletonImage( imageUrl: item.image, borderRadius: const BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), bottomLeft: Radius.circular(0), bottomRight: Radius.circular(0)), height: 120, ), Padding( padding: const EdgeInsets.fromLTRB(12, 20, 12, 10), child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( item.title, textAlign: TextAlign.start, style: TextStyle( color: DesignConfig.isDark ? Colors.white : Colors.black, fontWeight: FontWeight.bold), maxLines: 1, overflow: TextOverflow.ellipsis, ), const SizedBox( height: 15, ), Row( mainAxisAlignment: MainAxisAlignment.start, children: [ SvgPicture.asset( 'lib/assets/icons/calendar.svg'), const SizedBox( width: 5, ), Text( DateTime.parse(item.subtitles.first) .toPersianDateStr(), style: TextStyle( color: Theme.of(context).colorScheme.caption, fontSize: 12), ), ], ) ], ), ), ], ), ), ), ); break; case 'radar': cardContent = GestureDetector( key: ValueKey('radar_$index'), onTap: () => context.read().navigationHandler( list.type, item.id, item.link, description: item.description, ), child: Padding( padding: const EdgeInsets.all(8.0), child: Container( margin: const EdgeInsets.symmetric(horizontal: 4), decoration: BoxDecoration( color: DesignConfig.isDark ? const Color.fromARGB(255, 36, 36, 36) : Colors.white, borderRadius: BorderRadius.circular(20), border: Border.all( color: const Color.fromARGB(255, 184, 184, 184))), clipBehavior: Clip.antiAlias, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Padding( padding: const EdgeInsets.all(9.5), child: Column( children: [ Row( children: [ SvgPicture.asset( 'lib/assets/icons/Pouyesh Ofogh.svg'), const SizedBox( width: 5, ), Expanded( child: Text( item.title, textAlign: TextAlign.start, style: TextStyle( color: DesignConfig.isDark ? Colors.white : Colors.black, fontWeight: FontWeight.bold), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), ], ), const SizedBox( height: 12, ), Row( mainAxisAlignment: MainAxisAlignment.start, children: [ SvgPicture.asset( 'lib/assets/icons/calendar.svg'), const SizedBox( width: 5, ), Text( DateTime.parse(item.subtitles.first) .toPersianDateStr(), style: TextStyle( color: Theme.of(context).colorScheme.caption, fontSize: 12), ), ], ), ], ), ), const SizedBox( height: 5, ), SkeletonImage( imageUrl: item.image, height: 130, borderRadius: const BorderRadius.only( bottomLeft: Radius.circular(16), bottomRight: Radius.circular(16), topRight: Radius.circular(0), topLeft: Radius.circular(0)), ), ], ), ), ), ); break; case 'trend': cardContent = GestureDetector( key: ValueKey('trend_$index'), onTap: () { if (item.link.startsWith('http')) { AppInitializer.openWebLink( context, '${item.link}?accessToken=${RequestService.token}', mode: LaunchMode.inAppWebView, ); } else { context.read().navigationHandler( list.type, item.id, item.link, ); } }, child: Padding( padding: const EdgeInsets.all(8.0), child: Container( margin: const EdgeInsets.symmetric(horizontal: 4), decoration: BoxDecoration( color: DesignConfig.isDark ? const Color.fromARGB(255, 36, 36, 36) : Colors.white, borderRadius: BorderRadius.circular(16), border: Border.all( color: const Color.fromARGB(255, 184, 184, 184), width: 1), ), clipBehavior: Clip.antiAlias, child: Row( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Expanded( child: Padding( padding: const EdgeInsets.all(15.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ Text( item.title, textAlign: TextAlign.start, style: TextStyle( color: DesignConfig.isDark ? Colors.white : Colors.black, fontWeight: FontWeight.bold), maxLines: 3, overflow: TextOverflow.ellipsis, ), const SizedBox( height: 10, ), Row( children: [ SvgPicture.asset( 'lib/assets/icons/ion_extension-puzzle-outline.svg'), const SizedBox( width: 5, ), Text( item.subtitles.last, style: TextStyle( color: Theme.of(context) .colorScheme .caption), ), ], ) ], ), ), ), SizedBox( width: 150, child: SkeletonImage( imageUrl: item.image, height: 10, width: 150, borderRadius: const BorderRadius.only( topLeft: Radius.circular(16), bottomLeft: Radius.circular(16), topRight: Radius.circular(0), bottomRight: Radius.circular(0)), ), ), ], ), ), ), ); break; case 'technology': cardContent = GestureDetector( key: ValueKey('technology_$index'), onTap: () { if (item.link.startsWith('http')) { AppInitializer.openWebLink( context, '${item.link}?accessToken=${RequestService.token}', mode: LaunchMode.inAppWebView, ); } else { context.read().navigationHandler( list.type, item.id, item.link, ); } }, child: Padding( padding: const EdgeInsets.all(5.0), child: Container( margin: const EdgeInsets.symmetric(horizontal: 4), decoration: BoxDecoration( color: DesignConfig.isDark ? const Color.fromARGB(255, 36, 36, 36) : Colors.white, borderRadius: BorderRadius.circular(16), border: Border.all( color: const Color.fromARGB(255, 184, 184, 184), width: 1), ), clipBehavior: Clip.antiAlias, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ SkeletonImage( imageUrl: item.image, borderRadius: const BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), bottomLeft: Radius.circular(0), bottomRight: Radius.circular(0)), height: 110, ), Padding( padding: const EdgeInsets.fromLTRB(12, 20, 12, 10), child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( item.title, textAlign: TextAlign.start, style: TextStyle( color: DesignConfig.isDark ? Colors.white : Colors.black, fontWeight: FontWeight.bold), maxLines: 1, overflow: TextOverflow.ellipsis, ), const SizedBox( height: 10, ), Row( mainAxisAlignment: MainAxisAlignment.start, children: [ SvgPicture.asset( 'lib/assets/icons/ion_extension-puzzle-outline.svg'), const SizedBox( width: 5, ), Text(item.subtitles.first) ], ) ], ), ), ], ), ), ), ); break; case 'risk': cardContent = GestureDetector( key: ValueKey('risk_$index'), onTap: () { if (item.link.startsWith('http')) { AppInitializer.openWebLink( context, '${item.link}?accessToken=${RequestService.token}', mode: LaunchMode.inAppWebView, ); } else { context.read().navigationHandler( list.type, item.id, item.link, ); } }, child: Padding( padding: const EdgeInsets.all(8.0), child: Container( margin: const EdgeInsets.symmetric(horizontal: 4), decoration: BoxDecoration( color: DesignConfig.isDark ? const Color.fromARGB(255, 36, 36, 36) : Colors.white, borderRadius: BorderRadius.circular(20), border: Border.all( color: const Color.fromARGB(255, 184, 184, 184))), clipBehavior: Clip.antiAlias, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Padding( padding: const EdgeInsets.all(9.5), child: Column( children: [ Row( children: [ Expanded( child: Text( item.title, textAlign: TextAlign.start, style: TextStyle( color: DesignConfig.isDark ? Colors.white : Colors.black, fontWeight: FontWeight.bold), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), ], ), const SizedBox( height: 13, ), const Row( mainAxisAlignment: MainAxisAlignment.start, children: [ SizedBox( height: 19, ) ], ), ], ), ), const SizedBox( height: 5, ), SkeletonImage( imageUrl: item.image, height: 130, borderRadius: const BorderRadius.only( bottomLeft: Radius.circular(16), bottomRight: Radius.circular(16), topRight: Radius.circular(0), topLeft: Radius.circular(0)), ), ], ), ), )); break; case 'startup': cardContent = GestureDetector( key: ValueKey('startup_$index'), onTap: () { if (item.link.startsWith('http')) { AppInitializer.openWebLink( context, '${item.link}?accessToken=${RequestService.token}', mode: LaunchMode.inAppWebView, ); } else { context.read().navigationHandler( list.type, item.id, item.link, ); } }, child: Padding( padding: const EdgeInsets.all(8.0), child: Container( margin: const EdgeInsets.symmetric(horizontal: 4), decoration: BoxDecoration( color: DesignConfig.isDark ? const Color.fromARGB(255, 36, 36, 36) : Colors.white, borderRadius: BorderRadius.circular(16), border: Border.all( color: const Color.fromARGB(255, 184, 184, 184), width: 1), ), clipBehavior: Clip.antiAlias, child: Row( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ SizedBox( width: 150, child: SkeletonImage( imageUrl: item.image, height: 10, width: 150, borderRadius: const BorderRadius.only( topLeft: Radius.circular(0), bottomLeft: Radius.circular(0), topRight: Radius.circular(16), bottomRight: Radius.circular(16)), ), ), Expanded( child: Padding( padding: const EdgeInsets.all(15.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ Text( item.title, textAlign: TextAlign.start, style: TextStyle( color: DesignConfig.isDark ? Colors.white : Colors.black, fontWeight: FontWeight.bold), maxLines: 3, overflow: TextOverflow.ellipsis, ), const SizedBox( height: 10, ), Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ SvgPicture.asset( 'lib/assets/icons/location.svg'), const SizedBox( width: 5, ), Text( item.subtitles.first, style: const TextStyle( color: Color.fromARGB( 255, 102, 102, 102)), ), ], ), const SizedBox( height: 10, ), Container( decoration: BoxDecoration( color: DesignConfig.isDark ? const Color.fromARGB( 255, 188, 188, 188) : const Color.fromARGB( 255, 235, 235, 235), borderRadius: const BorderRadius.all( Radius.circular(8)), ), child: Padding( padding: const EdgeInsets.all(8.0), child: Text( item.subtitles.last, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle( color: Color.fromARGB( 255, 102, 102, 102), fontSize: 12), ), ), ) ], ) ], ), ), ), ], ), ), ), ); break; case 'delphi': cardContent = GestureDetector( key: ValueKey('delphi_$index'), onTap: () { if (item.link.startsWith('http')) { AppInitializer.openWebLink( context, '${item.link}?accessToken=${RequestService.token}', mode: LaunchMode.inAppWebView, ); } else { context.read().navigationHandler( list.type, item.id, item.link, ); } }, child: Padding( padding: const EdgeInsets.all(8.0), child: Container( margin: const EdgeInsets.symmetric(horizontal: 4), decoration: BoxDecoration( color: DesignConfig.isDark ? Colors.black : Colors.white, borderRadius: BorderRadius.circular(16), border: Border.all( color: const Color.fromARGB(255, 184, 184, 184), width: 1), ), clipBehavior: Clip.antiAlias, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ SkeletonImage( imageUrl: item.image, borderRadius: const BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), bottomLeft: Radius.circular(0), bottomRight: Radius.circular(0)), height: 135, ), Padding( padding: const EdgeInsets.fromLTRB(12, 15, 12, 15), child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( item.title, textAlign: TextAlign.start, style: TextStyle( color: DesignConfig.isDark ? Colors.white : Colors.black, fontWeight: FontWeight.bold), maxLines: 2, overflow: TextOverflow.ellipsis, ), ], ), ), ], ), ), ), ); break; default: cardContent = GestureDetector( key: ValueKey('survey_$index'), onTap: () => context.read().navigationHandler( list.type, item.id, item.link, ), child: Padding( padding: const EdgeInsets.all(8.0), child: Container( margin: const EdgeInsets.symmetric(horizontal: 0), decoration: BoxDecoration( color: const Color.fromARGB(255, 27, 60, 89), borderRadius: BorderRadius.circular(12), ), clipBehavior: Clip.antiAlias, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ SkeletonImage( imageUrl: item.image, height: 165, borderRadius: const BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12), bottomLeft: Radius.circular(0), bottomRight: Radius.circular(0)), ), Padding( padding: const EdgeInsets.all(8.0), child: Text( item.title, textAlign: TextAlign.center, style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), ], ), ), ), ); } return cardContent; }, ); } }