YSVideoDetail.dart 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  1. import 'package:flutter/cupertino.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter/services.dart';
  4. import 'package:flutterappfuyou/code/base/YSNetWorking.dart';
  5. import 'package:flutterappfuyou/code/base/YSTools.dart';
  6. import 'package:video_player/video_player.dart';
  7. import 'package:wakelock/wakelock.dart';
  8. class YSVideoDetail extends StatefulWidget {
  9. final videoId;
  10. const YSVideoDetail({Key key, this.videoId}) : super(key: key);
  11. @override
  12. _YSVideoDetailState createState() => _YSVideoDetailState();
  13. }
  14. class _YSVideoDetailState extends State<YSVideoDetail> {
  15. Map _infoDict = {};
  16. @override
  17. void initState() {
  18. Wakelock.enable();
  19. Future.delayed(Duration(seconds: 0)).then((value) {
  20. _getVideoDetailData();
  21. });
  22. super.initState();
  23. }
  24. @override
  25. void dispose() {
  26. Wakelock.disable();
  27. super.dispose();
  28. }
  29. @override
  30. Widget build(BuildContext context) {
  31. return AnnotatedRegion<SystemUiOverlayStyle>(
  32. value: SystemUiOverlayStyle.light,
  33. child: Scaffold(
  34. body: SingleChildScrollView(
  35. child: Column(
  36. children: [
  37. Container(
  38. height: MediaQuery.of(context).size.height-50,
  39. width: MediaQuery.of(context).size.width,
  40. color: Color(0xFFF5F3F0),
  41. child: LayoutBuilder(
  42. builder: (BuildContext context, BoxConstraints constraints) {
  43. return Column(
  44. children: [
  45. Stack(
  46. children: [
  47. Container(
  48. height: constraints.maxHeight*0.4,
  49. color: Colors.black,
  50. child: _infoDict.isNotEmpty?YSVideoView(url: _infoDict['url_play'],):Container(),
  51. ),
  52. Container(
  53. child: GestureDetector(
  54. child: Icon(Icons.chevron_left,size: 30,color: Colors.white,),
  55. onTap: (){Navigator.pop(context);},
  56. ),
  57. padding: EdgeInsets.only(top: ysTOP(context)+10,left: 15),
  58. )
  59. ],
  60. ),
  61. Container(
  62. height: constraints.maxHeight*0.1,
  63. alignment: Alignment.centerLeft,
  64. child: Text(_infoDict['title']??'',style: TextStyle(fontSize: 15,color: Color(0xFF3A3A3C)),maxLines: 2,),
  65. padding: EdgeInsets.only(left: 10,right: 10),
  66. decoration: BoxDecoration(
  67. border: Border(bottom: BorderSide(color: Colors.grey,width: 0.1))
  68. ),
  69. ),
  70. Container(
  71. height: constraints.maxHeight*0.05,
  72. padding: EdgeInsets.only(left: 10,right: 10),
  73. decoration: BoxDecoration(
  74. border: Border(bottom: BorderSide(color: Colors.grey.withOpacity(0.1),width: 5))
  75. ),
  76. child: Row(
  77. children: [
  78. Container(
  79. width: (constraints.maxWidth-20)*0.8,
  80. child: Text(_infoDict['created_at']??'',style: TextStyle(fontSize: 12,color: Color(0xFF707070)),maxLines: 1,),
  81. ),
  82. Container(
  83. width: (constraints.maxWidth-20)*0.2,
  84. child: RichText(
  85. text: TextSpan(
  86. style: TextStyle(fontSize: 12,color: Color(0xFF707070)),
  87. children: [
  88. WidgetSpan(child: Icon(Icons.remove_red_eye_outlined,size: 15,color: Color(0xFF707070))),
  89. TextSpan(text: ' ${_infoDict['view']??''}')
  90. ]
  91. ),
  92. ),
  93. alignment: Alignment.centerRight,
  94. )
  95. ],
  96. ),
  97. ),
  98. Container(
  99. height: constraints.maxHeight*0.05,
  100. alignment: Alignment.centerLeft,
  101. child: Text('${_infoDict['comment_count']??''}条评论',style: TextStyle(fontSize: 12,color: Color(0xFF707070)),maxLines: 1,),
  102. padding: EdgeInsets.only(left: 10,right: 10),
  103. decoration: BoxDecoration(
  104. border: Border(bottom: BorderSide(color: Colors.grey,width: 0.1))
  105. ),
  106. ),
  107. Container(
  108. height: constraints.maxHeight*0.4,
  109. child: YSCommentView(videoId: widget.videoId,),
  110. )
  111. ],
  112. );
  113. },
  114. )
  115. ),
  116. Container(
  117. height: 50,
  118. color: Colors.white,
  119. width: MediaQuery.of(context).size.width,
  120. padding: EdgeInsets.only(left: 20,right: 20,top: 10,bottom: 10),
  121. child: GestureDetector(
  122. onTap: (){
  123. ysShowBottomAlertView2(context, YSInputView(valueSetter: (value) async{
  124. Map dict = await ysRequestHttp(context, requestType.post, 'train/video/comment', {'video_id':widget.videoId,'body':value});
  125. if(dict!=null){
  126. FocusScope.of(context).unfocus();
  127. refreshKey2.currentState.refresh();
  128. }
  129. },));
  130. },
  131. child: Container(
  132. alignment: Alignment.centerLeft,
  133. padding: EdgeInsets.only(left: 20,right: 20),
  134. child: Text('我来说两句',style: TextStyle(fontSize: 15,color: Color(0xFF707070)),),
  135. decoration: BoxDecoration(
  136. color: Color(0xFFF5F3F0),
  137. borderRadius: BorderRadius.all(Radius.circular(50))
  138. ),
  139. ),
  140. ),
  141. )
  142. ],
  143. ),
  144. ),
  145. ),
  146. );
  147. }
  148. _getVideoDetailData() async{
  149. Map dict = await ysRequestHttp(context, requestType.get, 'train/video/info', {'video_id':widget.videoId});
  150. if(dict!=null){
  151. _infoDict = dict['data'];
  152. setState(() {});
  153. }
  154. }
  155. }
  156. class YSVideoView extends StatefulWidget {
  157. final String url;
  158. const YSVideoView({Key key, this.url}) : super(key: key);
  159. @override
  160. _YSVideoViewState createState() => _YSVideoViewState();
  161. }
  162. class _YSVideoViewState extends State<YSVideoView> {
  163. VideoPlayerController _videoPlayerController;
  164. StateSetter _progressSet;
  165. String _startTime = '00:00:00';
  166. String _endTime = '00:00:00';
  167. @override
  168. void initState() {
  169. _videoPlayerController = VideoPlayerController.network(widget.url)
  170. ..addListener(() {
  171. Duration duration = _videoPlayerController.value.duration;
  172. Duration position = _videoPlayerController.value.position;
  173. _endTime = '${duration.inHours}'.padLeft(2,'0')+':'+'${duration.inMinutes}'.padLeft(2,'0')+':'+'${duration.inSeconds}'.padLeft(2,'0');
  174. _startTime = '${position.inHours}'.padLeft(2,'0')+':'+'${position.inMinutes}'.padLeft(2,'0')+':'+'${position.inSeconds}'.padLeft(2,'0');
  175. if(mounted&&_progressSet!=null){
  176. _progressSet(() {});
  177. }
  178. })
  179. ..initialize().then((_) {
  180. _videoPlayerController.setLooping(true);
  181. _videoPlayerController.play();
  182. setState(() {});
  183. });
  184. super.initState();
  185. }
  186. @override
  187. void dispose() {
  188. _videoPlayerController.dispose();
  189. super.dispose();
  190. }
  191. @override
  192. Widget build(BuildContext context) {
  193. return Container(
  194. child: Stack(
  195. children: [
  196. _videoPlayerController.value.aspectRatio<1?Center(
  197. child: AspectRatio(
  198. aspectRatio: _videoPlayerController.value.aspectRatio,
  199. child: VideoPlayer(_videoPlayerController,),
  200. ),
  201. ):VideoPlayer(_videoPlayerController,),
  202. _pauseView(controller: _videoPlayerController,),
  203. Positioned(
  204. left: 10,
  205. right: 10,
  206. top: (MediaQuery.of(context).size.height-50)*0.4-50,
  207. child: StatefulBuilder(
  208. builder: (context,progressSet){
  209. _progressSet = progressSet;
  210. return Container(
  211. child: Row(
  212. children: [
  213. Container(
  214. width: 100,
  215. child: Row(
  216. children: [
  217. GestureDetector(
  218. onTap: (){
  219. progressSet(() {
  220. _videoPlayerController.value.isPlaying?_videoPlayerController.pause() : _videoPlayerController.play();
  221. });
  222. progressSet(() {});
  223. },
  224. child: Icon(_videoPlayerController.value.isPlaying?Icons.pause:Icons.play_arrow,size: 25,color: Colors.white,),
  225. ),
  226. Container(
  227. alignment: Alignment.center,
  228. width: 75,
  229. child: Text(_startTime,style: TextStyle(fontSize: 10,color: Colors.white),),
  230. )
  231. ],
  232. ),
  233. ),
  234. Container(
  235. width: MediaQuery.of(context).size.width-230,
  236. height:6.5,
  237. child: VideoProgressIndicator(
  238. _videoPlayerController,
  239. allowScrubbing: true,
  240. colors: VideoProgressColors(playedColor: Colors.white,backgroundColor: Colors.black),
  241. )
  242. ),
  243. Container(
  244. width: 100,
  245. child: Row(
  246. children: [
  247. Container(
  248. alignment: Alignment.center,
  249. width: 75,
  250. child: Text(_endTime,style: TextStyle(fontSize: 12,color: Colors.white),),
  251. ),
  252. GestureDetector(
  253. onTap: (){
  254. Navigator.of(context).push(
  255. CupertinoPageRoute(builder: (context){
  256. return YSVideoHorizontal(player: _videoPlayerController,title: '',);
  257. })
  258. );
  259. },
  260. child: Image.asset('lib/images/spsx.png',height: 25,width: 25,),
  261. )
  262. ],
  263. )
  264. ),
  265. ],
  266. ),
  267. );
  268. },
  269. )
  270. )
  271. ],
  272. )
  273. );
  274. }
  275. _pauseView({VideoPlayerController controller}) {
  276. return StatefulBuilder(
  277. builder: (context,playSet){
  278. return Stack(
  279. children: <Widget>[
  280. AnimatedSwitcher(
  281. duration: Duration(milliseconds: 50),
  282. reverseDuration: Duration(milliseconds: 200),
  283. child: controller.value.isPlaying? Container():Container(
  284. alignment: Alignment.center,
  285. color: Colors.transparent,
  286. child: Icon(
  287. Icons.play_circle_filled,
  288. color: Colors.white,
  289. size: 50,
  290. ),
  291. ),
  292. ),
  293. GestureDetector(
  294. onTap: () {
  295. playSet(() {
  296. controller.value.isPlaying ? controller.pause() : controller.play();
  297. });
  298. _progressSet(() {});
  299. },
  300. ),
  301. ],
  302. );
  303. },
  304. );
  305. }
  306. }
  307. class YSInputView extends StatefulWidget {
  308. final ValueSetter valueSetter;
  309. const YSInputView({Key key, this.valueSetter}) : super(key: key);
  310. @override
  311. _YSInputViewState createState() => _YSInputViewState();
  312. }
  313. class _YSInputViewState extends State<YSInputView> {
  314. TextEditingController _editingController = TextEditingController();
  315. @override
  316. void dispose() {
  317. _editingController.dispose();
  318. super.dispose();
  319. }
  320. @override
  321. Widget build(BuildContext context) {
  322. return Container(
  323. height: 50,
  324. color: Colors.white,
  325. width: MediaQuery.of(context).size.width,
  326. padding: EdgeInsets.only(left: 20,right: 20,top: 10,bottom: 10),
  327. child: CupertinoTextField(
  328. autofocus: true,
  329. placeholder: '我来说两句',
  330. style: TextStyle(fontSize: 15,color: Color(0xFF707070)),
  331. placeholderStyle: TextStyle(fontSize: 15,color: Color(0xFF707070)),
  332. padding: EdgeInsets.only(left: 20,right: 20),
  333. controller: _editingController,
  334. decoration: BoxDecoration(
  335. color: Color(0xFFF5F3F0),
  336. borderRadius: BorderRadius.all(Radius.circular(50))
  337. ),
  338. textInputAction: TextInputAction.send,
  339. onSubmitted: (value){
  340. if(value.isNotEmpty){
  341. widget.valueSetter(value);
  342. _editingController.text = '';
  343. Navigator.pop(context);
  344. }
  345. },
  346. ),
  347. );
  348. }
  349. }
  350. class YSCommentView extends StatefulWidget {
  351. final videoId;
  352. const YSCommentView({Key key, this.videoId}) : super(key: key);
  353. @override
  354. _YSCommentViewState createState() => _YSCommentViewState();
  355. }
  356. class _YSCommentViewState extends State<YSCommentView> {
  357. List _commentArray = [];
  358. @override
  359. Widget build(BuildContext context) {
  360. return YSRefreshLoad(
  361. key: refreshKey2,
  362. dataWidget: SingleChildScrollView(
  363. child: ListView.separated(
  364. itemBuilder: (context,index){
  365. Map item = _commentArray[index];
  366. return Container(
  367. padding: EdgeInsets.only(top: 10,bottom: 10),
  368. child: Row(
  369. crossAxisAlignment: CrossAxisAlignment.start,
  370. children: [
  371. Container(
  372. height: 40,
  373. width: 40,
  374. decoration: BoxDecoration(
  375. color: Colors.grey,
  376. borderRadius: BorderRadius.all(Radius.circular(50)),
  377. image: DecorationImage(image: NetworkImage(item['user_avatar']),fit: BoxFit.cover)
  378. ),
  379. ),
  380. Container(
  381. width: MediaQuery.of(context).size.width-60,
  382. padding: EdgeInsets.only(left: 15),
  383. child: Column(
  384. crossAxisAlignment: CrossAxisAlignment.start,
  385. children: [
  386. Text(item['user_simple_name']??'',style: TextStyle(fontSize: 15,color: Color(0xFF3A3A3C),fontWeight: FontWeight.bold),maxLines: 1,),
  387. Container(
  388. padding: EdgeInsets.only(top: 8,bottom: 8),
  389. child: Text(item['body']??'',style: TextStyle(fontSize: 15,color: Color(0xFF707070)),maxLines: 1,),
  390. ),
  391. Text(item['created_at']??'',style: TextStyle(fontSize: 12,color: Color(0xFF707070)),maxLines: 1,),
  392. ],
  393. ),
  394. )
  395. ],
  396. ),
  397. );
  398. },
  399. separatorBuilder: (context,index){
  400. return Divider(color: Colors.grey,height: 0.1,thickness: 0.1,);
  401. },
  402. itemCount: _commentArray.length,
  403. padding: EdgeInsets.only(left: 10,right: 10),
  404. shrinkWrap: true,
  405. physics: NeverScrollableScrollPhysics(),
  406. ),
  407. ),
  408. url: 'train/video/comment',
  409. request: {'video_id':widget.videoId},
  410. postData: (value){
  411. _commentArray = value;
  412. setState(() {});
  413. },
  414. );
  415. }
  416. }
  417. class YSVideoHorizontal extends StatefulWidget {
  418. final VideoPlayerController player;
  419. final title;
  420. const YSVideoHorizontal({Key key, this.player, this.title}) : super(key: key);
  421. @override
  422. _YSVideoHorizontalState createState() => _YSVideoHorizontalState();
  423. }
  424. class _YSVideoHorizontalState extends State<YSVideoHorizontal> {
  425. var _playSet,_progressSet;
  426. String _startTime = '00:00:00';
  427. String _endTime = '00:00:00';
  428. @override
  429. void initState() {
  430. SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
  431. SystemChrome.setPreferredOrientations([
  432. DeviceOrientation.landscapeLeft, //全屏时旋转方向,左边
  433. ]);
  434. widget.player..addListener(() {
  435. Duration duration = widget.player.value.duration;
  436. Duration position = widget.player.value.position;
  437. _endTime = '${duration.inHours}'.padLeft(2,'0')+':'+'${duration.inMinutes}'.padLeft(2,'0')+':'+'${duration.inSeconds}'.padLeft(2,'0');
  438. _startTime = '${position.inHours}'.padLeft(2,'0')+':'+'${position.inMinutes}'.padLeft(2,'0')+':'+'${position.inSeconds}'.padLeft(2,'0');
  439. if(mounted&&_progressSet!=null){
  440. _progressSet(() {});
  441. }
  442. });
  443. super.initState();
  444. }
  445. @override
  446. void dispose() {
  447. widget.player..removeListener(() { });
  448. SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: SystemUiOverlay.values);
  449. SystemChrome.setPreferredOrientations([
  450. DeviceOrientation.portraitUp,
  451. ]);
  452. super.dispose();
  453. }
  454. @override
  455. Widget build(BuildContext context) {
  456. return Scaffold(
  457. backgroundColor: Colors.black,
  458. body: Container(
  459. height: MediaQuery.of(context).size.height,
  460. width: MediaQuery.of(context).size.width,
  461. child: Stack(
  462. children: <Widget>[
  463. widget.player.value.aspectRatio<1?Center(
  464. child: AspectRatio(
  465. aspectRatio: widget.player.value.aspectRatio,
  466. child: VideoPlayer(widget.player,),
  467. ),
  468. ):VideoPlayer(widget.player,),
  469. _pauseView(controller: widget.player,),
  470. Container(
  471. width: MediaQuery.of(context).size.width,
  472. height: 30,
  473. margin: EdgeInsets.only(left: 15,right: 35,top: MediaQuery.of(context).padding.top+10),
  474. child: Row(
  475. children: [
  476. GestureDetector(
  477. onTap: (){Navigator.pop(context);},
  478. child: Icon(Icons.chevron_left,size: 30,color: Colors.white,),
  479. ),
  480. Container(
  481. width: MediaQuery.of(context).size.width-80,
  482. child: Text('${widget.title}',style: TextStyle(fontSize: 16,color: Colors.white),),
  483. )
  484. ],
  485. ),
  486. ),
  487. StatefulBuilder(
  488. builder: (context,progressSet){
  489. _progressSet = progressSet;
  490. return Container(
  491. margin: EdgeInsets.only(top: MediaQuery.of(context).size.height-50,left: 15,right: 15),
  492. child: Row(
  493. children: [
  494. Container(
  495. width: 100,
  496. child: Row(
  497. children: [
  498. GestureDetector(
  499. onTap: (){
  500. _playSet(() {
  501. widget.player.value.isPlaying?widget.player.pause() : widget.player.play();
  502. });
  503. progressSet(() {});
  504. },
  505. child: Icon(widget.player.value.isPlaying?Icons.pause:Icons.play_arrow,size: 25,color: Colors.white,),
  506. ),
  507. Container(
  508. alignment: Alignment.center,
  509. width: 75,
  510. child: Text(_startTime,style: TextStyle(fontSize: 12,color: Colors.white),),
  511. )
  512. ],
  513. ),
  514. ),
  515. Container(
  516. width: MediaQuery.of(context).size.width-230,
  517. height:6.5,
  518. child: VideoProgressIndicator(
  519. widget.player,
  520. allowScrubbing: true,
  521. colors: VideoProgressColors(playedColor: Colors.white,backgroundColor: Colors.black),
  522. )
  523. ),
  524. Container(
  525. width: 100,
  526. child: Row(
  527. children: [
  528. Container(
  529. alignment: Alignment.center,
  530. width: 75,
  531. child: Text(_endTime,style: TextStyle(fontSize: 12,color: Colors.white),),
  532. ),
  533. GestureDetector(
  534. onTap: (){Navigator.pop(context);},
  535. child: Image.asset('lib/images/spsx.png',height: 25,width: 25,),
  536. )
  537. ],
  538. )
  539. ),
  540. ],
  541. ),
  542. );
  543. },
  544. ),
  545. ],
  546. ),
  547. )
  548. ,
  549. );
  550. }
  551. _pauseView({VideoPlayerController controller}) {
  552. return StatefulBuilder(
  553. builder: (context,playSet){
  554. _playSet = playSet;
  555. return Stack(
  556. children: <Widget>[
  557. AnimatedSwitcher(
  558. duration: Duration(milliseconds: 50),
  559. reverseDuration: Duration(milliseconds: 200),
  560. child: controller.value.isPlaying? Container():Container(
  561. alignment: Alignment.center,
  562. color: Colors.transparent,
  563. child: Icon(
  564. Icons.play_circle_filled,
  565. color: Colors.white,
  566. size: 50,
  567. ),
  568. ),
  569. ),
  570. GestureDetector(
  571. onTap: () {
  572. playSet(() {
  573. controller.value.isPlaying ? controller.pause() : controller.play();
  574. });
  575. _progressSet(() {});
  576. },
  577. ),
  578. ],
  579. );
  580. },
  581. );
  582. }
  583. }