Skip to main content
RapidDev - Software Development Agency
flutterflow-tutorials

How to Build a Custom Video Player with Advanced Features in FlutterFlow

Extend the basic video player with picture-in-picture mode, playback speed selection (0.5x-2x), quality switching (360p/720p/1080p), and subtitles from SRT files. Create a Custom Widget using the better_player package which bundles all these features with a polished control overlay. Configure BetterPlayerController with BetterPlayerConfiguration and BetterPlayerDataSource that includes resolution maps for quality switching and subtitle sources for closed captions.

What you'll learn

  • How to set up the better_player package with advanced configuration
  • How to enable quality switching with multiple resolution URLs
  • How to add subtitles from SRT files for closed captions
  • How to enable picture-in-picture mode for background viewing
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner6 min read25-30 minFlutterFlow Pro+ (Custom Code required)March 2026RapidDev Engineering Team
TL;DR

Extend the basic video player with picture-in-picture mode, playback speed selection (0.5x-2x), quality switching (360p/720p/1080p), and subtitles from SRT files. Create a Custom Widget using the better_player package which bundles all these features with a polished control overlay. Configure BetterPlayerController with BetterPlayerConfiguration and BetterPlayerDataSource that includes resolution maps for quality switching and subtitle sources for closed captions.

Building an Advanced Video Player in FlutterFlow

The basic video player (#7) covers simple playback. For streaming apps, e-learning platforms, and media-rich apps, you need playback speed, quality options, subtitles, and PiP. The better_player package wraps all these features into one widget.

Prerequisites

  • FlutterFlow Pro plan or higher (Custom Code required)
  • Video files available in multiple resolutions (360p, 720p, 1080p URLs)
  • SRT subtitle files hosted at accessible URLs (optional)
  • Understanding of the basic video player Custom Widget pattern

Step-by-step guide

1

Add the better_player dependency to your project

Go to Custom Code → Pubspec Dependencies and add better_player with version ^0.0.84. Click Save. better_player wraps the standard video_player with a comprehensive control overlay, quality switching, subtitles, PiP, and caching — all configured via its controller objects. It is the most feature-complete video package for Flutter.

Expected result: The better_player package appears in Pubspec Dependencies without errors.

2

Create the Custom Widget with BetterPlayerController

Create a Custom Widget called AdvancedPlayer. Add parameters: videoUrl (String, required), video720Url (String, optional), video1080Url (String, optional), subtitleUrl (String, optional). In initState, create BetterPlayerConfiguration with autoPlay, looping, and controlsConfiguration. Create BetterPlayerDataSource with the videoUrl. If quality URLs are provided, add them to the resolutions map. Build returns a BetterPlayer widget with the controller.

advanced_player.dart
1import 'package:better_player/better_player.dart';
2
3class AdvancedPlayer extends StatefulWidget {
4 const AdvancedPlayer({
5 super.key, this.width, this.height,
6 required this.videoUrl,
7 this.video720Url, this.video1080Url, this.subtitleUrl,
8 });
9 final double? width;
10 final double? height;
11 final String videoUrl;
12 final String? video720Url;
13 final String? video1080Url;
14 final String? subtitleUrl;
15
16 @override
17 State<AdvancedPlayer> createState() => _AdvancedPlayerState();
18}
19
20class _AdvancedPlayerState extends State<AdvancedPlayer> {
21 late BetterPlayerController _controller;
22
23 @override
24 void initState() {
25 super.initState();
26 final config = BetterPlayerConfiguration(
27 autoPlay: false,
28 looping: false,
29 fit: BoxFit.contain,
30 controlsConfiguration: BetterPlayerControlsConfiguration(
31 enablePlaybackSpeed: true,
32 enableSubtitles: widget.subtitleUrl != null,
33 enableQualities: true,
34 ),
35 );
36 final resolutions = <String, String>{
37 '360p': widget.videoUrl,
38 if (widget.video720Url != null) '720p': widget.video720Url!,
39 if (widget.video1080Url != null) '1080p': widget.video1080Url!,
40 };
41 final dataSource = BetterPlayerDataSource(
42 BetterPlayerDataSourceType.network,
43 widget.videoUrl,
44 resolutions: resolutions,
45 subtitles: widget.subtitleUrl != null
46 ? [BetterPlayerSubtitlesSource(
47 type: BetterPlayerSubtitlesSourceType.network,
48 urls: [widget.subtitleUrl!],
49 )]
50 : null,
51 );
52 _controller = BetterPlayerController(config, betterPlayerDataSource: dataSource);
53 }
54
55 @override
56 void dispose() {
57 _controller.dispose();
58 super.dispose();
59 }
60
61 @override
62 Widget build(BuildContext context) {
63 return SizedBox(
64 width: widget.width ?? double.infinity,
65 height: widget.height ?? 250,
66 child: BetterPlayer(controller: _controller),
67 );
68 }
69}

Expected result: The Custom Widget compiles with quality switching, speed controls, and subtitle support.

3

Configure quality switching with multiple resolution URLs

When adding videos to your Firestore collection, store multiple resolution URLs: videoUrl360 (small, fast), videoUrl720 (medium), videoUrl1080 (high quality). Pass all three to the AdvancedPlayer widget. better_player shows a quality selector in the controls overlay where users can switch between 360p, 720p, and 1080p on the fly. The player buffers the new quality and continues from the same position.

Expected result: Users see a quality selector button in the video controls and can switch resolutions during playback.

4

Add subtitle support from SRT files

Host your SRT subtitle file at an accessible URL (e.g., Firebase Storage). Pass the URL to the subtitleUrl parameter. better_player parses the SRT format and overlays subtitles on the video. Users can toggle subtitles on/off via the CC button in the controls. For multiple languages, provide an array of BetterPlayerSubtitlesSource with different language labels.

Expected result: Subtitles display over the video and users can toggle them on or off.

5

Enable picture-in-picture mode for background viewing

Add controller.enablePictureInPicture(controller.betterPlayerGlobalKey!) call when the user taps a PiP button or navigates away. On Android, PiP requires the Activity to have android:supportsPictureInPicture='true' in AndroidManifest.xml (configure after code export). iOS supports PiP natively for video. PiP lets users watch video in a small floating window while using other parts of the app.

Expected result: Video continues playing in a small floating window when the user navigates to another page.

Complete working example

advanced_player.dart
1import 'package:better_player/better_player.dart';
2
3class AdvancedPlayer extends StatefulWidget {
4 const AdvancedPlayer({
5 super.key,
6 this.width,
7 this.height,
8 required this.videoUrl,
9 this.video720Url,
10 this.video1080Url,
11 this.subtitleUrl,
12 });
13
14 final double? width;
15 final double? height;
16 final String videoUrl;
17 final String? video720Url;
18 final String? video1080Url;
19 final String? subtitleUrl;
20
21 @override
22 State<AdvancedPlayer> createState() => _AdvancedPlayerState();
23}
24
25class _AdvancedPlayerState extends State<AdvancedPlayer> {
26 late BetterPlayerController _controller;
27
28 @override
29 void initState() {
30 super.initState();
31 final config = BetterPlayerConfiguration(
32 autoPlay: false,
33 looping: false,
34 fit: BoxFit.contain,
35 controlsConfiguration: BetterPlayerControlsConfiguration(
36 enablePlaybackSpeed: true,
37 enableSubtitles: widget.subtitleUrl != null,
38 enableQualities: true,
39 enablePip: true,
40 playbackSpeedList: const [0.5, 0.75, 1.0, 1.25, 1.5, 2.0],
41 ),
42 );
43
44 final resolutions = <String, String>{
45 '360p': widget.videoUrl,
46 };
47 if (widget.video720Url != null) resolutions['720p'] = widget.video720Url!;
48 if (widget.video1080Url != null) resolutions['1080p'] = widget.video1080Url!;
49
50 final dataSource = BetterPlayerDataSource(
51 BetterPlayerDataSourceType.network,
52 widget.videoUrl,
53 resolutions: resolutions,
54 subtitles: widget.subtitleUrl != null
55 ? [
56 BetterPlayerSubtitlesSource(
57 type: BetterPlayerSubtitlesSourceType.network,
58 urls: [widget.subtitleUrl!],
59 name: 'English',
60 ),
61 ]
62 : null,
63 );
64
65 _controller = BetterPlayerController(
66 config,
67 betterPlayerDataSource: dataSource,
68 );
69 }
70
71 @override
72 void dispose() {
73 _controller.dispose();
74 super.dispose();
75 }
76
77 @override
78 Widget build(BuildContext context) {
79 return SizedBox(
80 width: widget.width ?? double.infinity,
81 height: widget.height ?? 250,
82 child: BetterPlayer(controller: _controller),
83 );
84 }
85}

Common mistakes when building a Custom Video Player with Advanced Features in FlutterFlow

Why it's a problem: Using the basic video_player package for features that better_player includes

How to avoid: Use better_player when you need any advanced features. Only use video_player for the simplest possible playback.

Why it's a problem: Providing only one video resolution without quality options

How to avoid: Encode videos in multiple resolutions (360p, 720p, 1080p) and provide all URLs in the resolutions map for user-selectable quality.

Why it's a problem: Not disposing the BetterPlayerController on page exit

How to avoid: Override dispose() and call _controller.dispose() to stop playback and free resources.

Best practices

  • Use better_player for advanced features — it wraps video_player with quality, speed, subtitles, and PiP
  • Provide multiple resolution URLs for user-selectable quality switching
  • Add SRT subtitle files for accessibility and multi-language support
  • Dispose the controller in dispose() to prevent background playback and memory leaks
  • Enable playback speed options (0.5x to 2x) for e-learning and review use cases
  • Configure PiP mode for background viewing (requires Android manifest changes after export)
  • Pass video URLs as Component Parameters for reusable player widgets

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

Write a FlutterFlow Custom Widget for an advanced video player using better_player. Include quality switching with 360p/720p/1080p resolution map, playback speed options, SRT subtitle support, and picture-in-picture configuration.

FlutterFlow Prompt

Add a full-width video player container on my page at 250px height. I will add the Custom Widget with advanced controls inside it.

Frequently asked questions

Does better_player support HLS and DASH streaming?

Yes. better_player supports HLS (.m3u8) and DASH (.mpd) adaptive streaming formats. Set the data source URL to the manifest file and it handles adaptive bitrate automatically.

Can I add multiple subtitle languages?

Yes. Add multiple BetterPlayerSubtitlesSource entries to the subtitles array, each with a different URL and language name. Users select from a language picker in the controls.

Does PiP work on iOS?

iOS supports PiP natively for AVPlayer-based video. better_player integrates with this. The user must enable PiP in iOS Settings and the app must declare background audio capability.

How do I cache videos for offline playback?

better_player supports caching with BetterPlayerCacheConfiguration. Set maxCacheSize and maxCacheFileSize in the data source configuration.

Can I track video watch progress?

Yes. Listen to BetterPlayerController events for position changes. Save the current position to Firestore periodically. On reload, seek to the saved position for resume functionality.

Can RapidDev help build a video streaming platform?

Yes. RapidDev can implement video players with DRM, adaptive streaming, offline caching, progress tracking, and analytics.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.