Nova Player
Nova Player is a Flutter video player with built-in analytics and a clean custom-controls API. It’s backed by xstream_player
and uses a BLoC/repository architecture internally, but exposes a simple public surface so you don’t have to depend on the internals.
What is visible to you (public API)
VideoPlayer
widget for playbackNovaPlayerController
for custom controls (streams + commands)- Streams: player state, media progress, interactions, and convenience helpers
What’s implemented internally (not public)
- BLoCs, repositories, data sources
- Analytics wiring and collectors
- BetterPlayer internals (a read-only controller is available via
rawController
for advanced use)
Installation
Add to your pubspec.yaml
:
dependencies:
nova_player: ^0.1.0
Import:
import 'package:nova_player/nova_player.dart';
Quick start
Default controls
VideoPlayer(
appId: 'YOUR_APP_ID',
streamCode: 'YOUR_STREAM_CODE',
userContext: {'user_id': '123'}, // optional
)
Custom controls (recommended)
Use NovaPlayerController
as your single source of truth for streams and commands. Pass the same controller to the VideoPlayer
and to your custom overlay.
final controller = NovaPlayerController();
VideoPlayer(
appId: 'YOUR_APP_ID',
streamCode: 'YOUR_STREAM_CODE',
controller: controller,
controls: MyControls(controller: controller),
)
Minimal custom controls example:
class MyControls extends StatelessWidget {
final NovaPlayerController controller;
const MyControls({super.key, required this.controller});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(12),
alignment: Alignment.bottomCenter,
child: Row(
children: [
StreamBuilder<bool>(
stream: controller.isPlayingStream,
builder: (_, s) => IconButton(
icon: Icon((s.data ?? false) ? Icons.pause : Icons.play_arrow),
onPressed: controller.togglePlay,
),
),
const SizedBox(width: 8),
Expanded(
child: StreamBuilder<MediaProgress>(
stream: controller.mediaProgressStream,
builder: (_, snap) {
final p = snap.data?.progress ?? Duration.zero;
final d = snap.data?.totalDuration ?? Duration.zero;
final max = d.inSeconds == 0 ? 1.0 : d.inSeconds.toDouble();
return Slider(
value: p.inSeconds.clamp(0, max.toInt()).toDouble(),
max: max,
onChanged: (v) => controller.seekTo(Duration(seconds: v.toInt())),
);
},
),
),
IconButton(
onPressed: controller.enterFullscreen,
icon: const Icon(Icons.fullscreen),
),
],
),
);
}
}
See a full example in example/lib/main.dart
.
Public API
Widgets
VideoPlayer({required String appId, required String streamCode, Map<String, dynamic>? userContext, Widget? controls, NovaPlayerController? controller})
- Default controls are shown if
controls
is not provided. - To build custom controls, pass a
NovaPlayerController
and your customcontrols
widget.
- Default controls are shown if
Controller: NovaPlayerController
Streams (broadcast):
playerStateStream
:Stream<VideoPlayerState>
mediaProgressStream
:Stream<MediaProgress>
interactionStream
:Stream<PlayerInteraction>
videoValueStream
:Stream<VideoPlayerValue>
- Convenience:
isPlayingStream
,positionStream
,durationStream
Commands:
play()
,pause()
,togglePlay()
seekTo(Duration)
,seekRelative(Duration)
setVolume(double)
,mute()
,unmute()
toggleLooping()
setPlaybackSpeed(double)
selectTrack(BetterPlayerAsmsTrack)
enterFullscreen()
,exitFullscreen()
reset()
postInteraction(PlayerInteraction)
Advanced:
rawController
:BetterPlayerController
(use sparingly; API may change)
Notes:
- The controller is attached automatically by the player when provided.
- Internal BLoCs are not part of the public API.
Analytics
Analytics and interaction tracking are built in. You can observe interactions via interactionStream
or emit your own via postInteraction
from custom controls.
Platform support
- Android, iOS (uses
xstream_player
under the hood)
Contributing
Issues and PRs are welcome.
License
MIT © Nova Player Contributors