|
|
|
import 'package:fijkplayer/fijkplayer.dart'; |
|
import 'package:flutter/material.dart'; |
|
import 'package:nft/services/safety/base_stateful.dart'; |
|
import 'package:nft/utils/app_extension.dart'; |
|
import 'package:nft/utils/app_style.dart'; |
|
|
|
/* |
|
/// PARENT'S WIDGET |
|
/// Controller, state |
|
final WM3u8Controller wm3u8controller = WM3u8Controller(); |
|
WM3u8State wm3u8state; |
|
|
|
@override |
|
Future<void> afterFirstBuild(BuildContext context) async { |
|
super.afterFirstBuild(context); |
|
wm3u8controller.onStateChange = (WM3u8State state) { |
|
print('onStateChange: $state'); |
|
setState(() { |
|
wm3u8state = state; |
|
}); |
|
}; |
|
} |
|
|
|
/// BUILD |
|
/// Stream container |
|
WM3u8Container( |
|
streamUrl: streamUrl, |
|
controller: wm3u8controller, |
|
), |
|
|
|
/// Refesh button |
|
if (wm3u8state == WM3u8State.error || |
|
wm3u8state == WM3u8State.completed) |
|
Container( |
|
alignment: Alignment.center, |
|
margin: EdgeInsets.only(top: 95.H), |
|
child: IconButton( |
|
icon: const Icon( |
|
Icons.refresh, |
|
color: Colors.white, |
|
), |
|
onPressed: () { |
|
wm3u8controller.onRestart(); |
|
}), |
|
) |
|
|
|
*/ |
|
|
|
enum WM3u8State { loading, started, paused, error, completed } |
|
|
|
class WM3u8Controller { |
|
WM3u8Controller(); |
|
|
|
// Implement in parent's widget |
|
Function(WM3u8State value) onStateChange = (_) {}; |
|
|
|
// Implement in widget |
|
Function() onRestart; |
|
FijkPlayer player; |
|
} |
|
|
|
class WM3u8Container extends StatefulWidget { |
|
const WM3u8Container({Key key, this.controller, this.streamUrl}) |
|
: super(key: key); |
|
|
|
final String streamUrl; |
|
final WM3u8Controller controller; |
|
|
|
@override |
|
_WM3u8ContainerState createState() => _WM3u8ContainerState(); |
|
} |
|
|
|
class _WM3u8ContainerState extends BaseStateful<WM3u8Container> { |
|
WM3u8Controller get controller => widget.controller; |
|
|
|
WM3u8State _state; |
|
|
|
set state(WM3u8State value) { |
|
controller.onStateChange(value); |
|
setState(() { |
|
_state = value; |
|
}); |
|
} |
|
|
|
@override |
|
void initState() { |
|
super.initState(); |
|
controller.onRestart = () { |
|
state = WM3u8State.loading; |
|
}; |
|
} |
|
|
|
@override |
|
void afterFirstBuild(BuildContext context) { |
|
super.afterFirstBuild(context); |
|
state = WM3u8State.loading; |
|
} |
|
|
|
@override |
|
Widget build(BuildContext context) { |
|
super.build(context); |
|
return _state == null |
|
? Container() |
|
: Stack( |
|
children: <Widget>[ |
|
if (_state != WM3u8State.error && _state != WM3u8State.completed) |
|
_WM3u8( |
|
streamUrl: widget.streamUrl, |
|
onStarted: () { |
|
state = WM3u8State.started; |
|
}, |
|
onPaused: () { |
|
state = WM3u8State.paused; |
|
}, |
|
onError: () { |
|
state = WM3u8State.error; |
|
}, |
|
onCompleted: () { |
|
state = WM3u8State.completed; |
|
}, |
|
controller: controller, |
|
), |
|
if (_state == WM3u8State.error || _state == WM3u8State.completed) |
|
Container( |
|
margin: EdgeInsets.symmetric(horizontal: 60.W), |
|
alignment: Alignment.center, |
|
child: Text( |
|
'THE STREAM IS NOT LIVE YET OR IT IS IDLE.\nWHEN IT\'S LIVE YOU\'LL SEE IT HERE', |
|
textAlign: TextAlign.center, |
|
style: normalTextStyle(20.SP, |
|
color: Colors.white, |
|
fontFamily: appTheme.assets.fontLeaguegothic), |
|
)), |
|
if (_state == WM3u8State.loading) _loadingView() |
|
], |
|
); |
|
} |
|
|
|
// Build loading |
|
Widget _loadingView() { |
|
return Container( |
|
color: Colors.black12, |
|
alignment: Alignment.center, |
|
child: SizedBox( |
|
height: 50.H, |
|
width: 50.H, |
|
child: const CircularProgressIndicator( |
|
backgroundColor: Colors.white, |
|
), |
|
), |
|
); |
|
} |
|
} |
|
|
|
class _WM3u8 extends StatefulWidget { |
|
const _WM3u8({ |
|
@required this.streamUrl, |
|
this.onStarted, |
|
this.onPaused, |
|
this.onError, |
|
this.onCompleted, |
|
this.controller, |
|
Key key, |
|
}) : super(key: key); |
|
|
|
final String streamUrl; |
|
final Function onStarted; |
|
final Function onPaused; |
|
final Function onError; |
|
final Function onCompleted; |
|
final WM3u8Controller controller; |
|
|
|
@override |
|
_WM3u8State createState() => _WM3u8State(); |
|
} |
|
|
|
class _WM3u8State extends BaseStateful<_WM3u8> { |
|
WM3u8Controller get controller => widget.controller; |
|
final FijkPlayer player = FijkPlayer(); |
|
|
|
@override |
|
void initState() { |
|
super.initState(); |
|
controller.player = player; |
|
} |
|
|
|
@override |
|
void afterFirstBuild(BuildContext context) { |
|
super.afterFirstBuild(context); |
|
player.addListener(() { |
|
final FijkState state = player.value.state; |
|
print('updatestate: $state'); |
|
if (state == FijkState.error && widget.onError != null) { |
|
widget.onError(); |
|
} else if (state == FijkState.started && widget.onStarted != null) { |
|
widget.onStarted(); |
|
} else if (state == FijkState.paused && widget.onPaused != null) { |
|
widget.onPaused(); |
|
} else if (state == FijkState.completed && widget.onCompleted != null) { |
|
widget.onCompleted(); |
|
} |
|
}); |
|
// Start player |
|
// https://fijkplayer.befovy.com/docs/en/fijkstate.html#gsc.tab=0 |
|
player.setDataSource(widget.streamUrl, autoPlay: true); |
|
} |
|
|
|
@override |
|
void dispose() { |
|
player.release().then((_) { |
|
player.dispose(); |
|
}); |
|
super.dispose(); |
|
} |
|
|
|
@override |
|
Widget build(BuildContext context) { |
|
super.build(context); |
|
return FijkView( |
|
player: player, |
|
color: Colors.black12, |
|
fit: (player.value?.size?.aspectRatio ?? 2) > 1 |
|
? FijkFit.contain |
|
: FijkFit.cover, |
|
); |
|
} |
|
} |