123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650 |
- import 'dart:async';
- import 'dart:convert';
- import 'dart:io';
- import 'package:flutter/cupertino.dart';
- import 'package:flutter/material.dart';
- import 'package:flutter/services.dart';
- import 'package:flutterappfuyou/code/live/view/YSLiveUserView.dart';
- import 'package:live_flutter_plugin/v2_tx_live_player.dart';
- import 'package:live_flutter_plugin/v2_tx_live_player_observer.dart';
- import 'package:live_flutter_plugin/widget/v2_tx_live_video_widget.dart';
- import 'package:wakelock/wakelock.dart';
- import '../base/YSNetWorking.dart';
- import '../base/YSTools.dart';
- import 'YSLiveAnchor.dart';
- import 'YSVideoDetail.dart';
- class YSLiveDetail extends StatefulWidget {
- final liveId;
- final String passWord;
- const YSLiveDetail({Key key, this.liveId, this.passWord = ''}) : super(key: key);
- @override
- _YSLiveDetailState createState() => _YSLiveDetailState();
- }
- class _YSLiveDetailState extends State<YSLiveDetail> with SingleTickerProviderStateMixin{
- V2TXLivePlayer _livePlayer;
- Map _infoDict = {};
- List _userArray = [];
- int _userCount = 0;
- bool _isMute = false;
- bool _isPause = false;
- WebSocket _socket;
- String _content = '';
- @override
- void initState() {
- Wakelock.enable();
- _createPlayer();
- Future.delayed(Duration(seconds: 0)).then((value) {
- _getVideoDetailData();
- });
- super.initState();
- }
- @override
- void dispose() {
- Wakelock.disable();
- _livePlayer.stopPlay();
- _livePlayer.destroy();
- super.dispose();
- }
- _createPlayer() async{
- _livePlayer = await V2TXLivePlayer.create();
- _livePlayer.addListener(onPlayerObserver);
- }
- /// Player observer
- onPlayerObserver(V2TXLivePlayerListenerType type, param) {
- debugPrint("==player listener type= ${type.toString()}");
- debugPrint("==player listener type= $param");
- }
- @override
- Widget build(BuildContext context) {
- return NotificationListener<CustomerValueNotification>(
- onNotification: (notification){
- Map data = notification.value;
- String type = data['type'];
- if(type=='headcount'){
- _userArray = data['value']['users']??[];
- _userCount = data['value']['total']??0;
- setState(() {});
- }else if(type=='mute'){
- _isMute = data['value']['MuteTime']==0;
- LogUtil.d('train/live2/info$_isMute');
- setState(() {});
- }else if(type=='pause'||type=='resume'){
- _isPause = type=='pause';
- setState(() {});
- }else if(type=='leave'){
- ysFlutterToast(context, '直播已结束');
- Navigator.of(context).pop('');
- }else if(type=='enter'){
- _livePlayer.startLivePlay(_infoDict['pull_url']);
- }else if(type=='staff'){
- List users = data['value']['users']??[];
- ysShowBottomAlertView(context, YSUsersAlertView(users: users,),isBarr: true);
- }
- return true;
- },
- child: AnnotatedRegion<SystemUiOverlayStyle>(
- value: SystemUiOverlayStyle.light,
- child: Scaffold(
- backgroundColor: Colors.black,
- body: SingleChildScrollView(
- child: Container(
- width: ysWidth(context),
- height: ysHeight(context),
- child: Stack(
- children: [
- Container(
- padding: EdgeInsets.only(top: ysTOP(context)+10),
- child: ClipRRect(
- borderRadius: BorderRadius.all(Radius.circular(10)),
- child: Container(
- width: ysWidth(context),
- height: ysHeight(context)-ysTOP(context)-70,
- child: Stack(
- children: [
- _infoDict.isNotEmpty?V2TXLiveVideoWidget(
- onViewCreated: (viewId) async {
- _livePlayer.setRenderViewID(viewId);
- },
- ):Image.asset('lib/images/图.png',fit: BoxFit.fill,width: ysWidth(context),height: ysHeight(context),),
- Container(
- width: ysWidth(context),
- height: ysHeight(context),
- padding: EdgeInsets.only(top: 10,left: 10,right: 10,bottom: 10),
- child: Column(
- children: [
- Container(
- height: 40,
- child: Row(
- children: [
- Container(
- width: (ysWidth(context)-20)*0.3,
- decoration: BoxDecoration(
- color: Colors.black,
- borderRadius: BorderRadius.all(Radius.circular(50))
- ),
- child: Row(
- children: [
- if(_infoDict.isNotEmpty)Container(
- height: 40,
- width: 40,
- decoration: BoxDecoration(
- borderRadius: BorderRadius.all(Radius.circular(50)),
- image: DecorationImage(image: NetworkImage(_infoDict['cast_avatar']))
- ),
- ),
- Container(
- width: (ysWidth(context)-20)*0.3-40,
- padding: EdgeInsets.only(left: 5,right: 5),
- child: Text(_infoDict['cast_name']??'',style: TextStyle(fontSize: 10,color: Colors.white,fontWeight: FontWeight.bold,),
- maxLines: 1,overflow: TextOverflow.ellipsis,),
- )
- ],
- ),
- ),
- GestureDetector(
- onTap: (){
- if(_socket==null)return;
- _socket.add(jsonEncode({'type':'staff'}));
- // Navigator.of(context).push(
- // CupertinoPageRoute(builder: (context){
- // return YSLiveUser();
- // })
- // );
- },
- behavior: HitTestBehavior.opaque,
- child: Row(
- children: [
- Container(
- width: (ysWidth(context)-20)*0.55,
- padding: EdgeInsets.only(top: 5,bottom: 5,left: 10),
- child: ListView.builder(
- itemBuilder: (context,index){
- Map item = _userArray[index];
- return Container(
- height: 30,
- width: 30,
- decoration: BoxDecoration(
- borderRadius: BorderRadius.all(Radius.circular(50)),
- color: Colors.white,
- image: DecorationImage(image: NetworkImage(item['avatar']),fit: BoxFit.cover)
- ),
- );
- },
- itemCount: _userArray.length,
- scrollDirection: Axis.horizontal,
- ),
- ),
- Container(
- width: (ysWidth(context)-20)*0.15,
- decoration: BoxDecoration(
- color: Colors.black,
- borderRadius: BorderRadius.all(Radius.circular(50))
- ),
- alignment: Alignment.center,
- child: Text('$_userCount',style: TextStyle(fontSize: 12,color: Colors.white),),
- )
- ],
- ),
- )
- ],
- ),
- ),
- ],
- ),
- ),
- if(_isPause)Center(
- child: Icon(Icons.play_circle_fill,size: 100,color: Colors.white,),
- ),
- if(_infoDict.isNotEmpty)Positioned(
- bottom: 20,
- left: 10,
- child:YSTalkView(
- postSocket: (socket){
- _socket = socket;
- },
- )
- ),
- if(_isPause)Center(
- child: Icon(Icons.play_circle_fill,size: 100,color: Colors.white,),
- ),
- ],
- ),
- ),
- ),
- ),
- Positioned(
- bottom: 10,
- left: 10,
- right: 10,
- child: Container(
- margin: EdgeInsets.only(top: 10,bottom: 10),
- height: 30,
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- GestureDetector(
- onTap: (){
- if(_isMute){
- ysFlutterToast(context, '直播间禁止发言');
- return;
- }
- ysShowBottomAlertView2(context, YSInputView(valueSetter: (value) async{
- User.instance.content = value;
- _socket.add(jsonEncode({'type':'message','content':value}));
- },));
- },
- child: Container(
- width: (ysWidth(context)-20)*0.6,
- height: 30,
- decoration: BoxDecoration(
- borderRadius: BorderRadius.all(Radius.circular(50)),
- border: Border.all(color: Colors.white,width: 1)
- ),
- child: Text('说点什么',style: TextStyle(fontSize: 13,color: Colors.white),),
- alignment: Alignment.centerLeft,
- padding: EdgeInsets.only(left: 15,right: 15),
- ),
- ),
- GestureDetector(
- onTap: (){
- Navigator.pop(context);
- },
- child: Icon(Icons.close,size: 30,color: Colors.white,)
- )
- ],
- ),
- )
- ),
- ],
- ),
- ),
- ),
- ),
- ),
- );
- }
- _getVideoDetailData() async{
- Map request = {'live_id':widget.liveId};
- if(widget.passWord.isNotEmpty)request['password'] = widget.passWord;
- Map dict = await ysRequestHttp(context, requestType.get, 'train/live2/info', request);
- if(dict!=null){
- _infoDict = dict['data'];
- _livePlayer.startLivePlay(_infoDict['pull_url']);
- User().stream = _infoDict['live_stream']??'';
- setState(() {});
- }
- }
- }
- class YSLiveInfoView extends StatefulWidget {
- const YSLiveInfoView({Key key}) : super(key: key);
- @override
- _YSLiveInfoViewState createState() => _YSLiveInfoViewState();
- }
- class _YSLiveInfoViewState extends State<YSLiveInfoView> {
- @override
- Widget build(BuildContext context) {
- return Align(
- alignment: Alignment.topCenter,
- child: Container(
- width: MediaQuery.of(context).size.width-20,
- decoration: BoxDecoration(
- color: Colors.white,
- borderRadius: BorderRadius.all(Radius.circular(5))
- ),
- margin: EdgeInsets.only(top: 20,left: 10,right: 10),
- padding: EdgeInsets.only(top: 20,left: 10,right: 10,bottom: 20),
- child: Text('简介',style: TextStyle(fontSize: 14,color: Colors.black),),
- ),
- );
- }
- }
- class YSCommentView2 extends StatefulWidget {
- final double height;
- const YSCommentView2({Key key, this.height = 0}) : super(key: key);
- @override
- _YSCommentView2State createState() => _YSCommentView2State();
- }
- class _YSCommentView2State extends State<YSCommentView2> {
- List _dataArray = [];
- static WebSocket _socket;
- Timer _timer;
- String _content = '';
- bool _isMute = false;
- @override
- void initState() {
- User().isAnchor = false;
- _getSocket();
- super.initState();
- }
- @override
- void dispose() {
- _socket.close();
- if(_timer!=null){
- if(_timer.isActive)_timer.cancel();
- }
- super.dispose();
- }
- _getSocket() async{
- WebSocket.connect('wss://v2fy.niwoshenghuo.com/websocket').then((socket) {
- LogUtil.d('WebSocket connect');
- _socket = socket;
- socket.listen(_onData, cancelOnError: false);
- _timer =Timer.periodic(Duration(seconds: 50), (timer) {
- _socket.add('heartbeat');
- });
- _socket.add(jsonEncode({'type':'headcount'}));
- }).catchError((e){
- LogUtil.d("Unable to connect: $e");
- _getSocket(); // 连接超时,重新建立连接
- });
- }
- _onData(event) async{
- Map dict = jsonDecode(event);
- LogUtil.d("---onData---$dict");
- if(dict['type']=='connection'){
- Map data = dict['data'];
- _dataArray.add({'type':3,'content':data['content']});
- setState(() {});
- Map message = {};
- message['type'] = 'bind';
- message['live_stream'] = User().stream;
- message['uid'] = data['uid'];
- message['user'] = {'username':User().name,'avatar':User().avatar,'is_owner':User().isAnchor};
- _socket.add(jsonEncode(message));
- }else if(dict['type']=='message'){
- var data = dict['data'];
- if(data is List){
- _dataArray.add({'type':2,'content':_content,'name':User().name,'avatar':User().avatar});
- }else{
- Map user = data['user'];
- _dataArray.add({'type':1,'content':data['content'],'name':user['username'],'avatar':user['avatar']});
- }
- setState(() {});
- }else if(dict['type']=='headcount'||dict['type']=='staff'||dict['type']=='mute'||dict['type']=='pause'||dict['type']=='resume'){
- var data = dict['data'];
- if(dict['type']=='mute'){
- _isMute = data['MuteTime']==0;
- LogUtil.d('MuteTime========$_isMute');
- setState(() {});
- }
- }
- }
- @override
- Widget build(BuildContext context) {
- return Column(
- children: [
- Container(
- height: widget.height,
- child: ListView.separated(
- padding: EdgeInsets.only(top: 20,bottom: 20,left: 20,right: 20),
- itemBuilder: (context,index){
- Map item = _dataArray[index];
- return LayoutBuilder(builder: (context,conSize){
- return item['type']==3?Container(
- child: Text(item['content'],style: TextStyle(fontSize: 13,color: Colors.orange),),
- ):item['type']==1?Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Row(
- children: [
- Container(
- height: 35,
- width: 35,
- decoration: BoxDecoration(
- color: Colors.white,
- image: DecorationImage(image: NetworkImage(item['avatar'])),
- borderRadius: BorderRadius.all(Radius.circular(50))
- ),
- ),
- Container(
- width: conSize.maxWidth-35,
- padding: EdgeInsets.only(left: 8),
- child: Text(item['name']??'',style: TextStyle(fontSize: 13,color: Color(0xFF575757)),),
- )
- ],
- ),
- Container(
- margin: EdgeInsets.only(left: 45,top: 5),
- padding: EdgeInsets.all(10),
- decoration: BoxDecoration(
- color: Colors.white,
- borderRadius: BorderRadius.all(Radius.circular(5))
- ),
- child: Text(item['content']??'',style: TextStyle(fontSize: 13,color: Color(0xFF151515)),),
- )
- ],
- ):Column(
- crossAxisAlignment: CrossAxisAlignment.end,
- children: [
- Row(
- children: [
- Container(
- width: conSize.maxWidth-35,
- padding: EdgeInsets.only(right: 8),
- child: Text(item['name']??'',style: TextStyle(fontSize: 13,color: Color(0xFF575757)),),
- alignment: Alignment.centerRight,
- ),
- Container(
- height: 35,
- width: 35,
- decoration: BoxDecoration(
- color: Colors.white,
- image: DecorationImage(image: NetworkImage(item['avatar'])),
- borderRadius: BorderRadius.all(Radius.circular(50))
- ),
- ),
- ],
- ),
- Container(
- margin: EdgeInsets.only(right: 45,top: 5),
- padding: EdgeInsets.all(10),
- decoration: BoxDecoration(
- color: Color(0xFFE7688C),
- borderRadius: BorderRadius.all(Radius.circular(5))
- ),
- child: Text(item['content']??'',style: TextStyle(fontSize: 13,color: Colors.white),),
- )
- ],
- );
- });
- },
- separatorBuilder: (context,index){
- return Container(height: 10,);
- },
- itemCount: _dataArray.length
- ),
- ),
- Container(
- height: 50,
- color: Colors.white,
- width: MediaQuery.of(context).size.width,
- padding: EdgeInsets.only(left: 20,right: 20,top: 10,bottom: 10),
- child: GestureDetector(
- onTap: (){
- if(_isMute){
- ysFlutterToast(context, '直播间禁止发言');
- return;
- }
- ysShowBottomAlertView2(context, YSInputView(valueSetter: (value) async{
- _content = value;
- _socket.add(jsonEncode({'type':'message','content':value}));
- },));
- },
- child: Container(
- alignment: Alignment.centerLeft,
- padding: EdgeInsets.only(left: 20,right: 20),
- child: Text('请输入您要发送的内容',style: TextStyle(fontSize: 15,color: Color(0xFF707070)),),
- decoration: BoxDecoration(
- color: Color(0xFFF5F3F0),
- borderRadius: BorderRadius.all(Radius.circular(5))
- ),
- ),
- ),
- )
- ],
- );
- }
- }
- class YSLiveDetail2 extends StatefulWidget {
- final liveId;
- final String passWord;
- const YSLiveDetail2({Key key, this.liveId, this.passWord}) : super(key: key);
- @override
- _YSLiveDetail2State createState() => _YSLiveDetail2State();
- }
- class _YSLiveDetail2State extends State<YSLiveDetail2> with SingleTickerProviderStateMixin{
- V2TXLivePlayer _livePlayer;
- Map _infoDict = {};
- List _titles = ['简介','讨论'];
- TabController _tabController;
- int _index = 0;
- StateSetter _tabSet;
- @override
- void initState() {
- Wakelock.enable();
- _tabController = TabController(
- vsync: this,
- length: _titles.length
- )..addListener(() {
- _index = _tabController.index;
- _tabSet(() {});
- });
- _createPlayer();
- Future.delayed(Duration(seconds: 0)).then((value) {
- _getVideoDetailData();
- });
- super.initState();
- }
- _createPlayer() async{
- _livePlayer = await V2TXLivePlayer.create();
- _livePlayer.addListener(onPlayerObserver);
- }
- /// Player observer
- onPlayerObserver(V2TXLivePlayerListenerType type, param) {
- }
- @override
- void dispose() {
- Wakelock.disable();
- super.dispose();
- }
- @override
- void deactivate() {
- _livePlayer?.removeListener(onPlayerObserver);
- _livePlayer?.stopPlay();
- _livePlayer?.destroy();
- super.deactivate();
- }
- @override
- Widget build(BuildContext context) {
- return AnnotatedRegion<SystemUiOverlayStyle>(
- value: SystemUiOverlayStyle.light,
- child: Scaffold(
- body: SingleChildScrollView(
- child: Column(
- children: [
- Container(
- height: MediaQuery.of(context).size.height,
- width: MediaQuery.of(context).size.width,
- color: Color(0xFFF5F3F0),
- child: LayoutBuilder(
- builder: (BuildContext context, BoxConstraints constraints) {
- return Column(
- children: [
- Container(
- height: constraints.maxHeight*0.4,
- color: Colors.black,
- child: V2TXLiveVideoWidget(
- onViewCreated: (viewId) async {
- _livePlayer.setRenderViewID(viewId);
- },
- ),
- ),
- Container(
- height: constraints.maxHeight*0.6,
- child: DefaultTabController(
- length: _titles.length,
- child: Column(
- children: [
- Container(
- width: ysWidth(context),
- height: 50,
- child: StatefulBuilder(
- builder: (context,setState){
- _tabSet = setState;
- return TabBar(
- controller: _tabController,
- indicatorColor: Color(0xFFFA4444),
- labelColor: Color(0xFFFA4444),
- indicatorWeight: 2,
- indicatorPadding: EdgeInsets.all(0),
- labelPadding: EdgeInsets.all(0),
- indicatorSize: TabBarIndicatorSize.label,
- labelStyle: TextStyle(fontSize: 15),
- unselectedLabelColor: Color(0xFF5D6978),
- tabs: _titles.map((f) {
- return Container(
- color: _titles[_index]==f?Colors.transparent:Colors.white,
- alignment: Alignment.center,
- child: Tab(text: '$f'),
- width: ysWidth(context)/2,
- );
- }).toList(),
- );
- },
- ),
- ),
- Container(
- height: constraints.maxHeight*0.6-50,
- child: TabBarView(
- controller: _tabController,
- children: _titles.map((f) {
- return f=='简介'?YSLiveInfoView():_infoDict.isNotEmpty?YSCommentView2(
- height: constraints.maxHeight*0.6-100
- ):Container();
- }).toList(),
- )
- ),
- ],
- ),
- ),
- )
- ],
- );
- },
- )
- ),
- ],
- ),
- ),
- ),
- );
- }
- _getVideoDetailData() async{
- Map request = {'live_id':widget.liveId};
- if(widget.passWord.isNotEmpty)request['password'] = widget.passWord;
- Map dict = await ysRequestHttp(context, requestType.get, 'train/live2/info', request);
- if(dict!=null){
- _infoDict = dict['data'];
- _livePlayer.startLivePlay(_infoDict['pull_url']);
- User().stream = _infoDict['live_stream']??'';
- setState(() {});
- }
- }
- }
|