Skip to main content

Video Quality

Control the quality of video streams in the classroom. By default, quality adapts automatically based on network conditions, but users can also select manually.

VideoQuality Enum

ValueResolutionBitrate (approx)When to Use
autoAdaptiveVariesDefault, recommended
low480p500-800 kbpsSlow networks, data savings
medium720p1.5-2.5 MbpsBalanced quality/bandwidth
high1080p3-5 MbpsFast networks, best quality

Setting Initial Quality

Set the starting quality when creating the player:

VerrifloPlayer(
iframeUrl: iframeUrl,
initialQuality: VideoQuality.auto, // Default
)

For users on mobile data who want to save bandwidth:

VerrifloPlayer(
iframeUrl: iframeUrl,
initialQuality: VideoQuality.low,
)

How Auto Works

When VideoQuality.auto is selected, the player:

  1. Starts at medium quality
  2. Monitors network conditions (bandwidth, packet loss, latency)
  3. Upgrades quality if the network can handle it
  4. Downgrades smoothly if the network struggles

The goal: smooth playback over maximum resolution. A stable 720p is better than stuttering 1080p.

Built-in Quality Selector

The default control bar includes a quality selector. Users can tap it to choose their preferred quality.

If you want to hide it (to build your own):

VerrifloPlayer(
iframeUrl: iframeUrl,
showControls: false, // Hides all built-in controls
)

Building a Custom Quality Selector

If you've hidden the built-in controls, here's how to build your own:

class CustomQualityClassroom extends StatefulWidget {
final String iframeUrl;

const CustomQualityClassroom({required this.iframeUrl, super.key});


State<CustomQualityClassroom> createState() => _CustomQualityClassroomState();
}

class _CustomQualityClassroomState extends State<CustomQualityClassroom> {
VideoQuality _quality = VideoQuality.auto;


Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
VerrifloPlayer(
iframeUrl: widget.iframeUrl,
initialQuality: _quality,
showControls: false,
onClassEnded: () => Navigator.pop(context),
),

// Custom quality button
Positioned(
bottom: 20,
left: 20,
child: _buildQualityButton(),
),
],
),
);
}

Widget _buildQualityButton() {
return PopupMenuButton<VideoQuality>(
onSelected: _changeQuality,
color: Colors.grey[900],
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: Colors.black54,
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.settings, color: Colors.white, size: 18),
const SizedBox(width: 6),
Text(
_quality.label,
style: const TextStyle(color: Colors.white),
),
],
),
),
itemBuilder: (_) => VideoQuality.values.map((quality) {
final isSelected = quality == _quality;
return PopupMenuItem<VideoQuality>(
value: quality,
child: Row(
children: [
if (isSelected)
const Icon(Icons.check, color: Colors.white, size: 18)
else
const SizedBox(width: 18),
const SizedBox(width: 8),
Text(
quality.label,
style: TextStyle(
color: Colors.white,
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
),
),
],
),
);
}).toList(),
);
}

void _changeQuality(VideoQuality quality) {
setState(() => _quality = quality);
// Note: To actually change quality, the player needs to be reloaded
// with the new initialQuality. For runtime changes, the built-in
// control bar handles this via JavaScript messages.
}
}
note

Changing initialQuality after the player has loaded won't take effect immediately. The built-in controls work by sending a message to the iframe. If you need runtime quality changes with custom controls, you'll need to reload the player.

Quality Extension Methods

The VideoQuality enum has helper properties:

VideoQuality.high.label     // "1080p"
VideoQuality.medium.label // "720p"
VideoQuality.low.label // "480p"
VideoQuality.auto.label // "Auto"

VideoQuality.high.jsValue // "high"
VideoQuality.medium.jsValue // "medium"
VideoQuality.low.jsValue // "low"
VideoQuality.auto.jsValue // "auto"

When Quality Matters

Mobile Data Users

Consider detecting network type and suggesting low quality:

import 'package:connectivity_plus/connectivity_plus.dart';

Future<VideoQuality> getSuggestedQuality() async {
final connectivity = await Connectivity().checkConnectivity();

if (connectivity == ConnectivityResult.mobile) {
// Suggest low to save data
return VideoQuality.low;
}

// On WiFi, let auto handle it
return VideoQuality.auto;
}

User Preference

Let users save their preference:

import 'package:shared_preferences/shared_preferences.dart';

Future<void> saveQualityPreference(VideoQuality quality) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('video_quality', quality.jsValue);
}

Future<VideoQuality> loadQualityPreference() async {
final prefs = await SharedPreferences.getInstance();
final saved = prefs.getString('video_quality');

switch (saved) {
case 'high': return VideoQuality.high;
case 'medium': return VideoQuality.medium;
case 'low': return VideoQuality.low;
default: return VideoQuality.auto;
}
}

Quality Selection UI Pattern

A settings bottom sheet for quality:

void _showQualitySheet(BuildContext context) {
showModalBottomSheet(
context: context,
backgroundColor: Colors.grey[900],
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
),
builder: (context) {
return SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Padding(
padding: EdgeInsets.all(16),
child: Text(
'Video Quality',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
const Divider(color: Colors.grey),
...VideoQuality.values.map((quality) {
final isSelected = quality == _currentQuality;
return ListTile(
leading: Icon(
isSelected ? Icons.check_circle : Icons.circle_outlined,
color: isSelected ? Colors.green : Colors.grey,
),
title: Text(
quality.label,
style: const TextStyle(color: Colors.white),
),
subtitle: Text(
_getQualityDescription(quality),
style: TextStyle(color: Colors.grey[400]),
),
onTap: () {
_setQuality(quality);
Navigator.pop(context);
},
);
}),
const SizedBox(height: 16),
],
),
);
},
);
}

String _getQualityDescription(VideoQuality quality) {
switch (quality) {
case VideoQuality.auto:
return 'Adjusts automatically based on your connection';
case VideoQuality.high:
return '1080p • Best quality • Uses more data';
case VideoQuality.medium:
return '720p • Good quality • Balanced';
case VideoQuality.low:
return '480p • Lower quality • Saves data';
}
}

Bandwidth Estimation

The approximate bandwidth needed for each quality:

QualityDownload Speed Needed
Low (480p)1 Mbps
Medium (720p)3 Mbps
High (1080p)5+ Mbps
AutoAdapts to available

These are minimums. For smooth, buffer-free playback, aim for 2x these values.

Troubleshooting Quality

Video stuck on low quality even on good network:

  • Auto mode prioritizes stability. It might take time to upgrade.
  • Try manually selecting a higher quality.

Buffering or freezing:

  • Network can't sustain the selected quality
  • Switch to Auto or lower quality manually

Quality keeps fluctuating:

  • Network is unstable
  • Auto is doing its job—adapting to conditions

That covers the Flutter SDK! For more help, check:

Questions? support@verriflo.com