Monitor Better Player (Flutter)

Understand how the FastPix Flutter SDK integrates with Better Player to capture playback metrics, errors, and viewer interactions.

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

1flutter pub add fastpix_better_player_wrapper

Option B: Add the dependency directly

Alternatively, you can add the dependency directly in your pubspec.yaml:

Dart
1dependencies:
2fastpix_better_player_wrapper: 0.0.1
3 better_player_plus: ^1.0.8

Then run:

Dart
1flutter 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:

Dart
1// Import FastPix Better Player Wrapper SDK into Dart files
2import '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.

Dart
1class VideoPlayerScreen extends StatefulWidget {
2 @override
3 _VideoPlayerScreenState createState() => _VideoPlayerScreenState();
4 }
5 class _VideoPlayerScreenState extends State<VideoPlayerScreen> {
6 late BetterPlayerController _betterPlayerController;
7 late FastPixBaseBetterPlayer _fastPixPlayer;
8
9@override
10 void initState() {
11 super.initState();
12 _initializePlayer();
13 }
14
15void _initializePlayer() {
16 _betterPlayerController = BetterPlayerController(
17 BetterPlayerConfiguration(
18 autoPlay: true,
19 looping: false,
20 aspectRatio: 16 / 9,
21 ),
22
23betterPlayerDataSource: BetterPlayerDataSource(
24 BetterPlayerDataSourceType.network,
25 "https://example.com/video.mp4",
26 ),
27 );
28
29// Create FastPix wrapper
30 _fastPixPlayer = FastPixBaseVideoPlayerBuilder(
31 playerController: _betterPlayerController,
32 workspaceId: "your_workspace_id", // Required
33 beaconUrl: "https://your-beacon-url.com",
34 viewerId: "unique_viewer_id", // Required
35 )
36
37.setVideoData(
38 VideoData(
39 videoUrl: "https://example.com/video.mp4",
40 videoThumbnailUrl: "https://example.com/thumbnail.jpg",
41 ),
42 )
43 .setEnabledLogging(true) // Optional: for debugging
44 .build();
45 _fastPixPlayer.start();
46 }

Step 3: Build the UI

Use the standard BetterPlayer widget to render the video.

Dart
1@override
2 Widget build(BuildContext context) {
3 return Scaffold(
4 appBar: AppBar(title: Text('Video Player')),
5 body: Column(
6 children: [
7 AspectRatio(
8 aspectRatio: 16 / 9,
9 child: BetterPlayer(controller: _betterPlayerController),
10 ),
11 ],
12 ),
13 );
14 }

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.

Dart
1@override
2 void dispose() {
3 // Dispose analytics and player controller
4 _fastPixPlayer.disposeMetrix();
5 super.dispose();
6 }


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.


Dart
1// Define custom analytics data
2 List<CustomData> customData = [
3 CustomData(value: "entertainment"),
4 CustomData(value: "action"),
5 CustomData(value: "PG-13"),
6 CustomData(value: "English"),
7 ];
8
9// Player metadata
10 PlayerData playerData = PlayerData(
11 playerName: "CustomVideoPlayer",
12 playerVersion: "2.1.0",
13 playerType: "mobile",
14 );
15
16// Video metadata
17 VideoData videoData = VideoData(
18 videoUrl: "https://example.com/video.mp4",
19 videoThumbnailUrl: "https://example.com/thumbnail.jpg",
20 videoTitle: "Sample Video",
21 videoDuration: 120000, // in milliseconds
22 );
23
24
25
26// Build with advanced configuration
27 _fastPixPlayer = FastPixBaseVideoPlayerBuilder(
28 playerController: _betterPlayerController,
29 workspaceId: "your_workspace_id",
30 beaconUrl: "https://your-beacon-url.com",
31 viewerId: "unique_viewer_id",
32 )
33 .setVideoData(videoData)
34 .setPlayerData(playerData)
35 .setCustomData(customData)
36 .setEnabledLogging(true)
37 .build();
38
39Player Dimension Tracking
40
41For player layout insights, you can report player size using a `GlobalKey`.
42
43
44
45class _VideoPlayerScreenState extends State<VideoPlayerScreen> {
46 final GlobalKey _playerKey = GlobalKey();
47
48@override
49 Widget build(BuildContext context) {
50 return Scaffold(
51 body: Column(
52 children: [
53 AspectRatio(
54 aspectRatio: 16 / 9,
55 child: Container(
56 key: _playerKey, // Add key for dimension tracking
57 child: BetterPlayer(controller: _betterPlayerController),
58 ),
59 ),
60 ],
61 ),
62 );
63 }
64
65@override
66 void initState() {
67 super.initState();
68 _initializePlayer();
69 // Report player dimensions for analytics
70 WidgetsBinding.instance.addPostFrameCallback((_) {
71 _fastPixPlayer.reportPlayerSize(_playerKey);
72 });
73 }
74 }

Event tracking & analytics

The SDK automatically tracks standard playback events without additional code.

EventDescriptionWhen it occurs
playVideo starts playingUser presses play or autoplay triggers
playingActive playbackVideo is actively playing
pauseVideo pausedUser pauses or video is interrupted
seekingSeeking operation startedUser seeks to different position
seekedSeeking completedSeek operation finishes
bufferingVideo bufferingInsufficient data for playback
bufferedBuffering completedEnough data available
variantChangedQuality/resolution changeAdaptive bitrate changes
endedVideo completedVideo reaches end
errorPlayback errorAny playback failure

Builder methods

FastPixBaseVideoPlayerBuilder : Used to configure and create a FastPix-wrapped player instance.

Dart
1FastPixBaseVideoPlayerBuilder({
2 required BetterPlayerController playerController,
3 required String workspaceId,
4 String? beaconUrl,
5 required String viewerId,
6 })

Configuration methods for customizing player behavior, metadata, and output:

FunctionParametersReturn TypeDescription
setEnabledLogging(bool value)bool valueFastPixBaseVideoPlayerBuilderEnable/disable detailed logging
setCustomData(List<CustomData> value)List<CustomData> valueFastPixBaseVideoPlayerBuilderSet custom analytics data
setPlayerData(PlayerData? value)PlayerData? valueFastPixBaseVideoPlayerBuilderSet player metadata
setVideoData(VideoData? value)VideoData? valueFastPixBaseVideoPlayerBuilderSet video information
build()NoneFastPixBaseBetterPlayerCreate the configured player instance

Player instance methods

FastPixBaseBetterPlayer once built, the player provides lifecycle utilities:

FunctionParametersReturn TypeDescription
start()NonevoidInitialize and start analytics tracking
disposeMetrix()NoneFuture<void>Clean up analytics resources and player
reportPlayerSize(GlobalKey key)GlobalKey keyvoidReport player dimensions for analytics

Properties:

PropertyTypeDescription
audioLanguageStringCurrent audio track language
playerWidthSizedoubleCurrent player width
playerHeightSizedoubleCurrent player height

PlayerObserver implementation

The class implements PlayerObserver interface with these methods:

FunctionReturn TypeDescription
isPlayerAutoPlayOn()boolCheck if autoplay is enabled
isPlayerFullScreen()boolCheck if player is in fullscreen
isPlayerPaused()boolCheck if player is paused
isVideoSourceLive()boolCheck if video is live stream
playerHeight()doubleGet current player height
playerWidth()doubleGet current player width
playerLanguageCode()StringGet current audio language
playerPlayHeadTime()Future<int>Get current playback position
videoSourceDuration()intGet total video duration
videoSourceUrl()StringGet video source URL
videoSourceMimeType()StringGet video MIME type

Error reporting

The SDK automatically captures and reports error details through ErrorModel:

Dart
1// Check for player errors
2 ErrorModel? error = _fastPixPlayer.getPlayerError();
3 if (error != null) {
4 print('Player error: ${error.message}');
5 print('Error source: ${error.source}');
6 }


Best practices

1. Resource management

Handles resource cleanup by disposing the FastPix wrapper before the controller.

Dart
1@override
2 void dispose() {
3 // Always dispose FastPix wrapper before disposing controller
4 _fastPixPlayer.disposeMetrix();
5 super.dispose();
6 }

2. Error handling

Wraps player initialization in a try–catch block to handle setup errors gracefully.

Dart
1void _initializePlayer() {
2 try {
3 _fastPixPlayer = FastPixBaseVideoPlayerBuilder(
4 // ... configuration
5 ).build();
6 _fastPixPlayer.start();
7 } catch (e) {
8 print('Failed to initialize FastPix player: $e');
9 // Handle initialization error
10 }
11 }

3. Configuration validation

Ensures required IDs and video URLs are valid before building the player.

Dart
1// Validate required parameters
2 if (workspaceId.isEmpty || viewerId.isEmpty) {
3 throw ArgumentError('workspaceId and viewerId are required');
4 }
5 // Validate video data
6 if (videoData?.videoUrl.isEmpty ?? true) {
7 throw ArgumentError('Valid video URL is required');
8 }

4. Performance optimization

Applies settings to optimize performance and analytics, such as conditional logging and meaningful identifiers.

Dart
1// Enable logging only in debug mode
2 .setEnabledLogging(kDebugMode)
3 // Use meaningful viewer IDs for analytics
4 .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:

Dart
1import 'package:flutter/material.dart';
2import 'package:better_player_plus/better_player_plus.dart';
3import 'package:fastpix_better_player_wrapper/fastpix_better_player_wrapper.dart';
4
5class VideoPlayerPage extends StatefulWidget {
6 final String videoUrl;
7 final String videoTitle;
8 final String workspaceId;
9 final String viewerId;
10
11 const VideoPlayerPage({
12 Key? key,
13 required this.videoUrl,
14 required this.videoTitle,
15 required this.workspaceId,
16 required this.viewerId,
17 }) : super(key: key);
18
19 @override
20 _VideoPlayerPageState createState() => _VideoPlayerPageState();
21}
22
23class _VideoPlayerPageState extends State<VideoPlayerPage> {
24 late BetterPlayerController _betterPlayerController;
25 late FastPixBaseBetterPlayer _fastPixPlayer;
26 final GlobalKey _playerKey = GlobalKey();
27
28 bool _isLoading = true;
29 String? _errorMessage;
30
31 @override
32 void initState() {
33 super.initState();
34 _initializePlayer();
35 }
36
37 void _initializePlayer() async {
38 try {
39 setState(() {
40 _isLoading = true;
41 _errorMessage = null;
42 });
43
44 // Initialize BetterPlayer controller
45 _betterPlayerController = BetterPlayerController(
46 BetterPlayerConfiguration(
47 autoPlay: true,
48 looping: false,
49 aspectRatio: 16 / 9,
50 fit: BoxFit.contain,
51 controlsEnabled: true,
52 showPlaceholderUntilPlay: true,
53 placeholder: Container(
54 color: Colors.black,
55 child: const Center(
56 child: CircularProgressIndicator(color: Colors.white),
57 ),
58 ),
59 ),
60 betterPlayerDataSource: BetterPlayerDataSource(
61 BetterPlayerDataSourceType.network,
62 widget.videoUrl,
63 ),
64 );
65
66 // Configure video metadata
67 VideoData videoData = VideoData(
68 videoUrl: widget.videoUrl,
69 videoTitle: widget.videoTitle,
70 videoThumbnailUrl: "https://example.com/thumbnail.jpg",
71 videoDuration: 0, // Will be auto-detected
72 );
73
74 // Configure player metadata
75 PlayerData playerData = PlayerData(
76 playerName: "FastPixVideoPlayer",
77 playerVersion: "1.0.0",
78 playerType: "mobile",
79 );
80
81 // Configure custom analytics data
82 List<CustomData> customData = [
83 CustomData(value: "entertainment"),
84 CustomData(value: "movie"),
85 CustomData(value: "HD"),
86 ];
87
88 // Build FastPix player with analytics
89 _fastPixPlayer = FastPixBaseVideoPlayerBuilder(
90 playerController: _betterPlayerController,
91 workspaceId: widget.workspaceId,
92 beaconUrl: "https://your-beacon-url.com", // Replace with your beacon URL
93 viewerId: widget.viewerId,
94 )
95 .setVideoData(videoData)
96 .setPlayerData(playerData)
97 .setCustomData(customData)
98 .setEnabledLogging(kDebugMode) // Enable logging in debug mode
99 .build();
100
101 // Start analytics tracking
102 _fastPixPlayer.start();
103
104 // Report player dimensions after widget is built
105 WidgetsBinding.instance.addPostFrameCallback((_) {
106 _fastPixPlayer.reportPlayerSize(_playerKey);
107 });
108
109 setState(() {
110 _isLoading = false;
111 });
112
113 } catch (e) {
114 setState(() {
115 _isLoading = false;
116 _errorMessage = 'Failed to initialize player: $e';
117 });
118 print('Player initialization error: $e');
119 }
120 }
121
122 @override
123 Widget build(BuildContext context) {
124 return Scaffold(
125 appBar: AppBar(
126 title: Text(widget.videoTitle),
127 backgroundColor: Colors.black,
128 foregroundColor: Colors.white,
129 ),
130 body: _buildBody(),
131 );
132 }
133
134 Widget _buildBody() {
135 if (_isLoading) {
136 return const Center(
137 child: CircularProgressIndicator(),
138 );
139 }
140
141 if (_errorMessage != null) {
142 return Center(
143 child: Column(
144 mainAxisAlignment: MainAxisAlignment.center,
145 children: [
146 const Icon(
147 Icons.error_outline,
148 size: 64,
149 color: Colors.red,
150 ),
151 const SizedBox(height: 16),
152 Text(
153 'Error',
154 style: Theme.of(context).textTheme.headlineSmall,
155 ),
156 const SizedBox(height: 8),
157 Text(
158 _errorMessage!,
159 textAlign: TextAlign.center,
160 style: Theme.of(context).textTheme.bodyMedium,
161 ),
162 const SizedBox(height: 16),
163 ElevatedButton(
164 onPressed: _initializePlayer,
165 child: const Text('Retry'),
166 ),
167 ],
168 ),
169 );
170 }
171
172 return Column(
173 children: [
174 // Video Player
175 AspectRatio(
176 aspectRatio: 16 / 9,
177 child: Container(
178 key: _playerKey,
179 color: Colors.black,
180 child: BetterPlayer(controller: _betterPlayerController),
181 ),
182 ),
183
184 // Player Info Panel
185 Container(
186 padding: const EdgeInsets.all(16),
187 child: Column(
188 crossAxisAlignment: CrossAxisAlignment.start,
189 children: [
190 Text(
191 widget.videoTitle,
192 style: Theme.of(context).textTheme.headlineSmall,
193 ),
194 const SizedBox(height: 8),
195 Text(
196 'Video URL: ${widget.videoUrl}',
197 style: Theme.of(context).textTheme.bodySmall,
198 ),
199 const SizedBox(height: 8),
200 Text(
201 'Viewer ID: ${widget.viewerId}',
202 style: Theme.of(context).textTheme.bodySmall,
203 ),
204 ],
205 ),
206 ),
207 ],
208 );
209 }
210
211 @override
212 void dispose() {
213 // Clean up resources
214 _fastPixPlayer.disposeMetrix();
215 _betterPlayerController.dispose();
216 super.dispose();
217 }
218}
219
220// Usage in main app
221class MyApp extends StatelessWidget {
222 @override
223 Widget build(BuildContext context) {
224 return MaterialApp(
225 title: 'FastPix Video Player Demo',
226 theme: ThemeData(
227 primarySwatch: Colors.blue,
228 brightness: Brightness.dark,
229 ),
230 home: const VideoPlayerPage(
231 videoUrl: "https://example.com/sample-video.mp4",
232 videoTitle: "Sample Video",
233 workspaceId: "your_workspace_id",
234 viewerId: "user_123_session_456",
235 ),
236 );
237 }
238}


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