game_video.dart 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. import 'dart:async';
  2. import 'dart:io';
  3. import 'package:android_intent/android_intent.dart';
  4. import 'package:device_apps/device_apps.dart';
  5. import 'package:flutter/material.dart';
  6. import 'package:sport/bean/feed_back.dart';
  7. import 'package:sport/bean/game.dart';
  8. import 'package:sport/pages/my/feedback_detail_page.dart';
  9. import 'package:sport/pages/web/web_page.dart';
  10. import 'package:sport/router/navigator_util.dart';
  11. import 'package:sport/services/api/inject_api.dart';
  12. import 'package:sport/services/game_manager.dart';
  13. import 'package:sport/sharesdk/wechat.dart';
  14. import 'package:sport/utils/DateFormat.dart';
  15. import 'package:sport/utils/toast.dart';
  16. import 'package:sport/widgets/dialog/alert_dialog.dart';
  17. import 'package:sport/widgets/dialog/request_dialog.dart';
  18. import 'package:sport/widgets/image.dart';
  19. import 'package:sport/widgets/misc.dart';
  20. import 'package:sport/widgets/popmenu_bg.dart';
  21. import 'package:video_player/video_player.dart';
  22. class GameDetailsVideo extends StatefulWidget {
  23. final Function changeIsFullScreen;
  24. final bool _isFullScreen;
  25. final VideoPlayerController controller;
  26. bool isLoading;
  27. final GameInfoData data;
  28. GameDetailsVideo(this.changeIsFullScreen, this._isFullScreen, this.controller, this.isLoading, this.data, {Key? key}) : super(key: key);
  29. @override
  30. State<StatefulWidget> createState() {
  31. return _GameDetailsVideoState();
  32. }
  33. }
  34. class _GameDetailsVideoState extends State<GameDetailsVideo> with WechatMixin, InjectLoginApi {
  35. void changeIsFullScreen;
  36. Timer? timer;
  37. var opacity = 1.0;
  38. late VoidCallback _listener;
  39. ValueNotifier<Duration> timeEnd = ValueNotifier(Duration()); // 播放的时间
  40. ValueNotifier<Duration> timeStart = ValueNotifier(Duration()); //
  41. bool isPlaying = false;
  42. @override
  43. void initState() {
  44. super.initState();
  45. widget.controller.addListener(_listener = () {
  46. if (!mounted) return;
  47. timeStart.value = widget.controller.value.position;
  48. timeEnd.value = widget.controller.value.duration;
  49. setState(() {
  50. isPlaying = widget.controller.value.isPlaying;
  51. });
  52. });
  53. }
  54. @override
  55. void dispose() {
  56. super.dispose();
  57. timer?.cancel();
  58. widget.controller.removeListener(_listener);
  59. timeStart.dispose();
  60. timeEnd.dispose();
  61. }
  62. void initdelay() {
  63. if (timer != null) {
  64. timer!.cancel();
  65. }
  66. setState(() {
  67. opacity = 1;
  68. });
  69. timer = Timer(new Duration(seconds: 3), () {
  70. setState(() {
  71. if (mounted) opacity = 0;
  72. });
  73. });
  74. }
  75. @override
  76. Widget build(BuildContext context) {
  77. bool isLoading = widget.isLoading;
  78. VideoPlayerController controller = widget.controller;
  79. Widget body = Stack(
  80. children: <Widget>[
  81. isLoading == false
  82. ? InkWell(
  83. child: Center(child: AspectRatio(aspectRatio: controller.value.aspectRatio,child: (VideoPlayer(controller)))),
  84. onTap: () {
  85. initdelay();
  86. },
  87. onDoubleTap: () {
  88. if (isPlaying) {
  89. controller.pause();
  90. isPlaying = false;
  91. } else {
  92. controller.play();
  93. }
  94. },
  95. )
  96. : Center(child: CircularProgressIndicator()),
  97. isPlaying != true && isLoading == false
  98. ? Container(
  99. width: double.infinity,
  100. height: double.infinity,
  101. color: Colors.black45,
  102. child: Center(
  103. child: Column(
  104. mainAxisSize: MainAxisSize.min,
  105. children: [
  106. SizedBox(
  107. height: 20,
  108. ),
  109. InkWell(
  110. child: Center(
  111. child: Image.asset(
  112. "lib/assets/img/game_icon_play.png",
  113. width: 50,
  114. height: 50,
  115. )),
  116. onTap: () async {
  117. if (isPlaying) {
  118. controller.pause();
  119. } else {
  120. if (controller.value.position.compareTo(controller.value.duration) == 0) {
  121. controller.seekTo(Duration.zero);
  122. } else {
  123. initdelay();
  124. }
  125. controller.play();
  126. }
  127. },
  128. ),
  129. SizedBox(
  130. height: 5,
  131. ),
  132. Text(
  133. "运动演示",
  134. style: TextStyle(color: Colors.white, fontSize: 14.0),
  135. ),
  136. ],
  137. ),
  138. ),
  139. )
  140. : Container(),
  141. if (widget._isFullScreen == true)
  142. Positioned(
  143. left: 0,
  144. right: 0,
  145. child: AnimatedOpacity(
  146. duration: new Duration(seconds: 1),
  147. opacity: opacity,
  148. child: Container(
  149. padding: EdgeInsets.symmetric(horizontal: 0),
  150. // color: Colors.red,
  151. decoration: BoxDecoration(
  152. gradient: LinearGradient(begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [
  153. Color(0xFF000000).withOpacity(.5),
  154. Color(0x00000000)
  155. // Color(0x00000000),
  156. ])),
  157. child: SafeArea(
  158. child: Row(
  159. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  160. children: <Widget>[
  161. IconButton(
  162. onPressed: () {
  163. if (widget._isFullScreen) {
  164. widget.changeIsFullScreen();
  165. } else {
  166. // 原生的返回上一页?
  167. Navigator.pop(context);
  168. }
  169. },
  170. icon: Image.asset(
  171. "lib/assets/img/topbar_return_white.png",
  172. width: 26,
  173. height: 38,
  174. ),
  175. ),
  176. ],
  177. ),
  178. right: false,
  179. ),
  180. ),
  181. ),
  182. ),
  183. if (isPlaying == true)
  184. widget._isFullScreen == true ? Positioned(
  185. bottom: 0,
  186. left: 0,
  187. right: 0,
  188. child: AnimatedOpacity(
  189. duration: new Duration(seconds: 1),
  190. opacity: opacity,
  191. child: Container(
  192. padding: EdgeInsets.symmetric(horizontal: 8),
  193. decoration: BoxDecoration(
  194. gradient: LinearGradient(begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [
  195. Color(0x00000000), Color(0xFF000000).withOpacity(.5)
  196. // Color(0x00000000),
  197. ])),
  198. child: Column(
  199. mainAxisSize: MainAxisSize.min,
  200. children: <Widget>[
  201. Row(
  202. // mainAxisAlignment: MainAxisAlignment.end,
  203. children: <Widget>[
  204. InkWell(
  205. child: isPlaying != true
  206. ? Image.asset(
  207. "lib/assets/img/gamevideo_play_white.png",
  208. width: 26,
  209. height: 38,
  210. )
  211. : Image.asset(
  212. "lib/assets/img/gamevideo_suspend.png",
  213. width: 26,
  214. height: 38,
  215. ),
  216. onTap: () {
  217. if (isPlaying) {
  218. controller.pause();
  219. } else {
  220. if (controller.value.position.compareTo(controller.value.duration) == 0) {
  221. controller.seekTo(Duration.zero);
  222. } else {
  223. initdelay();
  224. }
  225. controller.play();
  226. }
  227. },
  228. ),
  229. Padding(
  230. padding: EdgeInsets.symmetric(horizontal: 5.0),
  231. child: ValueListenableBuilder<Duration>(
  232. valueListenable: timeStart,
  233. builder: (_, v, __) {
  234. int second = v.inSeconds;
  235. return Text(DateFormat.toTime(second), style: TextStyle(color: Colors.white));
  236. }),
  237. ),
  238. Expanded(
  239. child: SizedBox(
  240. height: 3,
  241. child: VideoProgressIndicator(controller,
  242. allowScrubbing: true, // 是否可以拖动
  243. padding: const EdgeInsets.all(0),
  244. colors: VideoProgressColors(playedColor: Theme.of(context).accentColor))),
  245. ),
  246. Padding(
  247. padding: EdgeInsets.symmetric(horizontal: 5.0),
  248. child: ValueListenableBuilder<Duration>(
  249. valueListenable: timeEnd,
  250. builder: (_, v, __) {
  251. int second = v.inSeconds;
  252. return Text(DateFormat.toTime(second), style: TextStyle(color: Colors.white));
  253. }),
  254. ),
  255. InkWell(
  256. // 全屏按钮
  257. child: Image.asset(
  258. "lib/assets/img/gamevideo_fullscreen.png",
  259. width: 26,
  260. height: 38,
  261. ),
  262. onTap: () {
  263. widget.changeIsFullScreen();
  264. })
  265. ],
  266. ),
  267. ],
  268. ),
  269. ),
  270. ),
  271. ):Positioned(
  272. bottom: 0,
  273. left: 0,
  274. right: 0,
  275. child: AnimatedOpacity(
  276. duration: new Duration(seconds: 1),
  277. opacity: opacity,
  278. child: Container(
  279. padding: EdgeInsets.symmetric(horizontal: 8),
  280. // decoration: BoxDecoration(
  281. // gradient: LinearGradient(begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [
  282. // Color(0x00000000), Color(0xFF000000).withOpacity(.5)
  283. // // Color(0x00000000),
  284. // ])),
  285. child: Column(
  286. mainAxisSize: MainAxisSize.min,
  287. children: <Widget>[
  288. Row(
  289. // mainAxisAlignment: MainAxisAlignment.end,
  290. children: <Widget>[
  291. // InkWell(
  292. // child: isPlaying != true
  293. // ? Image.asset(
  294. // "lib/assets/img/gamevideo_play_white.png",
  295. // width: 26,
  296. // height: 38,
  297. // )
  298. // : Image.asset(
  299. // "lib/assets/img/gamevideo_suspend.png",
  300. // width: 26,
  301. // height: 38,
  302. // ),
  303. // onTap: () {
  304. // if (isPlaying) {
  305. // controller.pause();
  306. // } else {
  307. // if (controller.value.position.compareTo(controller.value.duration) == 0) {
  308. // controller.seekTo(Duration.zero);
  309. // } else {
  310. // initdelay();
  311. // }
  312. // controller.play();
  313. // }
  314. // },
  315. // ),
  316. // Padding(
  317. // padding: EdgeInsets.symmetric(horizontal: 5.0),
  318. // child: ValueListenableBuilder<Duration>(
  319. // valueListenable: timeStart,
  320. // builder: (_, v, __) {
  321. // int second = v.inSeconds;
  322. // return Text(DateFormat.toTime(second), style: TextStyle(color: Colors.white));
  323. // }),
  324. // ),
  325. // Expanded(
  326. // child: SizedBox(
  327. // height: 3,
  328. // child: VideoProgressIndicator(controller,
  329. // allowScrubbing: true, // 是否可以拖动
  330. // padding: const EdgeInsets.all(0),
  331. // colors: VideoProgressColors(playedColor: Theme.of(context).accentColor))),
  332. // ),
  333. // Padding(
  334. // padding: EdgeInsets.symmetric(horizontal: 5.0),
  335. // child: ValueListenableBuilder<Duration>(
  336. // valueListenable: timeEnd,
  337. // builder: (_, v, __) {
  338. // int second = v.inSeconds;
  339. // return Text(DateFormat.toTime(second), style: TextStyle(color: Colors.white));
  340. // }),
  341. // ),
  342. InkWell(
  343. // 全屏按钮
  344. child: Image.asset(
  345. "lib/assets/img/gamevideo_fullscreen.png",
  346. width: 26,
  347. height: 38,
  348. ),
  349. onTap: () {
  350. widget.changeIsFullScreen();
  351. })
  352. ],
  353. ),
  354. ],
  355. ),
  356. ),
  357. ),
  358. ),
  359. // ClosedCaption(text: _controller.value.caption.text),
  360. ],
  361. );
  362. return Container(
  363. color: Colors.black,
  364. // 约束宽高的
  365. child: widget._isFullScreen
  366. ? body
  367. : AspectRatio(
  368. aspectRatio: 1.78,
  369. child: body,
  370. ));
  371. }
  372. _onPopMenuSelected(var val) async {
  373. switch (val) {
  374. case '运动记录':
  375. NavigatorUtil.goGameHistory(context, widget.data);
  376. break;
  377. case '操作指引':
  378. NavigatorUtil.goPage(context, (context) => WebPage(url: "http://xie-web.hiyd.com/index.html#/game_guide?id=${widget.data.id}"));
  379. break;
  380. case '问题反馈':
  381. FeedTypeInfoData? group;
  382. await request(context, () async {
  383. FeedTypeInfo _feedTypeInfo = await loginApi.getFeedBackTypes();
  384. if (_feedTypeInfo.code == 0) {
  385. group = _feedTypeInfo.data?.singleWhere((element) => element.groupId == '3');
  386. }
  387. });
  388. if (group != null) {
  389. NavigatorUtil.goPage(context, (context) => FeedbackDetailPage(group!));
  390. }
  391. break;
  392. case '删除运动':
  393. if (Platform.isAndroid) {
  394. var installed = await DeviceApps.isAppInstalled(widget.data.packageNameAndroid ?? "");
  395. if (installed == true) {
  396. if (await showDialog(
  397. context: context,
  398. builder: (context) => CustomAlertDialog(title: '是否删除运动', ok: () => Navigator.of(context).pop(true)),
  399. ) ==
  400. true) {
  401. GameManager.deleteFile(widget.data);
  402. if (Platform.isAndroid) {
  403. AndroidIntent intent = AndroidIntent(
  404. action: 'android.intent.action.DELETE',
  405. data: 'package:${widget.data.packageNameAndroid}',
  406. );
  407. await intent.launch();
  408. setState(() {});
  409. }
  410. }
  411. } else {}
  412. }
  413. break;
  414. }
  415. }
  416. }