// ignore_for_file: use_build_context_synchronously, deprecated_member_use import 'package:chewie/chewie.dart'; import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/assets.dart'; import 'package:didvan/models/enums.dart'; import 'package:didvan/models/studio_details_data.dart'; import 'package:didvan/providers/user.dart'; import 'package:didvan/routes/routes.dart'; import 'package:didvan/services/media/media.dart'; import 'package:didvan/views/comments/comments.dart'; import 'package:didvan/views/comments/comments_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/bookmark_button.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/overview/multitype.dart'; import 'package:didvan/views/widgets/state_handlers/state_handler.dart'; import 'package:didvan/views/widgets/tag_item.dart'; import 'package:didvan/views/widgets/video/primary_controls.dart'; import 'package:flutter/material.dart'; import 'package:flutter_html/flutter_html.dart'; import 'package:flutter_svg/svg.dart'; import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher_string.dart'; import 'package:video_player/video_player.dart'; class VideoDetailsPage extends StatefulWidget { final Map pageData; const VideoDetailsPage({Key? key, required this.pageData}) : super(key: key); @override State createState() => _VideoDetailsPageState(); } class _VideoDetailsPageState extends State with TickerProviderStateMixin { int _currentlyPlayingId = 0; VideoPlayerController? _videoPlayerController; ChewieController? _chewieController; bool _isDescriptionExpanded = false; final _focusNode = FocusNode(); late AnimationController _animationController; late Animation _fadeAnimation; late Animation _slideAnimation; final GlobalKey _relatedContentKey = GlobalKey(); final GlobalKey _commentsKey = GlobalKey(); @override void initState() { super.initState(); _animationController = AnimationController( vsync: this, duration: const Duration(milliseconds: 800), ); _fadeAnimation = Tween(begin: 0.0, end: 1.0).animate( CurvedAnimation( parent: _animationController, curve: Curves.easeIn, ), ); _slideAnimation = Tween(begin: const Offset(0, 0.2), end: Offset.zero).animate( CurvedAnimation( parent: _animationController, curve: Curves.easeOut, ), ); final state = context.read(); state.args = widget.pageData['args']; Future.delayed( Duration.zero, () => state.getStudioDetails(widget.pageData['id']).then((_) { if (mounted) { _initializePlayer(state.studio); Future.delayed(const Duration(milliseconds: 500), () { if (mounted) { state.getRelatedContents(); _animationController.forward(); } }); } }), ); } Future _initializePlayer(StudioDetailsData studio) async { if (studio.type == 'video') { _videoPlayerController?.dispose(); _chewieController?.dispose(); debugPrint("Playing video from URL: ${studio.link}"); _videoPlayerController = VideoPlayerController.network(studio.link); try { await _videoPlayerController!.initialize(); if (mounted) { setState(() { _chewieController = ChewieController( videoPlayerController: _videoPlayerController!, customControls: const PrimaryControls(), autoPlay: true, looping: true, aspectRatio: 16 / 9, materialProgressColors: ChewieProgressColors( playedColor: Theme.of(context).colorScheme.title, handleColor: Theme.of(context).colorScheme.title, ), ); _currentlyPlayingId = studio.id; }); } } catch (e) { debugPrint("Error initializing video player: $e"); } } } @override Widget build(BuildContext context) { return Consumer( builder: (context, state, child) { if (state.isStudioLoaded && _currentlyPlayingId != state.studio.id) { Future.microtask(() => _initializePlayer(state.studio)); } return StateHandler( state: state, onRetry: () { try { state.getStudioDetails(state.studio.id); } catch (e) { state.getStudioDetails(widget.pageData['id']); } }, builder: (context, state) { if (!state.isStudioLoaded) { return Scaffold( body: Center( child: Image.asset( Assets.loadingAnimation, width: 100, height: 100, ), ), ); } return WillPopScope( onWillPop: () async { if (MediaService.currentPodcast != null) { state.studio = MediaService.currentPodcast!; } state.handleTracking(id: state.studio.id); return true; }, child: Scaffold( backgroundColor: Theme.of(context).colorScheme.surface, appBar: PreferredSize( preferredSize: const Size.fromHeight(90.0), child: AppBar( scrolledUnderElevation: 0, surfaceTintColor: Colors.transparent, backgroundColor: Theme.of(context).colorScheme.surface, elevation: 0, automaticallyImplyLeading: false, flexibleSpace: SafeArea( child: Container( padding: const EdgeInsets.symmetric( horizontal: 16.0, vertical: 8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Center( child: SvgPicture.asset( 'lib/assets/images/logos/logo-horizontal-light.svg', height: 55, ), ), IconButton( icon: SvgPicture.asset( 'lib/assets/icons/arrow-left.svg', color: Theme.of(context).colorScheme.caption, height: 24, ), onPressed: () { Navigator.pop(context); }, ), ], ), ), ), )), body: SingleChildScrollView( child: FadeTransition( opacity: _fadeAnimation, child: SlideTransition( position: _slideAnimation, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Hero( tag: 'video-${state.studio.id}', child: AspectRatio( aspectRatio: 16 / 9, child: Stack( children: [ (_chewieController != null && _chewieController! .videoPlayerController .value .isInitialized) ? Chewie(controller: _chewieController!) : Center( child: Image.asset( Assets.loadingAnimation, width: 100, height: 100, ), ), Positioned( top: 1, left: 1, child: Row( children: [ Container( height: 36, decoration: BoxDecoration( color: Colors.black.withOpacity(0.6), shape: BoxShape.circle, ), child: IconButton( icon: SvgPicture.asset( 'lib/assets/icons/fluent_mention-32-regular.svg', width: 28, height: 28, ), onPressed: () => Navigator.of(context).pushNamed( Routes.mentions, arguments: { 'id': state.studio.id, 'type': 'studio', 'title': state.studio.title, }, ), ), ), BookmarkButton( value: state.studio.marked, onMarkChanged: (value) { if (widget.pageData[ 'onMarkChanged'] != null) { widget.pageData['onMarkChanged']( state.studio.id, value); } }, gestureSize: 35, type: 'video', itemId: state.studio.id, ), ], ), ), ], ), ), ), _buildDescriptionSection(state), _buildRelatedContentSection(state), _buildCommentsSection(state), ], ), ), ), ), ), ); }, ); }, ); } Widget _buildDescriptionSection(StudioDetailsState state) { return AnimatedSwitcher( duration: const Duration(milliseconds: 500), child: KeyedSubtree( key: ValueKey('description-${state.studio.id}'), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.all(8.0), child: Text( state.studio.title, style: TextStyle( fontSize: 17, fontWeight: FontWeight.bold, color: DesignConfig.isDark ? const Color.fromARGB(255, 0, 90, 119) : const Color.fromARGB(255, 0, 53, 70)), ), ), Container( decoration: BoxDecoration( borderRadius: BorderRadius.vertical( bottom: Radius.circular(_isDescriptionExpanded ? 0 : 16.0), ), boxShadow: _isDescriptionExpanded ? null : [ BoxShadow( color: Colors.black.withOpacity(0.1), blurRadius: 8.0, spreadRadius: -2.0, offset: const Offset(0, 6), ), ], ), child: Stack( alignment: Alignment.bottomCenter, children: [ AnimatedSize( duration: const Duration(milliseconds: 300), child: ClipRRect( borderRadius: BorderRadius.vertical( bottom: Radius.circular( _isDescriptionExpanded ? 0 : 16.0), top: Radius.circular( _isDescriptionExpanded ? 0 : 16.0), ), child: Container( color: Theme.of(context).colorScheme.surface, child: ConstrainedBox( constraints: BoxConstraints( maxHeight: _isDescriptionExpanded ? double.infinity : 150.0, ), child: Html( key: ValueKey(state.studio.id), data: state.studio.description, onAnchorTap: (href, _, __) => launchUrlString(href!), style: { '*': Style( direction: TextDirection.rtl, textAlign: TextAlign.right, lineHeight: LineHeight.percent(135), margin: const Margins(), padding: HtmlPaddings.zero, color: const Color.fromARGB( 255, 102, 102, 102), fontWeight: FontWeight.normal, ), }, ), ), ), ), ), Positioned( bottom: 0, left: 0, right: 0, child: AnimatedOpacity( duration: const Duration(milliseconds: 300), opacity: _isDescriptionExpanded ? 0.0 : 1.0, child: Container( height: 90, decoration: BoxDecoration( borderRadius: const BorderRadius.vertical( bottom: Radius.circular(16.0), top: Radius.circular(16.0), ), gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Theme.of(context) .colorScheme .surface .withOpacity(0.0), Theme.of(context).colorScheme.surface, ], stops: const [0.0, 0.9], ), ), ), ), ), ], ), ), Transform.translate( offset: const Offset(0, -14.0), child: InkWell( onTap: () { setState(() { _isDescriptionExpanded = !_isDescriptionExpanded; }); }, child: Padding( padding: EdgeInsets.zero, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( padding: const EdgeInsets.all(4.0), decoration: BoxDecoration( color: Theme.of(context).colorScheme.focused, shape: BoxShape.circle, ), child: SvgPicture.asset( _isDescriptionExpanded ? 'lib/assets/icons/arrow-up2.svg' : 'lib/assets/icons/arrow-down.svg', color: Theme.of(context).primaryColor, height: 25, ), ), ], ), ), ), ), if (state.studio.tags.isNotEmpty) const SizedBox(height: 16), if (state.studio.tags.isNotEmpty) Wrap( spacing: 8, runSpacing: 8, children: [ for (var i = 0; i < state.studio.tags.length; i++) TagItem( tag: state.studio.tags[i], onMarkChanged: (id, value) { if (widget.pageData['onMarkChanged'] != null) { widget.pageData['onMarkChanged'](id, value); } }, type: 'video', ), ], ), ], ), ), const SizedBox(height: 20), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const SizedBox(), if (state.nextStudio != null && state.alongSideState == AppState.idle) StudioPreview( isNext: true, studio: state.nextStudio!, ), if (state.alongSideState == AppState.busy) StudioPreview.placeHolder, if (state.prevStudio != null && state.alongSideState == AppState.idle) StudioPreview( isNext: false, studio: state.prevStudio!, ), if (state.alongSideState == AppState.busy) StudioPreview.placeHolder, const SizedBox(), ], ), ], ), ), ); } Widget _buildCommentsSection(StudioDetailsState state) { return ChangeNotifierProvider( create: (context) => CommentsState() ..itemId = state.studio.id ..type = 'studio' ..onCommentsChanged = state.onCommentsChanged ..getComments(), child: Padding( padding: const EdgeInsets.all(8.0), child: Container( width: double.infinity, margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Consumer( builder: (context, userProvider, child) { final user = userProvider.user; final hasProfileImage = user.photo != null && user.photo!.isNotEmpty; return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ // CircleAvatar( // radius: 20, // backgroundColor: DesignConfig.isDark // ? Colors.transparent // : Colors.white, // backgroundImage: // hasProfileImage ? NetworkImage(user.photo!) : null, // child: !hasProfileImage // ? Icon( // DidvanIcons.avatar_light, // size: 50, // color: DesignConfig.isDark // ? Colors.white // : Colors.black, // ) // : null, // ), // const SizedBox(width: 8), Expanded( child: CommentMessageBox(focusNode: _focusNode), ), ], ); }, ), SizedBox(height: state.studio.comments == 0 ? 0 : 16), SizedBox( width: double.infinity, child: state.studio.comments == 0 ? const SizedBox() : DidvanText( 'نظرات کاربران:', style: TextStyle( color: DesignConfig.isDark ? const Color.fromARGB(255, 0, 90, 119) : const Color.fromARGB(255, 0, 53, 70), fontSize: 18, fontWeight: FontWeight.bold, ), ), ), const SizedBox(height: 16), state.studio.comments == 0 ? const SizedBox() : SizedBox( height: 400, child: Comments( key: _commentsKey, pageData: const {'isPage': false}, ), ), ], ), ), ), ); } Widget _buildRelatedContentSection(StudioDetailsState state) { debugPrint("تعداد مطالب مرتبط: ${state.studio.relatedContents.length}"); debugPrint( "آیا لیست مطالب مرتبط خالی است؟ ${state.studio.relatedContentsIsEmpty}"); debugPrint("تعداد tags: ${state.studio.tags.length}"); return Container( width: double.infinity, margin: const EdgeInsets.all(8), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.all(8.0), child: Text( "مطالب مرتبط:", style: TextStyle( fontSize: 18, color: DesignConfig.isDark ? const Color.fromARGB(255, 0, 90, 119) : const Color.fromARGB(255, 0, 53, 70), fontWeight: FontWeight.bold), ), ), Padding( padding: const EdgeInsets.all(8), child: Builder( builder: (context) { if (state.studio.relatedContents.isNotEmpty) { return AnimatedList( key: _relatedContentKey, initialItemCount: state.studio.relatedContents.length, shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemBuilder: (context, index, animation) { final item = state.studio.relatedContents[index]; return FadeTransition( opacity: animation, child: SlideTransition( position: Tween( begin: const Offset(0, 0.2), end: Offset.zero, ).animate(animation), child: Padding( padding: const EdgeInsets.only(bottom: 8), child: InkWell( borderRadius: BorderRadius.circular(12), onTap: () { String routeName; Map arguments; if (item.type == 'video') { routeName = Routes.videoDetails; arguments = { 'id': item.id, 'type': item.type, }; } else if (item.type == 'podcast') { routeName = Routes.studioDetails; arguments = { 'id': item.id, 'type': item.type, }; } else if (item.type == 'news') { routeName = Routes.newsDetails; arguments = { 'id': item.id, }; } else if (item.type == 'radar') { routeName = Routes.radarDetails; arguments = { 'id': item.id, }; } else { routeName = Routes.studioDetails; arguments = { 'id': item.id, 'type': item.type, }; } Navigator.pushNamed( context, routeName, arguments: arguments, ); }, child: MultitypeOverview( item: item, onMarkChanged: (id, value) { if (widget.pageData['onMarkChanged'] != null) { widget.pageData['onMarkChanged'](id, value); } }, ), ), ), ), ); }, ); } else if (state.studio.relatedContentsIsEmpty) { return const Padding( padding: EdgeInsets.all(32.0), child: Center( child: DidvanText( 'مطالب مرتبطی یافت نشد', style: TextStyle( color: Colors.grey, fontSize: 16, ), ), ), ); } else { return Column( children: [ const Padding( padding: EdgeInsets.all(8.0), child: Text( 'در حال بارگذاری مطالب مرتبط...', style: TextStyle( fontSize: 12, color: Colors.grey, ), ), ), ...List.generate( 3, (index) => Padding( padding: const EdgeInsets.only(bottom: 8), child: MultitypeOverview.placeholder, )), ], ); } }, ), ), ], ), ); } @override void dispose() { _animationController.dispose(); _videoPlayerController?.dispose(); _chewieController?.dispose(); _focusNode.dispose(); super.dispose(); } }