video_play_item_widget.dart 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. import 'dart:io';
  2. import 'package:better_player_plus/better_player_plus.dart';
  3. import 'package:flutter/material.dart';
  4. import 'package:flutter_riverpod/flutter_riverpod.dart';
  5. import 'package:flutter_screenutil/flutter_screenutil.dart';
  6. import 'package:go_router/go_router.dart';
  7. import 'package:news_app/ui/video/video_detail_page.dart';
  8. import 'package:news_app/ui/video/video_recommend_list_page.dart';
  9. import '../../constant/config.dart';
  10. import '../../gen/assets.gen.dart';
  11. import '../../model/video_new_model.dart';
  12. import '../../util/device_util.dart';
  13. import '../../util/log.util.dart';
  14. import '../../util/share_util.dart';
  15. import '../../widget/auth_gesture_detector.dart';
  16. import '../../widget/load_image.dart';
  17. import '../../widget/my_txt.dart';
  18. class VideoPlayItemWidget extends ConsumerStatefulWidget {
  19. final VideoNewModel item;
  20. final bool isActive;
  21. const VideoPlayItemWidget({
  22. super.key,
  23. required this.item,
  24. required this.isActive,
  25. });
  26. @override
  27. ConsumerState<VideoPlayItemWidget> createState() => _VideoItemWidgetState();
  28. }
  29. class _VideoItemWidgetState extends ConsumerState<VideoPlayItemWidget> {
  30. late BetterPlayerController _controller;
  31. bool _isPlaying = false;
  32. bool _isWeChatInstalled = false;
  33. Future<void> _checkWeChatInstallation() async {
  34. if (Platform.isAndroid) {
  35. final installed = await isWeChatInstalledOnlyAndroid();
  36. setState(() => _isWeChatInstalled = installed);
  37. } else if (Platform.isIOS) {
  38. final installed = await fluwx.isWeChatInstalled;
  39. setState(() => _isWeChatInstalled = installed);
  40. }
  41. }
  42. Future<void> shareAction(VideoNewModel data) async {
  43. showModalBottomSheet(
  44. context: context,
  45. builder:
  46. (context) => SafeArea(
  47. child: Column(
  48. mainAxisSize: MainAxisSize.min,
  49. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  50. children: [
  51. SizedBox(height: 10.h),
  52. Container(
  53. width: double.infinity,
  54. height: 80.h,
  55. decoration: BoxDecoration(
  56. borderRadius: BorderRadius.only(
  57. topLeft: Radius.circular(10.r),
  58. topRight: Radius.circular(10.r),
  59. ),
  60. color: Colors.white,
  61. ),
  62. child: Row(
  63. crossAxisAlignment: CrossAxisAlignment.center,
  64. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  65. children: [
  66. if (_isWeChatInstalled)
  67. GestureDetector(
  68. onTap: () {
  69. Navigator.pop(context);
  70. shareWeiXinUrl(
  71. title: data.shareDesc ?? "",
  72. url: data.shareUrl ?? "",
  73. );
  74. },
  75. child: Container(
  76. width: 100.w,
  77. height: 80.h,
  78. child: Column(
  79. crossAxisAlignment: CrossAxisAlignment.center,
  80. mainAxisAlignment: MainAxisAlignment.center,
  81. children: [
  82. LoadAssetImage(
  83. 'share_wxhy',
  84. width: 40.w,
  85. height: 40.h,
  86. ),
  87. SizedBox(height: 10.h),
  88. myTxt(
  89. text: "微信好友",
  90. color: Colors.black,
  91. fontSize: 12.sp,
  92. fontWeight: FontWeight.bold,
  93. ),
  94. ],
  95. ),
  96. ),
  97. ),
  98. if (_isWeChatInstalled)
  99. GestureDetector(
  100. onTap: () {
  101. Navigator.pop(context);
  102. shareWeiXinPYUrl(
  103. title: data.shareDesc ?? "",
  104. url: data.shareUrl ?? "",
  105. );
  106. },
  107. child: Container(
  108. width: 100.w,
  109. height: 80.h,
  110. child: Column(
  111. mainAxisAlignment: MainAxisAlignment.center,
  112. children: [
  113. LoadAssetImage(
  114. 'share_pyq',
  115. width: 40.w,
  116. height: 40.h,
  117. ),
  118. SizedBox(height: 10.h),
  119. myTxt(
  120. text: "朋友圈",
  121. color: Colors.black,
  122. fontSize: 12.sp,
  123. fontWeight: FontWeight.bold,
  124. ),
  125. ],
  126. ),
  127. ),
  128. ),
  129. GestureDetector(
  130. onTap: () {
  131. Navigator.pop(context);
  132. shareUrl(
  133. title: data.shareDesc ?? "",
  134. url: data.shareUrl ?? "",
  135. );
  136. },
  137. child: Container(
  138. width: 100.w,
  139. height: 80.h,
  140. child: Column(
  141. mainAxisAlignment: MainAxisAlignment.center,
  142. children: [
  143. LoadAssetImage(
  144. 'share_xtfx',
  145. width: 40.w,
  146. height: 40.h,
  147. ),
  148. SizedBox(height: 10.h),
  149. myTxt(
  150. text: "系统分享",
  151. color: Colors.black,
  152. fontSize: 12.sp,
  153. fontWeight: FontWeight.bold,
  154. ),
  155. ],
  156. ),
  157. ),
  158. ),
  159. ],
  160. ),
  161. ),
  162. ],
  163. ),
  164. ),
  165. );
  166. }
  167. @override
  168. void initState() {
  169. super.initState();
  170. consoleLog("VideoPlayItemWidget: initState for video ${widget.item.contentId}, url: ${widget.item.url}, isActive: ${widget.isActive}");
  171. _checkWeChatInstallation();
  172. _controller = BetterPlayerController(
  173. BetterPlayerConfiguration(
  174. autoPlay: false,
  175. looping: true,
  176. aspectRatio: 16 / 9,
  177. fit: BoxFit.fitWidth,
  178. controlsConfiguration: const BetterPlayerControlsConfiguration(
  179. showControls: false,
  180. ),
  181. ),
  182. betterPlayerDataSource: BetterPlayerDataSource(
  183. BetterPlayerDataSourceType.network,
  184. widget.item.url ?? "",
  185. ),
  186. );
  187. _controller.addEventsListener(_handlePlayerEvent);
  188. // 如果初始化时就是激活状态,立即播放
  189. if (widget.isActive && mounted) {
  190. consoleLog("VideoPlayItemWidget: isActive is true, starting playback");
  191. _controller.play();
  192. }
  193. }
  194. void _handlePlayerEvent(BetterPlayerEvent event) {
  195. if (!mounted) return;
  196. consoleLog("VideoPlayItemWidget: BetterPlayerEvent: ${event.betterPlayerEventType} for video ${widget.item.contentId}");
  197. if (event.betterPlayerEventType == BetterPlayerEventType.play ||
  198. event.betterPlayerEventType == BetterPlayerEventType.pause ||
  199. event.betterPlayerEventType == BetterPlayerEventType.finished) {
  200. final isPlaying = _controller.isPlaying() ?? false;
  201. if (mounted && isPlaying != _isPlaying) {
  202. setState(() {
  203. _isPlaying = isPlaying;
  204. });
  205. }
  206. }
  207. }
  208. @override
  209. void didUpdateWidget(covariant VideoPlayItemWidget oldWidget) {
  210. super.didUpdateWidget(oldWidget);
  211. if (!mounted) return;
  212. consoleLog("VideoPlayItemWidget: didUpdateWidget, oldActive: ${oldWidget.isActive}, newActive: ${widget.isActive}");
  213. if (widget.isActive && !oldWidget.isActive) {
  214. consoleLog("VideoPlayItemWidget: Activating video ${widget.item.contentId}");
  215. _controller.play();
  216. } else if (!widget.isActive && oldWidget.isActive) {
  217. consoleLog("VideoPlayItemWidget: Deactivating video ${widget.item.contentId}");
  218. _controller.pause();
  219. }
  220. }
  221. @override
  222. void dispose() {
  223. _controller.removeEventsListener(_handlePlayerEvent);
  224. _controller.dispose();
  225. super.dispose();
  226. }
  227. void _togglePlay() {
  228. if (!mounted) return;
  229. if (_isPlaying) {
  230. _controller.pause();
  231. } else {
  232. _controller.play();
  233. }
  234. }
  235. @override
  236. Widget build(BuildContext context) {
  237. return GestureDetector(
  238. onTap: _togglePlay,
  239. child: Stack(
  240. fit: StackFit.expand,
  241. children: [
  242. ColoredBox(
  243. color: Colors.black,
  244. child: BetterPlayer(controller: _controller),
  245. ),
  246. if (!_isPlaying)
  247. Center(
  248. child: Image.asset(
  249. Assets.images.playIcon.path,
  250. width: 60.w,
  251. height: 60.w,
  252. ),
  253. ),
  254. Positioned(
  255. right: 10.h,
  256. bottom: 100.h,
  257. child: Column(
  258. mainAxisAlignment: MainAxisAlignment.center,
  259. children: [
  260. AuthGestureDetector(
  261. onTap: () {
  262. ref
  263. .read(recommendListProvider.notifier)
  264. .fetchVideoLike(
  265. videoId: widget.item.contentId,
  266. current: widget.item.isLiked ?? false,
  267. );
  268. },
  269. child: Icon(
  270. Icons.favorite,
  271. color:
  272. widget.item.isLiked == true ? Colors.red : Colors.white,
  273. size: 25.sp,
  274. ),
  275. ),
  276. myTxt(
  277. text:
  278. widget.item.likeCount == null
  279. ? ""
  280. : widget.item.likeCount.toString(),
  281. color: Colors.white,
  282. fontSize: 12.sp,
  283. ),
  284. SizedBox(height: 15.h),
  285. AuthGestureDetector(
  286. onTap: () {
  287. context.push(
  288. "/video/detail",
  289. extra: VideoParam(
  290. id: widget.item.contentId ?? "",
  291. videoUrl: widget.item.url,
  292. ),
  293. );
  294. },
  295. child: Icon(Icons.message, color: Colors.white, size: 25.sp),
  296. ),
  297. myTxt(
  298. text:
  299. widget.item.commentCount == null
  300. ? "0"
  301. : widget.item.commentCount.toString(),
  302. color: Colors.white,
  303. fontSize: 12.sp,
  304. ),
  305. SizedBox(height: 15.h),
  306. AuthGestureDetector(
  307. onTap: () {
  308. ref
  309. .read(recommendListProvider.notifier)
  310. .fetchVideoFavorite(
  311. videoId: widget.item.contentId,
  312. current: widget.item.isFavorite ?? false,
  313. );
  314. },
  315. child: Icon(
  316. Icons.star,
  317. color:
  318. widget.item.isFavorite == true
  319. ? Colors.red
  320. : Colors.white,
  321. size: 25.sp,
  322. ),
  323. ),
  324. myTxt(
  325. text:
  326. widget.item.favoriteCount == null
  327. ? "0"
  328. : widget.item.favoriteCount.toString(),
  329. color: Colors.white,
  330. fontSize: 12.sp,
  331. ),
  332. SizedBox(height: 15.h),
  333. GestureDetector(
  334. onTap: () {
  335. // shareUrl(
  336. // title: widget.item.shareDesc ?? "",
  337. // url: widget.item.shareUrl ?? "",
  338. // );
  339. shareAction(widget.item);
  340. if (uuid.isNotEmpty) {
  341. ref
  342. .read(recommendListProvider.notifier)
  343. .fetchVideoShare(contentId: widget.item.contentId);
  344. }
  345. },
  346. child: Icon(
  347. Icons.screen_share,
  348. color: Colors.white,
  349. size: 25.sp,
  350. ),
  351. ),
  352. myTxt(
  353. text:
  354. widget.item.shareCount == null
  355. ? "0"
  356. : widget.item.shareCount.toString(),
  357. color: Colors.white,
  358. fontSize: 12.sp,
  359. ),
  360. ],
  361. ),
  362. ),
  363. ],
  364. ),
  365. );
  366. }
  367. }