import 'dart:async'; import 'dart:io'; import 'package:better_player_plus/better_player_plus.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:go_router/go_router.dart'; import 'package:news_app/ui/video/video_detail_page.dart'; import '../../constant/config.dart'; import '../../gen/assets.gen.dart'; import '../../model/video_new_model.dart'; import '../../util/device_util.dart'; import '../../util/log.util.dart'; import '../../util/share_util.dart'; import '../../widget/auth_gesture_detector.dart'; import '../../widget/load_image.dart'; import '../../widget/my_txt.dart'; import '../video/video_recommend_list_page.dart'; class VideoPlayItemWidget extends ConsumerStatefulWidget { final VideoNewModel item; final bool isActive; final int index; const VideoPlayItemWidget({ super.key, required this.item, required this.isActive, required this.index, }); @override ConsumerState createState() => _VideoItemWidgetState(); } class _VideoItemWidgetState extends ConsumerState with AutomaticKeepAliveClientMixin, SingleTickerProviderStateMixin { bool _isPlaying = false; late Timer _playStateCheckTimer; bool _isWeChatInstalled = false; Future _checkWeChatInstallation() async { if (Platform.isAndroid) { final installed = await isWeChatInstalledOnlyAndroid(); if (mounted) { setState(() => _isWeChatInstalled = installed); } } else if (Platform.isIOS) { final installed = await fluwx.isWeChatInstalled; if (mounted) { setState(() => _isWeChatInstalled = installed); } } } Future shareAction(VideoNewModel data) async { if (!mounted) return; showModalBottomSheet( context: context, builder: (context) => SafeArea( child: Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ SizedBox(height: 10.h), SizedBox( width: double.infinity, height: 80.h, child: Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ if (_isWeChatInstalled) GestureDetector( onTap: () { Navigator.pop(context); shareWeiXinUrl( title: data.shareDesc ?? "", url: data.shareUrl ?? "", ); }, child: SizedBox( width: 100.w, height: 80.h, child: Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ LoadAssetImage( 'share_wxhy', width: 40.w, height: 40.h, ), SizedBox(height: 10.h), myTxt( text: "微信好友", color: Colors.black, fontSize: 12.sp, fontWeight: FontWeight.bold, ), ], ), ), ), if (_isWeChatInstalled) GestureDetector( onTap: () { Navigator.pop(context); shareWeiXinPYUrl( title: data.shareDesc ?? "", url: data.shareUrl ?? "", ); }, child: SizedBox( width: 100.w, height: 80.h, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ LoadAssetImage( 'share_pyq', width: 40.w, height: 40.h, ), SizedBox(height: 10.h), myTxt( text: "朋友圈", color: Colors.black, fontSize: 12.sp, fontWeight: FontWeight.bold, ), ], ), ), ), GestureDetector( onTap: () { Navigator.pop(context); shareUrl( title: data.shareDesc ?? "", url: data.shareUrl ?? "", ); }, child: SizedBox( width: 100.w, height: 80.h, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ LoadAssetImage( 'share_xtfx', width: 40.w, height: 40.h, ), SizedBox(height: 10.h), myTxt( text: "系统分享", color: Colors.black, fontSize: 12.sp, fontWeight: FontWeight.bold, ), ], ), ), ), ], ), ), ], ), ), ); } @override void initState() { super.initState(); consoleLog("VideoPlayItemWidget: initState for video ${widget.item.contentId}, url: ${widget.item.url}, isActive: ${widget.isActive}"); _checkWeChatInstallation(); _startPlayStateCheck(); } void _startPlayStateCheck() { // 定期检查视频播放状态 _playStateCheckTimer = Timer.periodic(const Duration(milliseconds: 500), (_) { if (!mounted) { _playStateCheckTimer.cancel(); return; } final controller = globalVideoController; if (controller != null && widget.isActive) { final isPlaying = controller.isPlaying() ?? false; // 更新播放状态 if (isPlaying != _isPlaying) { setState(() { _isPlaying = isPlaying; }); } } }); } @override void dispose() { _playStateCheckTimer.cancel(); super.dispose(); } void _togglePlay() { if (!mounted || !widget.isActive) return; final controller = globalVideoController; if (controller == null) return; // 检查控制器是否已初始化且未释放 try { if (controller.isVideoInitialized() != true) { consoleLog("_togglePlay: controller not initialized"); return; } } catch (e) { consoleLog("_togglePlay: controller check failed: $e"); return; } // 分别处理每个操作,确保错误被捕获 if (_isPlaying) { try { controller.pause(); // 立即更新状态 setState(() { _isPlaying = false; }); } catch (e) { consoleLog("_togglePlay: pause error: $e"); // 控制器可能已被释放,忽略错误 } } else { try { controller.play(); // 立即更新状态 setState(() { _isPlaying = true; }); } catch (e) { consoleLog("_togglePlay: play error: $e"); // 控制器可能已被释放,忽略错误 } } } @override Widget build(BuildContext context) { super.build(context); final controller = globalVideoController; return GestureDetector( onTap: _togglePlay, child: Stack( fit: StackFit.expand, children: [ // 黑色背景 const ColoredBox(color: Colors.black), // 视频播放器(只在当前页激活时显示) if (controller != null && widget.isActive) BetterPlayer( key: ValueKey('video_${widget.item.contentId}'), controller: controller, ), // 播放按钮(只在视频暂停时显示) if (!_isPlaying && widget.isActive) Center( child: Image.asset( Assets.images.playIcon.path, width: 60.w, height: 60.w, ), ), Positioned( right: 10.h, bottom: 100.h, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ AuthGestureDetector( onTap: () { if (!mounted) return; ref .read(recommendListProvider.notifier) .fetchVideoLike( videoId: widget.item.contentId, current: widget.item.isLiked ?? false, ); }, child: Icon( Icons.favorite, color: widget.item.isLiked == true ? Colors.red : Colors.white, size: 25.sp, ), ), myTxt( text: widget.item.likeCount == null ? "" : widget.item.likeCount.toString(), color: Colors.white, fontSize: 12.sp, ), SizedBox(height: 15.h), AuthGestureDetector( onTap: () { if (!mounted) return; context.push( "/video/detail", extra: VideoParam( id: widget.item.contentId ?? "", videoUrl: widget.item.url, ), ); }, child: Icon(Icons.message, color: Colors.white, size: 25.sp), ), myTxt( text: widget.item.commentCount == null ? "0" : widget.item.commentCount.toString(), color: Colors.white, fontSize: 12.sp, ), SizedBox(height: 15.h), AuthGestureDetector( onTap: () { if (!mounted) return; ref .read(recommendListProvider.notifier) .fetchVideoFavorite( videoId: widget.item.contentId, current: widget.item.isFavorite ?? false, ); }, child: Icon( Icons.star, color: widget.item.isFavorite == true ? Colors.red : Colors.white, size: 25.sp, ), ), myTxt( text: widget.item.favoriteCount == null ? "0" : widget.item.favoriteCount.toString(), color: Colors.white, fontSize: 12.sp, ), SizedBox(height: 15.h), GestureDetector( onTap: () { if (!mounted) return; shareAction(widget.item); if (uuid.isNotEmpty) { ref .read(recommendListProvider.notifier) .fetchVideoShare(contentId: widget.item.contentId); } }, child: Icon( Icons.screen_share, color: Colors.white, size: 25.sp, ), ), myTxt( text: widget.item.shareCount == null ? "0" : widget.item.shareCount.toString(), color: Colors.white, fontSize: 12.sp, ), ], ), ), ], ), ); } @override bool get wantKeepAlive => true; }