Monitor Better Player (Flutter)
The FastPix Better Player Wrapper is a Flutter plugin that combines the power of BetterPlayer Plus with FastPix Video Data. It allows you to track granular playback metrics, measure viewer engagement, and monitor performance - all while preserving the native flexibility of BetterPlayer.
If you're building a mobile video experience in Flutter and want accurate playback data without writing manual instrumentation, this wrapper takes care of that in a clean, extensible way.
Installation & Setup
Step 1: Add the SDK Dependency
You can install the FastPix Better Player Wrapper in one of two ways:
Option A: Using the terminal
flutter pub add fastpix_better_player_wrapper
Option B: Add the dependency directly
Alternatively, you can add the dependency directly in your pubspec.yaml
:
dependencies:
fastpix_better_player_wrapper: 0.0.1
better_player_plus: ^1.0.8
Then run:
flutter pub get
PLEASE NOTE
If you’re behind a proxy, or using a CI/CD runner, confirm that the SDK is being correctly fetched and cached. You can always verify installation by running
flutter pub deps
.
Step 2: Platform configuration
FastPix Better Player Wrapper doesn’t require any additional configuration for iOS or Android.
- Android: Auto-configured by the plugin
- iOS: Auto-configured by the plugin
- No need to edit native files or add permissions manually - you're good to go right after installation.
Basic usage
Step 1: Import the SDK
Now that the SDK is installed, you need to import it into any Dart file where you want to use FastPix player components. Typically, this will be in a page or file where you’ll implement video playback features - like your main.dart
or any feature module.
At the very top of the file, add the import statement:
// Import FastPix Better Player Wrapper SDK into Dart files
import 'package:fastpix_better_player_wrapper/fastpix_better_player_wrapper.dart';
This import statement ensures that all the classes, methods, and functionalities provided by the FastPix Better Player Wrapper SDK are available for use in your project.
Step 2: Initialize BetterPlayer
Once the SDK is imported, you can begin working with its APIs. This includes initializing the player, configuring playback options, and handling video streams.
class VideoPlayerScreen extends StatefulWidget {
@override
_VideoPlayerScreenState createState() => _VideoPlayerScreenState();
}
class _VideoPlayerScreenState extends State<VideoPlayerScreen> {
late BetterPlayerController _betterPlayerController;
late FastPixBaseBetterPlayer _fastPixPlayer;
@override
void initState() {
super.initState();
_initializePlayer();
}
void _initializePlayer() {
_betterPlayerController = BetterPlayerController(
BetterPlayerConfiguration(
autoPlay: true,
looping: false,
aspectRatio: 16 / 9,
),
betterPlayerDataSource: BetterPlayerDataSource(
BetterPlayerDataSourceType.network,
"https://example.com/video.mp4",
),
);
// Create FastPix wrapper
_fastPixPlayer = FastPixBaseVideoPlayerBuilder(
playerController: _betterPlayerController,
workspaceId: "your_workspace_id", // Required
beaconUrl: "https://your-beacon-url.com",
viewerId: "unique_viewer_id", // Required
)
.setVideoData(
VideoData(
videoUrl: "https://example.com/video.mp4",
videoThumbnailUrl: "https://example.com/thumbnail.jpg",
),
)
.setEnabledLogging(true) // Optional: for debugging
.build();
_fastPixPlayer.start();
}
Step 3: Build the UI
Use the standard BetterPlayer
widget to render the video.
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Video Player')),
body: Column(
children: [
AspectRatio(
aspectRatio: 16 / 9,
child: BetterPlayer(controller: _betterPlayerController),
),
],
),
);
}
Step 4: Clean up resources
To avoid memory leaks and dangling analytics sessions, dispose of the FastPix wrapper when the player is no longer in use.
@override
void dispose() {
// Dispose analytics and player controller
_fastPixPlayer.disposeMetrix();
super.dispose();
}
Advanced configuration
The FastPix Better Player Wrapper supports custom metadata so you can enrich your video data with detailed context about videos, players, and user sessions.
Adding custom metadata
You can send custom tags, player details, and video attributes directly into FastPix analytics.
// Define custom analytics data
List<CustomData> customData = [
CustomData(value: "entertainment"),
CustomData(value: "action"),
CustomData(value: "PG-13"),
CustomData(value: "English"),
];
// Player metadata
PlayerData playerData = PlayerData(
playerName: "CustomVideoPlayer",
playerVersion: "2.1.0",
playerType: "mobile",
);
// Video metadata
VideoData videoData = VideoData(
videoUrl: "https://example.com/video.mp4",
videoThumbnailUrl: "https://example.com/thumbnail.jpg",
videoTitle: "Sample Video",
videoDuration: 120000, // in milliseconds
);
// Build with advanced configuration
_fastPixPlayer = FastPixBaseVideoPlayerBuilder(
playerController: _betterPlayerController,
workspaceId: "your_workspace_id",
beaconUrl: "https://your-beacon-url.com",
viewerId: "unique_viewer_id",
)
.setVideoData(videoData)
.setPlayerData(playerData)
.setCustomData(customData)
.setEnabledLogging(true)
.build();
Player Dimension Tracking
For player layout insights, you can report player size using a `GlobalKey`.
class _VideoPlayerScreenState extends State<VideoPlayerScreen> {
final GlobalKey _playerKey = GlobalKey();
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
AspectRatio(
aspectRatio: 16 / 9,
child: Container(
key: _playerKey, // Add key for dimension tracking
child: BetterPlayer(controller: _betterPlayerController),
),
),
],
),
);
}
@override
void initState() {
super.initState();
_initializePlayer();
// Report player dimensions for analytics
WidgetsBinding.instance.addPostFrameCallback((_) {
_fastPixPlayer.reportPlayerSize(_playerKey);
});
}
}
Event tracking & analytics
The SDK automatically tracks standard playback events without additional code.
Event | Description | When it occurs |
---|---|---|
play | Video starts playing | User presses play or autoplay triggers |
playing | Active playback | Video is actively playing |
pause | Video paused | User pauses or video is interrupted |
seeking | Seeking operation started | User seeks to different position |
seeked | Seeking completed | Seek operation finishes |
buffering | Video buffering | Insufficient data for playback |
buffered | Buffering completed | Enough data available |
variantChanged | Quality/resolution change | Adaptive bitrate changes |
ended | Video completed | Video reaches end |
error | Playback error | Any playback failure |
Builder methods
FastPixBaseVideoPlayerBuilder
: Used to configure and create a FastPix-wrapped player instance.
FastPixBaseVideoPlayerBuilder({
required BetterPlayerController playerController,
required String workspaceId,
String? beaconUrl,
required String viewerId,
})
Configuration methods for customizing player behavior, metadata, and output:
Function | Parameters | Return Type | Description |
---|---|---|---|
setEnabledLogging(bool value) | bool value | FastPixBaseVideoPlayerBuilder | Enable/disable detailed logging |
setCustomData(List<CustomData> value) | List<CustomData> value | FastPixBaseVideoPlayerBuilder | Set custom analytics data |
setPlayerData(PlayerData? value) | PlayerData? value | FastPixBaseVideoPlayerBuilder | Set player metadata |
setVideoData(VideoData? value) | VideoData? value | FastPixBaseVideoPlayerBuilder | Set video information |
build() | None | FastPixBaseBetterPlayer | Create the configured player instance |
Player instance methods
FastPixBaseBetterPlayer
once built, the player provides lifecycle utilities:
Function | Parameters | Return Type | Description |
---|---|---|---|
start() | None | void | Initialize and start analytics tracking |
disposeMetrix() | None | Future<void> | Clean up analytics resources and player |
reportPlayerSize(GlobalKey key) | GlobalKey key | void | Report player dimensions for analytics |
Properties:
Property | Type | Description |
---|---|---|
audioLanguage | String | Current audio track language |
playerWidthSize | double | Current player width |
playerHeightSize | double | Current player height |
PlayerObserver implementation
The class implements PlayerObserver
interface with these methods:
Function | Return Type | Description |
---|---|---|
isPlayerAutoPlayOn() | bool | Check if autoplay is enabled |
isPlayerFullScreen() | bool | Check if player is in fullscreen |
isPlayerPaused() | bool | Check if player is paused |
isVideoSourceLive() | bool | Check if video is live stream |
playerHeight() | double | Get current player height |
playerWidth() | double | Get current player width |
playerLanguageCode() | String | Get current audio language |
playerPlayHeadTime() | Future<int> | Get current playback position |
videoSourceDuration() | int | Get total video duration |
videoSourceUrl() | String | Get video source URL |
videoSourceMimeType() | String | Get video MIME type |
Error reporting
The SDK automatically captures and reports error details through ErrorModel
:
// Check for player errors
ErrorModel? error = _fastPixPlayer.getPlayerError();
if (error != null) {
print('Player error: ${error.message}');
print('Error source: ${error.source}');
}
Best practices
1. Resource management
Handles resource cleanup by disposing the FastPix wrapper before the controller.
@override
void dispose() {
// Always dispose FastPix wrapper before disposing controller
_fastPixPlayer.disposeMetrix();
super.dispose();
}
2. Error handling
Wraps player initialization in a try–catch block to handle setup errors gracefully.
void _initializePlayer() {
try {
_fastPixPlayer = FastPixBaseVideoPlayerBuilder(
// ... configuration
).build();
_fastPixPlayer.start();
} catch (e) {
print('Failed to initialize FastPix player: $e');
// Handle initialization error
}
}
3. Configuration validation
Ensures required IDs and video URLs are valid before building the player.
// Validate required parameters
if (workspaceId.isEmpty || viewerId.isEmpty) {
throw ArgumentError('workspaceId and viewerId are required');
}
// Validate video data
if (videoData?.videoUrl.isEmpty ?? true) {
throw ArgumentError('Valid video URL is required');
}
4. Performance optimization
Applies settings to optimize performance and analytics, such as conditional logging and meaningful identifiers.
// Enable logging only in debug mode
.setEnabledLogging(kDebugMode)
// Use meaningful viewer IDs for analytics
.viewerId: "user_${userId}_session_${sessionId}"
Complete integration example
Below is a complete example showing how to integrate FastPix Better Player Wrapper in a Flutter application:
import 'package:flutter/material.dart';
import 'package:better_player_plus/better_player_plus.dart';
import 'package:fastpix_better_player_wrapper/fastpix_better_player_wrapper.dart';
class VideoPlayerPage extends StatefulWidget {
final String videoUrl;
final String videoTitle;
final String workspaceId;
final String viewerId;
const VideoPlayerPage({
Key? key,
required this.videoUrl,
required this.videoTitle,
required this.workspaceId,
required this.viewerId,
}) : super(key: key);
@override
_VideoPlayerPageState createState() => _VideoPlayerPageState();
}
class _VideoPlayerPageState extends State<VideoPlayerPage> {
late BetterPlayerController _betterPlayerController;
late FastPixBaseBetterPlayer _fastPixPlayer;
final GlobalKey _playerKey = GlobalKey();
bool _isLoading = true;
String? _errorMessage;
@override
void initState() {
super.initState();
_initializePlayer();
}
void _initializePlayer() async {
try {
setState(() {
_isLoading = true;
_errorMessage = null;
});
// Initialize BetterPlayer controller
_betterPlayerController = BetterPlayerController(
BetterPlayerConfiguration(
autoPlay: true,
looping: false,
aspectRatio: 16 / 9,
fit: BoxFit.contain,
controlsEnabled: true,
showPlaceholderUntilPlay: true,
placeholder: Container(
color: Colors.black,
child: const Center(
child: CircularProgressIndicator(color: Colors.white),
),
),
),
betterPlayerDataSource: BetterPlayerDataSource(
BetterPlayerDataSourceType.network,
widget.videoUrl,
),
);
// Configure video metadata
VideoData videoData = VideoData(
videoUrl: widget.videoUrl,
videoTitle: widget.videoTitle,
videoThumbnailUrl: "https://example.com/thumbnail.jpg",
videoDuration: 0, // Will be auto-detected
);
// Configure player metadata
PlayerData playerData = PlayerData(
playerName: "FastPixVideoPlayer",
playerVersion: "1.0.0",
playerType: "mobile",
);
// Configure custom analytics data
List<CustomData> customData = [
CustomData(value: "entertainment"),
CustomData(value: "movie"),
CustomData(value: "HD"),
];
// Build FastPix player with analytics
_fastPixPlayer = FastPixBaseVideoPlayerBuilder(
playerController: _betterPlayerController,
workspaceId: widget.workspaceId,
beaconUrl: "https://your-beacon-url.com", // Replace with your beacon URL
viewerId: widget.viewerId,
)
.setVideoData(videoData)
.setPlayerData(playerData)
.setCustomData(customData)
.setEnabledLogging(kDebugMode) // Enable logging in debug mode
.build();
// Start analytics tracking
_fastPixPlayer.start();
// Report player dimensions after widget is built
WidgetsBinding.instance.addPostFrameCallback((_) {
_fastPixPlayer.reportPlayerSize(_playerKey);
});
setState(() {
_isLoading = false;
});
} catch (e) {
setState(() {
_isLoading = false;
_errorMessage = 'Failed to initialize player: $e';
});
print('Player initialization error: $e');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.videoTitle),
backgroundColor: Colors.black,
foregroundColor: Colors.white,
),
body: _buildBody(),
);
}
Widget _buildBody() {
if (_isLoading) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (_errorMessage != null) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.error_outline,
size: 64,
color: Colors.red,
),
const SizedBox(height: 16),
Text(
'Error',
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 8),
Text(
_errorMessage!,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyMedium,
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: _initializePlayer,
child: const Text('Retry'),
),
],
),
);
}
return Column(
children: [
// Video Player
AspectRatio(
aspectRatio: 16 / 9,
child: Container(
key: _playerKey,
color: Colors.black,
child: BetterPlayer(controller: _betterPlayerController),
),
),
// Player Info Panel
Container(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.videoTitle,
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 8),
Text(
'Video URL: ${widget.videoUrl}',
style: Theme.of(context).textTheme.bodySmall,
),
const SizedBox(height: 8),
Text(
'Viewer ID: ${widget.viewerId}',
style: Theme.of(context).textTheme.bodySmall,
),
],
),
),
],
);
}
@override
void dispose() {
// Clean up resources
_fastPixPlayer.disposeMetrix();
_betterPlayerController.dispose();
super.dispose();
}
}
// Usage in main app
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'FastPix Video Player Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
brightness: Brightness.dark,
),
home: const VideoPlayerPage(
videoUrl: "https://example.com/sample-video.mp4",
videoTitle: "Sample Video",
workspaceId: "your_workspace_id",
viewerId: "user_123_session_456",
),
);
}
}
This integration example demonstrates:
- Complete player initialization with error handling
- Proper resource cleanup
- UI state management
- Integration with state management solutions
- Best practices for production use
Updated about 4 hours ago