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)
VideoPlayerwidget for playbackNovaPlayerControllerfor 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
rawControllerfor 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
controlsis not provided. - To build custom controls, pass a
NovaPlayerControllerand your customcontrolswidget.
- 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_playerunder the hood)
Contributing
Issues and PRs are welcome.
License
MIT © Nova Player Contributors