game_video.dart 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  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:flutter_spinkit/flutter_spinkit.dart';
  7. import 'package:sport/bean/feed_back.dart';
  8. import 'package:sport/bean/game.dart';
  9. import 'package:sport/pages/my/feedback_detail_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/toast.dart';
  15. import 'package:sport/widgets/dialog/alert_dialog.dart';
  16. import 'package:sport/widgets/dialog/request_dialog.dart';
  17. import 'package:sport/widgets/image.dart';
  18. import 'package:sport/widgets/misc.dart';
  19. import 'package:sport/widgets/popmenu_bg.dart';
  20. import 'package:video_player/video_player.dart';
  21. class GameDetailsVideo extends StatefulWidget {
  22. final Function changeIsFullScreen;
  23. final bool _isFullScreen;
  24. VideoPlayerController controller;
  25. bool isLoading;
  26. GameInfoData data;
  27. GameDetailsVideo(this.changeIsFullScreen, this._isFullScreen, this.controller, this.isLoading, this.data, {Key key}) : super(key: key);
  28. @override
  29. State<StatefulWidget> createState() {
  30. // TODO: implement 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. VoidCallback _listener;
  39. ValueNotifier<Duration> timeEnd = ValueNotifier(Duration()); // 播放的时间
  40. ValueNotifier<Duration> timeStart = ValueNotifier(Duration()); //
  41. final spinkit = SpinKitDualRing(
  42. color: Colors.white,
  43. );
  44. @override
  45. void initState() {
  46. super.initState();
  47. widget.controller.addListener(_listener = () {
  48. if (!mounted) return;
  49. setState(() {
  50. timeStart.value = widget.controller.value.position;
  51. });
  52. timeEnd.value = widget.controller.value.duration;
  53. });
  54. }
  55. @override
  56. void dispose() {
  57. super.dispose();
  58. timer?.cancel();
  59. widget.controller?.removeListener(_listener);
  60. timeStart?.dispose();
  61. timeEnd?.dispose();
  62. }
  63. void initdelay() {
  64. if (timer != null) {
  65. timer.cancel();
  66. }
  67. setState(() {
  68. opacity = 1;
  69. });
  70. timer = Timer(new Duration(seconds: 3), () {
  71. setState(() {
  72. if (mounted) opacity = 0;
  73. });
  74. });
  75. }
  76. @override
  77. Widget build(BuildContext context) {
  78. final size = MediaQuery.of(context).size;
  79. final width = size.width;
  80. final height = size.height;
  81. bool isLoading = widget.isLoading;
  82. bool isPlaying = widget.controller.value.isPlaying;
  83. VideoPlayerController controller = widget.controller;
  84. Widget top = Row(
  85. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  86. children: <Widget>[
  87. IconButton(
  88. onPressed: () {
  89. if (widget._isFullScreen) {
  90. widget.changeIsFullScreen();
  91. } else {
  92. // 原生的返回上一页?
  93. Navigator.pop(context);
  94. }
  95. },
  96. icon: Image.asset(
  97. "lib/assets/img/topbar_return_white.png",
  98. width: 26,
  99. height: 38,
  100. ),
  101. ),
  102. if (widget._isFullScreen == false)
  103. FutureBuilder<bool>(
  104. future: DeviceApps.isAppInstalled(widget.data.packageNameAndroid),
  105. builder: (BuildContext context, AsyncSnapshot<bool> snapshot) => snapshot.data == true
  106. ? PopupMenuTheme(
  107. data: PopupMenuThemeData(shape: PopmenuShape(borderRadius: BorderRadius.all(Radius.circular(10.0)))),
  108. child: PopupMenuButton(
  109. offset: Offset(0, kToolbarHeight / 2 + 15),
  110. icon: iconMore(),
  111. onSelected: (val) {
  112. _onPopMenuSelected(val);
  113. },
  114. itemBuilder: (context) {
  115. return divideMenus([
  116. menuItem('运动记录', 'linkpop_icon_record.png', '运动记录'),
  117. menuItem('设备测试', 'linkpop_icon_motion.png', '设备测试'),
  118. menuItem('问题反馈', 'linkpop_icon_feedback.png', '问题反馈'),
  119. menuItem('删除运动', 'linkpop_icon_del.png', '删除运动'),
  120. ]);
  121. },
  122. ))
  123. : PopupMenuTheme(
  124. data: PopupMenuThemeData(shape: PopmenuShape(borderRadius: BorderRadius.all(Radius.circular(10.0)))),
  125. child: PopupMenuButton(
  126. offset: Offset(0, kToolbarHeight / 2 + 15),
  127. icon: iconMore(),
  128. onSelected: (val) {
  129. _onPopMenuSelected(val);
  130. },
  131. itemBuilder: (context) {
  132. return divideMenus([
  133. menuItem('运动记录', 'linkpop_icon_record.png', '运动记录'),
  134. menuItem('设备测试', 'linkpop_icon_motion.png', '设备测试'),
  135. menuItem('问题反馈', 'linkpop_icon_feedback.png', '问题反馈'),
  136. ]);
  137. },
  138. )))
  139. ],
  140. );
  141. Widget body = Stack(
  142. children: <Widget>[
  143. isLoading == false
  144. ? InkWell(
  145. child: (VideoPlayer(controller)),
  146. onTap: () {
  147. initdelay();
  148. },
  149. onDoubleTap: () {
  150. if (isPlaying) {
  151. controller.pause();
  152. } else {
  153. controller.play();
  154. }
  155. },
  156. )
  157. : spinkit,
  158. isPlaying != true && isLoading == false
  159. ? InkWell(
  160. child: Center(
  161. child: Image.asset(
  162. "lib/assets/img/game_icon_play.png",
  163. width: 50,
  164. height: 50,
  165. )),
  166. onTap: () async {
  167. if (isPlaying) {
  168. controller.pause();
  169. } else {
  170. if (controller.value.position.compareTo(controller.value.duration) == 0) {
  171. controller.seekTo(Duration.zero);
  172. } else {
  173. initdelay();
  174. }
  175. controller.play();
  176. }
  177. },
  178. )
  179. : Container(),
  180. Positioned(
  181. left: 0,
  182. right: 0,
  183. child: AnimatedOpacity(
  184. duration: new Duration(seconds: 1),
  185. opacity: opacity,
  186. child: Container(
  187. padding: EdgeInsets.symmetric(horizontal: 0),
  188. // color: Colors.red,
  189. decoration: BoxDecoration(
  190. gradient: LinearGradient(begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [
  191. Color(0xFF000000).withOpacity(.5),
  192. Color(0x00000000)
  193. // Color(0x00000000),
  194. ])),
  195. child: widget._isFullScreen
  196. ? SafeArea(
  197. child: top,
  198. right: false,
  199. )
  200. : top,
  201. ),
  202. ),
  203. ),
  204. Positioned(
  205. bottom: 5,
  206. left: 0,
  207. right: 0,
  208. child: AnimatedOpacity(
  209. duration: new Duration(seconds: 1),
  210. opacity: opacity,
  211. child: Container(
  212. padding: EdgeInsets.symmetric(horizontal: 8),
  213. decoration: BoxDecoration(
  214. gradient: LinearGradient(begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [
  215. Color(0x00000000), Color(0xFF000000).withOpacity(.5)
  216. // Color(0x00000000),
  217. ])),
  218. child: Column(
  219. mainAxisSize: MainAxisSize.min,
  220. children: <Widget>[
  221. Row(
  222. mainAxisAlignment: MainAxisAlignment.end,
  223. children: <Widget>[
  224. InkWell(
  225. child: isPlaying != true
  226. ? Image.asset(
  227. "lib/assets/img/gamevideo_play_white.png",
  228. width: 26,
  229. height: 38,
  230. )
  231. : Image.asset(
  232. "lib/assets/img/gamevideo_suspend.png",
  233. width: 26,
  234. height: 38,
  235. ),
  236. onTap: () {
  237. if (isPlaying) {
  238. controller.pause();
  239. } else {
  240. if (controller.value.position.compareTo(controller.value.duration) == 0) {
  241. controller.seekTo(Duration.zero);
  242. } else {
  243. initdelay();
  244. }
  245. controller.play();
  246. }
  247. },
  248. ),
  249. Padding(
  250. padding: EdgeInsets.symmetric(horizontal: 5.0),
  251. child: ValueListenableBuilder(
  252. valueListenable: timeStart, builder: (_, v, __) => Text(v.toString().substring(2, 7), style: TextStyle(color: Colors.white))),
  253. ),
  254. Expanded(
  255. child: SizedBox(
  256. height: 3,
  257. child: VideoProgressIndicator(controller,
  258. allowScrubbing: true, // 是否可以拖动
  259. padding: const EdgeInsets.all(0),
  260. colors: VideoProgressColors(playedColor: Theme.of(context).accentColor))),
  261. ),
  262. Padding(
  263. padding: EdgeInsets.symmetric(horizontal: 5.0),
  264. child: ValueListenableBuilder(
  265. valueListenable: timeEnd, builder: (_, v, __) => Text(v.toString().substring(2, 7), style: TextStyle(color: Colors.white))),
  266. ),
  267. InkWell(
  268. // 全屏按钮
  269. child: Image.asset(
  270. "lib/assets/img/gamevideo_fullscreen.png",
  271. width: 26,
  272. height: 38,
  273. ),
  274. onTap: () {
  275. widget.changeIsFullScreen();
  276. })
  277. ],
  278. ),
  279. ],
  280. ),
  281. ),
  282. ),
  283. ),
  284. // ClosedCaption(text: _controller.value.caption.text),
  285. ],
  286. );
  287. return Container(
  288. color: Colors.black,
  289. // 约束宽高的
  290. child: widget._isFullScreen
  291. ? body
  292. : AspectRatio(
  293. aspectRatio: 1.78,
  294. child: body,
  295. ));
  296. }
  297. _onPopMenuSelected(var val) async {
  298. switch (val) {
  299. case '运动记录':
  300. NavigatorUtil.goGameHistory(context, widget.data);
  301. break;
  302. case '设备测试':
  303. ToastUtil.show("设备测试");
  304. break;
  305. case '问题反馈':
  306. FeedTypeInfoData group;
  307. await request(context, () async {
  308. FeedTypeInfo _feedTypeInfo = await loginApi.getFeedBackTypes();
  309. if (_feedTypeInfo.code == 0) {
  310. group = _feedTypeInfo.data.singleWhere((element) => element.groupId == '3');
  311. }
  312. });
  313. if (group != null) {
  314. NavigatorUtil.goPage(context, (context) => FeedbackDetailPage(group));
  315. }
  316. break;
  317. case '删除运动':
  318. if (Platform.isAndroid) {
  319. var installed = await DeviceApps.isAppInstalled(widget.data.packageNameAndroid);
  320. if (installed == true) {
  321. if (await showDialog(
  322. context: context,
  323. builder: (context) => CustomAlertDialog(title: '是否删除运动', ok: () => Navigator.of(context).pop(true)),
  324. ) ==
  325. true) {
  326. GameManager.deleteFile(widget.data);
  327. if (Platform.isAndroid) {
  328. AndroidIntent intent = AndroidIntent(
  329. action: 'android.intent.action.DELETE',
  330. data: 'package:${widget.data.packageNameAndroid}',
  331. );
  332. await intent.launch();
  333. }
  334. }
  335. } else {}
  336. }
  337. break;
  338. }
  339. }
  340. }