FlutterFlow includes a built-in FlutterFlowVideoPlayer widget that handles YouTube, Vimeo, and direct URL playback with minimal setup. For custom overlays, playback speed controls, or fullscreen toggle, create a Custom Widget using the video_player and chewie packages. This tutorial covers both the visual builder approach and the Custom Widget approach with working Dart code.
Adding Video Playback to Your FlutterFlow App
Video content is common in e-learning, social media, and marketing apps. FlutterFlow offers a built-in video player for quick setup, but it has limited customization. For advanced features like custom overlays or playback speed, you need a Custom Widget. This tutorial covers both paths so you can choose the right approach for your project.
Prerequisites
- A FlutterFlow project open in the builder
- A video URL (YouTube, Vimeo, or direct MP4/HLS link) to test with
- FlutterFlow Pro plan for the Custom Widget approach
- Basic understanding of Component Parameters
Step-by-step guide
Drag the built-in FlutterFlowVideoPlayer widget onto your page
Drag the built-in FlutterFlowVideoPlayer widget onto your page
Open the Widget Palette and search for VideoPlayer. Drag the FlutterFlowVideoPlayer widget into your page layout. In the Properties Panel, set Video Type to Network for a direct URL, YouTube for a YouTube link, or Asset for an uploaded video file. Paste your video URL into the Path field. Enable Show Controls to display play/pause, seek bar, and fullscreen buttons. Toggle Autoplay and Looping as needed.
Expected result: The video player appears on your page and plays the specified video with native controls.
Wrap the video player in an AspectRatio container
Wrap the video player in an AspectRatio container
Select the FlutterFlowVideoPlayer widget in the widget tree. Wrap it with a Container and set the Container width to double.infinity (fills parent). Set a fixed height that maintains a 16:9 aspect ratio relative to the screen width — for example, if your content area is 375px wide, set height to 211. Alternatively, wrap in an AspectRatio Custom Widget with ratio 16/9 to handle this automatically on all screen sizes.
Expected result: The video player maintains a consistent 16:9 aspect ratio across all screen sizes.
Add the video_player and chewie packages for Custom Widget
Add the video_player and chewie packages for Custom Widget
Go to Custom Code in the left Navigation Menu. Click Pubspec Dependencies. Add video_player with version ^2.8.0 and chewie with version ^1.7.0. Click Save. The chewie package wraps video_player with a polished control overlay including seek bar, volume, playback speed, and fullscreen — saving you from building all controls manually.
Expected result: Both packages appear in Pubspec Dependencies without version conflict errors.
Create a Custom Widget with Chewie for advanced video controls
Create a Custom Widget with Chewie for advanced video controls
Go to Custom Code → Custom Widgets → click Add. Name it AdvancedVideoPlayer. Add parameters: videoUrl (String, required), autoPlay (bool, default false), looping (bool, default false). In the code editor, create a StatefulWidget. In initState, initialize VideoPlayerController.networkUrl(Uri.parse(widget.videoUrl)), then create ChewieController with the video controller plus your configuration options. In build, return a Chewie widget wrapped in an AspectRatio.
1import 'package:video_player/video_player.dart';2import 'package:chewie/chewie.dart';34class AdvancedVideoPlayer extends StatefulWidget {5 const AdvancedVideoPlayer({6 super.key,7 this.width,8 this.height,9 required this.videoUrl,10 this.autoPlay,11 this.looping,12 });13 final double? width;14 final double? height;15 final String videoUrl;16 final bool? autoPlay;17 final bool? looping;1819 @override20 State<AdvancedVideoPlayer> createState() => _AdvancedVideoPlayerState();21}2223class _AdvancedVideoPlayerState extends State<AdvancedVideoPlayer> {24 late VideoPlayerController _videoController;25 ChewieController? _chewieController;2627 @override28 void initState() {29 super.initState();30 _videoController = VideoPlayerController.networkUrl(31 Uri.parse(widget.videoUrl),32 )..initialize().then((_) {33 _chewieController = ChewieController(34 videoPlayerController: _videoController,35 autoPlay: widget.autoPlay ?? false,36 looping: widget.looping ?? false,37 allowFullScreen: true,38 allowPlaybackSpeedChanging: true,39 playbackSpeeds: [0.5, 1.0, 1.5, 2.0],40 );41 setState(() {});42 });43 }4445 @override46 void dispose() {47 _videoController.dispose();48 _chewieController?.dispose();49 super.dispose();50 }5152 @override53 Widget build(BuildContext context) {54 return SizedBox(55 width: widget.width ?? double.infinity,56 height: widget.height ?? 250,57 child: _chewieController != null58 ? Chewie(controller: _chewieController!)59 : const Center(child: CircularProgressIndicator()),60 );61 }62}Expected result: The Custom Widget compiles and shows in the Widget Palette under Custom Widgets.
Place the Custom Widget and bind the video URL dynamically
Place the Custom Widget and bind the video URL dynamically
Drag AdvancedVideoPlayer from the Custom Widgets section of the Widget Palette onto your page. In the Properties Panel, bind the videoUrl parameter to a Page State variable, Route Parameter, or Backend Query field — this lets different pages or list items play different videos. Set autoPlay and looping as needed. The Chewie overlay provides play/pause, seek, volume, playback speed (0.5x to 2x), and fullscreen controls automatically.
Expected result: The advanced video player loads and plays the dynamically bound video URL with full Chewie controls.
Complete working example
1import 'package:video_player/video_player.dart';2import 'package:chewie/chewie.dart';34class AdvancedVideoPlayer extends StatefulWidget {5 const AdvancedVideoPlayer({6 super.key,7 this.width,8 this.height,9 required this.videoUrl,10 this.autoPlay,11 this.looping,12 });1314 final double? width;15 final double? height;16 final String videoUrl;17 final bool? autoPlay;18 final bool? looping;1920 @override21 State<AdvancedVideoPlayer> createState() => _AdvancedVideoPlayerState();22}2324class _AdvancedVideoPlayerState extends State<AdvancedVideoPlayer> {25 late VideoPlayerController _videoController;26 ChewieController? _chewieController;27 bool _hasError = false;2829 @override30 void initState() {31 super.initState();32 _videoController = VideoPlayerController.networkUrl(33 Uri.parse(widget.videoUrl),34 )..initialize().then((_) {35 _chewieController = ChewieController(36 videoPlayerController: _videoController,37 autoPlay: widget.autoPlay ?? false,38 looping: widget.looping ?? false,39 allowFullScreen: true,40 allowPlaybackSpeedChanging: true,41 playbackSpeeds: [0.5, 0.75, 1.0, 1.25, 1.5, 2.0],42 errorBuilder: (context, errorMessage) {43 return Center(44 child: Text(45 'Video failed to load',46 style: TextStyle(color: Colors.white),47 ),48 );49 },50 );51 setState(() {});52 }).catchError((error) {53 setState(() => _hasError = true);54 });55 }5657 @override58 void dispose() {59 _videoController.dispose();60 _chewieController?.dispose();61 super.dispose();62 }6364 @override65 Widget build(BuildContext context) {66 if (_hasError) {67 return SizedBox(68 width: widget.width ?? double.infinity,69 height: widget.height ?? 250,70 child: const Center(child: Text('Failed to load video')),71 );72 }73 return SizedBox(74 width: widget.width ?? double.infinity,75 height: widget.height ?? 250,76 child: _chewieController != null77 ? Chewie(controller: _chewieController!)78 : const Center(child: CircularProgressIndicator()),79 );80 }81}Common mistakes when creating a Custom Video Player for Your FlutterFlow App
Why it's a problem: Not wrapping the video player in an AspectRatio container
How to avoid: Always wrap your video widget in a Container with a 16:9 height-to-width ratio, or use an AspectRatio widget with aspectRatio: 16/9.
Why it's a problem: Forgetting to dispose the video controller
How to avoid: Override dispose() and call _videoController.dispose() and _chewieController?.dispose() to release resources.
Why it's a problem: Using the built-in player for multiple videos in a ListView
How to avoid: Use thumbnail images in the ListView. On tap, navigate to a detail page with the video player, or use a Custom Widget that initializes only when visible.
Best practices
- Use the built-in FlutterFlowVideoPlayer for simple single-video pages to avoid Custom Code complexity
- Use Chewie Custom Widget when you need playback speed, custom overlay, or programmatic controls
- Always dispose video controllers in the dispose() method to prevent memory leaks
- Show a loading spinner while the video initializes — chewie handles this automatically
- Pass video URLs as Component Parameters to reuse the same player widget across different pages
- For video lists, show thumbnail images and load the player only on tap to save resources
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
Write a FlutterFlow Custom Widget in Dart that plays a video from a URL parameter using the video_player and chewie packages. Include autoPlay and looping parameters, playback speed options, fullscreen support, and proper disposal of controllers.
Add a video player to this page. Set the video type to Network, paste this URL, enable show controls, and set autoplay to off.
Frequently asked questions
Does the built-in video player support YouTube URLs?
Yes. Set Video Type to YouTube in the Properties Panel and paste the YouTube URL. FlutterFlow handles the YouTube player embed automatically.
Can I play videos from Firebase Storage?
Yes. Store the video file in Firebase Storage, get the download URL, and pass it to the video player's URL property. Use a Backend Query to fetch the URL dynamically.
Why does my video show a black screen on iOS?
Common causes: the video URL uses HTTP instead of HTTPS (blocked by iOS), the video format is not supported (use MP4 with H.264), or the video controller was not initialized before building the widget.
Can I overlay text or buttons on the video?
With the built-in player, no. With a Custom Widget, yes — use a Stack to layer your custom UI on top of the video. Chewie provides a customizable overlay builder.
Does video autoplay work on web?
Browsers block autoplay with sound. Set the video to muted for autoplay to work on web, or require a user tap to start playback.
Can RapidDev help with complex video features?
Yes. RapidDev can build custom video players with DRM protection, adaptive streaming (HLS/DASH), offline caching, and analytics tracking.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation