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
| Value | Resolution | Bitrate (approx) | When to Use |
|---|---|---|---|
auto | Adaptive | Varies | Default, recommended |
low | 480p | 500-800 kbps | Slow networks, data savings |
medium | 720p | 1.5-2.5 Mbps | Balanced quality/bandwidth |
high | 1080p | 3-5 Mbps | Fast 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:
- Starts at medium quality
- Monitors network conditions (bandwidth, packet loss, latency)
- Upgrades quality if the network can handle it
- 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.
}
}
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:
| Quality | Download Speed Needed |
|---|---|
| Low (480p) | 1 Mbps |
| Medium (720p) | 3 Mbps |
| High (1080p) | 5+ Mbps |
| Auto | Adapts 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