nova_player 0.1.9
nova_player: ^0.1.9 copied to clipboard
A Flutter video player package with advanced analytics and interaction tracking capabilities.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:nova_player/nova_player.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Nova Player Example',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late final NovaPlayerController controller;
@override
void initState() {
super.initState();
controller = NovaPlayerController();
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Nova Player Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: AspectRatio(
aspectRatio: 16 / 9,
child: VideoPlayer(
appId: "oKBwIaa9rRXS5Hd7uR6Uvr5gXnAFXyDROWWfS/lrXOk=",
streamCode: "17472266444729",
userContext: {"user_id": "12190121"},
controller: controller,
controls: _MyCustomControls(controller: controller),
),
),
),
const SizedBox(height: 12),
_BottomTransportBar(controller: controller),
const SizedBox(height: 16),
],
),
),
);
}
}
class _MyCustomControls extends StatelessWidget {
final NovaPlayerController controller;
const _MyCustomControls({required this.controller});
@override
Widget build(BuildContext context) {
return Stack(
fit: StackFit.expand,
children: [
Positioned(
bottom: 12,
left: 12,
right: 12,
child: Row(
children: [
StreamBuilder<bool>(
stream: controller.isPlayingStream,
builder: (context, snapshot) {
final isPlaying = snapshot.data ?? false;
return IconButton(
onPressed: controller.togglePlay,
icon: Icon(isPlaying ? Icons.pause : Icons.play_arrow),
color: Colors.white,
);
},
),
const SizedBox(width: 8),
Expanded(
child: StreamBuilder<MediaProgress>(
stream: controller.mediaProgressStream,
builder: (context, snapshot) {
final progress = snapshot.data?.progress ?? Duration.zero;
final total = snapshot.data?.totalDuration ?? Duration.zero;
final max =
total.inSeconds == 0 ? 1.0 : total.inSeconds.toDouble();
return Row(
children: [
Text(_format(progress),
style: const TextStyle(color: Colors.white)),
const SizedBox(width: 8),
Expanded(
child: Slider(
value: progress.inSeconds
.clamp(0, max.toInt())
.toDouble(),
max: max,
onChanged: (v) =>
controller.seekTo(Duration(seconds: v.toInt())),
),
),
const SizedBox(width: 8),
Text(_format(total),
style: const TextStyle(color: Colors.white)),
],
);
},
),
),
const SizedBox(width: 8),
IconButton(
onPressed: () => controller.enterFullscreen(),
icon: const Icon(Icons.fullscreen),
color: Colors.white,
),
],
),
),
],
);
}
static String _format(Duration d) {
final two = (int n) => n.toString().padLeft(2, '0');
final h = d.inHours;
final m = d.inMinutes.remainder(60);
final s = d.inSeconds.remainder(60);
return h > 0 ? '${two(h)}:${two(m)}:${two(s)}' : '${two(m)}:${two(s)}';
}
}
class _BottomTransportBar extends StatelessWidget {
final NovaPlayerController controller;
const _BottomTransportBar({required this.controller});
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
onPressed: () =>
controller.seekRelative(const Duration(seconds: -10)),
icon: const Icon(Icons.replay_10),
),
const SizedBox(width: 8),
StreamBuilder<bool>(
stream: controller.isPlayingStream,
builder: (context, snap) {
final isPlaying = snap.data ?? false;
return ElevatedButton.icon(
onPressed: controller.togglePlay,
icon: Icon(isPlaying ? Icons.pause : Icons.play_arrow),
label: Text(isPlaying ? 'Pause' : 'Play'),
);
},
),
const SizedBox(width: 8),
IconButton(
onPressed: () => controller.seekRelative(const Duration(seconds: 10)),
icon: const Icon(Icons.forward_10),
),
const SizedBox(width: 8),
IconButton(
onPressed: () => controller.mute(),
icon: const Icon(Icons.volume_off),
),
const SizedBox(width: 8),
DropdownButton<double>(
value: 1.0,
underline: const SizedBox.shrink(),
items: const [0.5, 1.0, 1.5, 2.0]
.map((e) => DropdownMenuItem(value: e, child: Text('${e}x')))
.toList(),
onChanged: (v) => v != null ? controller.setPlaybackSpeed(v) : null,
)
],
);
}
}