A Flutter YouTube player that loads a video is not necessarily ready for release. It also needs to handle invalid links, seeking, fullscreen transitions and the platform where your app actually runs. For FlutterFlow developers adapting a community widget, the first step is to check the package version and identify whether the failure comes from playback, code or layout.
This guide examines a FlutterFlow community example built with youtube_player_flutter: ^9.1.3. Its author offered it after experiencing playback failures reported as “error 15.” Another developer asked whether seeking and full-width landscape playback worked, and the author clarified that the implementation was designed around portrait use.
That distinction matters. A workaround for one playback problem does not automatically provide a complete fullscreen experience. The code also contains several issues that can be addressed independently of YouTube’s playback service.
Start with the package version and target platform
The fixes below concern the 9.1.3 implementation in that discussion. Its version-specific README documents Android and iOS support and directs web developers to youtube_player_iframe. A FlutterFlow browser preview therefore cannot establish whether that older mobile implementation works correctly on a phone.
Do not combine an old widget with code copied from a different major version. The package changelog describes version 10 as a rewrite with changed APIs and the removal of YoutubePlayerBuilder. During this review, the current README and changelog also exposed inconsistent examples, reinforcing the need to check the resolved version and its actual API before upgrading.
For an existing app, first reproduce the problem with its current dependency set. For a new implementation, assess the current package against your FlutterFlow project’s Flutter and Dart versions, then use matching examples. Installing a newer release and retaining incompatible controller code can introduce compilation failures before playback even starts.
If your goal is to display a channel or playlist on a website, first consider whether you need a Flutter implementation at all. A YouTube widget for websites can provide an embeddable video feed for that separate use case. It does not replace the native player or solve its controller and fullscreen issues.
Identify the failure before replacing the widget
“Not working” can describe several unrelated problems. A player that never loads needs a different investigation from one that plays normally but remains small after rotation.
| What happens | What to investigate first |
|---|---|
| Custom code does not compile | Undefined names, imports and package/API compatibility |
| Leaving the page after an invalid URL throws an exception | Controller initialization and disposal |
| Player rotates but stays inside a small rectangle | Parent constraints and fullscreen placement |
| Video plays but controls cannot be dragged | Control configuration, overlays and gesture interception |
| Playback fails for one particular video | Availability and embedding restrictions |
| Playback fails across multiple videos | Exact error, platform, package and embedded-player configuration |
| Old code works on mobile but not in browser preview | Version-specific platform support |
Use one video known to permit embedding, disable autoplay initially and reproduce the issue on a minimal page. Record the exact error and environment. This reduces the number of variables before you start changing the player’s implementation.
Fix the problems in the posted FlutterFlow widget
The original code has a straightforward naming error in its readiness callback:
controller.addListener(listener);
The declared fields are _controller and _listener, so the matching call is:
_controller.addListener(_listener);
However, the listener shown in the thread only calls an empty setState(() {}). It does not save progress or update any custom interface state. If your widget does not need that notification, remove the listener and registration instead of rebuilding the entire widget unnecessarily. If you retain a listener, register it once for each controller and manage its lifecycle.
The next issue appears when the URL is invalid. The controller is declared late, but initialization is skipped if conversion fails. Later, dispose() still accesses that uninitialized field, potentially causing a LateInitializationError.
A nullable declaration makes this situation explicit:
YoutubePlayerController? _controller;
Disposal can then safely account for a missing controller:
@override
void dispose() {
_controller?.dispose();
super.dispose();
}
This change must be carried through the widget. In build(), store the controller in a local variable, return an error or loading state if it is null, and only construct YoutubePlayer with a non-null controller. Changing the declaration alone is not a complete patch.
There is also a control-visibility mismatch. The input named showControls affects controlsVisibleAtStart, while hideControls is always false. If the input is intended to hide controls entirely, configure both flags consistently:
controlsVisibleAtStart: widget.showControls ?? true,
hideControls: !(widget.showControls ?? true),
Finally, the example initializes its video only in initState(). If FlutterFlow later passes a different URL to the same widget instance, implement didUpdateWidget() to validate the new value and update playback using the matching package API. Clear any previous error when a valid replacement arrives.
These are code-review findings, not evidence that every YouTube playback error has been resolved. The snippets are targeted changes to the supplied widget and have not been compiled or tested in your project.
Does the player support dragging and a 10-second skip?
The posted configuration enables drag seeking through disableDragSeek: false and includes a ProgressBar. Version 9.1.3 also documents horizontal dragging for fast-forward and rewind. That supports the intention to let viewers navigate through a recorded video.
However, three interactions should be distinguished: dragging the progress bar to a timestamp, dragging across the video, and pressing a button that jumps forward exactly ten seconds. The thread’s bottomActions includes a progress bar and fullscreen button, but no dedicated ten-second skip button.
To implement that button, use the controller’s version-appropriate seek method. Read the current position, add ten seconds, and clamp the target to the known duration. Disable the action before readiness; for rewind, clamp the target to zero. Do not assume a live stream offers the same seeking behavior as a recorded video.
If dragging fails despite the enabled setting, simplify the surrounding screen. Temporarily remove overlays and parent gesture handlers, then retest. A control can be configured correctly while another widget intercepts the user’s touch.
Fix fullscreen layout before forcing landscape
The supplied widget places YoutubePlayerBuilder inside a SizedBox whose dimensions come from the inline widget. That is a plausible source of a player remaining constrained after entering fullscreen. It is a layout diagnosis to test, not a proven explanation for every device.
The version 9.1.3 example uses YoutubePlayerBuilder outside the screen’s Scaffold. Its normal builder returns the page, giving the fullscreen handling a broader place in the widget tree. It also demonstrates an exit callback for orientation behavior.
For an existing FlutterFlow screen, inspect the custom widget’s ancestors as well as its own code. Removing one inner SizedBox will not help if an outer container still enforces the same small dimensions. Where page-level placement is impractical, a dedicated player page is worth considering.
Orientation needs separate attention. Check which orientations the app supports, what should happen on fullscreen entry and what should be restored on exit. Repeatedly forcing landscape inside build() can create conflicting behavior; use lifecycle or fullscreen callbacks appropriate to the chosen package.
Also distinguish filling the screen from stretching the picture. A landscape display and a 16:9 video may have different proportions. Black bars can be the correct result when preserving the full image. Removing every bar may require cropping, which can cut off subtitles or content.
What “YouTube error 15” does—and does not—tell you
The community report uses the label “error 15,” but YouTube’s documented IFrame onError list does not define that number. Do not silently reinterpret it as 150 or 153. Capture the actual message, controller error and relevant runtime output before choosing a remedy.
YouTube documents 100 for a missing or private video, 101 and 150 for embedding restrictions, and 153 for missing HTTP Referer information or equivalent API client identification. A widget cannot override a creator’s embedding restriction, and a client-identification problem requires checking the integration rather than changing its dimensions.
If the original video fails, test another embeddable video in the same build. If both fail, compare the minimal player with the existing screen. This helps separate video-specific restrictions, integration problems and app layout without presenting an old workaround as a universal fix.
When a Shopify YouTube feed is the relevant solution
Some readers arrive at YouTube-widget tutorials because they need videos on a storefront rather than playback inside a FlutterFlow application. For that requirement, a Dart widget creates an unnecessary implementation path.
A Taggbox YouTube feed widget for Shopify can display content from sources such as a channel or playlist through a configurable website gallery. Its documented process involves selecting the source, customizing the feed and publishing the generated embed to the store.
That is relevant when the goal is presenting product demonstrations or a video collection on Shopify. It does not repair a Flutter controller, change a mobile app’s orientation behavior or establish a solution for error 15. Choose the approach according to where the videos need to appear and which interactions the viewer needs.
Test the complete viewing journey
After the code and layout changes, test more than the initial play button. Start a video, seek forward and backward, enter fullscreen, rotate the device, exit fullscreen and navigate away. Then reopen the page and try an invalid URL followed by a valid one.
Watch for lost playback position, duplicated audio, stuck orientation, clipped controls and errors during disposal. If the widget accepts changing input, switch the URL without recreating the whole page and confirm that the intended video appears.
For the legacy example, perform these checks in Android and iOS builds relevant to your audience. Keep browser testing separate from native testing. Record the dependency version with the results so a later upgrade does not invalidate the evidence unnoticed.
Frequently asked questions
Can I use the original widget unchanged?
The pasted version needs correction: its listener registration references undeclared names, and its disposal path can access an uninitialized controller after an invalid URL. Those issues should be addressed before judging playback reliability.
Why does the player remain small in landscape?
Rotation changes orientation, but parent layout constraints can still limit the player’s size. Inspect the surrounding containers and move fullscreen handling to a position that can use the available screen.
Does drag seeking provide a ten-second skip button?
No. Dragging selects a position through a gesture or progress bar. A fixed ten-second jump needs an explicit control and a seek target calculated from the current playback position.
Should I upgrade the package immediately?
Check compatibility first. A major-version change can require different controller initialization, controls and fullscreen handling. Preserve a reproducible baseline and migrate deliberately rather than treating an upgrade as a guaranteed playback repair.
Why does a new URL leave the old video playing?
The supplied example reads its initial URL during initState(). If the same stateful widget receives a new URL, it needs an update path that validates the input and tells the controller to change videos.
Can Taggbox replace this FlutterFlow player?
Its Shopify YouTube widget addresses website feed embedding. For the native app scenario in this thread, controller lifecycle, playback configuration and fullscreen layout still require a Flutter-compatible implementation.
A reliable Flutter YouTube player comes from matching the package to the platform, correcting lifecycle issues and testing fullscreen as part of the whole screen. Start with the specific failing behavior, then verify that the fix preserves the rest of the viewing experience.



Top comments (0)