NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
pub.dev · #5572 most downloaded on pub.dev
Native video player (AVPlayer on iOS, ExoPlayer on Android) with HLS, DRM, Chromecast & AirPlay casting, Picture-in-Picture, background playback, and fullscreen.
Last release 9 days ago
29 Sep 2026
Ships unpredictably
gaps range from 8 days to 4 months
Nearly every release is documented
notes for 59 of the last 60 stable releases
Nothing withdrawn
no release was ever pulled
11 months old
69 releases · first in 2025
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01WfGToK4RaKH6aMbTGgYBdB
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01WfGToK4RaKH6aMbTGgYBdB
NativeVideoPlayerConfig.iosAllowsVideoFrameAnalysis (default false).
iOS 16+ showed its own Live Text / Visual Look Up button on a paused frame
that contains text, on top of custom overlays. It is now off by default;
set the flag to true to get Apple's behavior back. Applies to every
AVPlayerViewController the plugin shows (inline, shared, Dart and native
fullscreen). Android ignores it.playback audio session on every return
to the foreground, on backgrounding, when an interruption ended and when Now
Playing was refreshed — also for a paused video. Pausing a video, resuming
Spotify and returning to the app therefore stopped Spotify again. The session
is now only (re)activated while the player is playing, in Picture-in-Picture
or on AirPlay.shouldResume, the player resumed even if it had
been paused before the interruption began. It now only resumes playback the
user had actually started.AUDIOFOCUS_LOSS; the
next play() requests focus again. `reusePort` not supported on this platform (#51, #52). mDNS sockets
are bound without reusePort, which Android does not support.UIApplication.keyWindow, which does not follow the
scene life cycle. They now resolve the key window through the
foreground-active window scene. The example app is migrated to UIScene.await inside try in the subtitle loader, null-aware map entries).One column per month.
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01WfGToK4RaKH6aMbTGgYBdB
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01WfGToK4RaKH6aMbTGgYBdB
isInitialized from that view is never delivered. The next view
is created while the previous native view still holds the shared player and
therefore counts as a shared player, whose listener only replayed playback
state when an item was loaded — with nothing loaded it sent nothing,
initialize() never completed, and the player showed a spinner until the
load watchdog turned it into an error. A shared player without an item now
sends isInitialized like a new one (Android already sent idle here).iOS: an item starts with subtitles off instead of with no opinion. 1.5.3 stopped AVFoundation picking a legible rendition from the system language and
appliesMediaSelectionCriteriaAutomatically = false does not cover the manifest: an HLS caption rendition flagged
DEFAULT was still selected by the item itself as soon as the legible group
resolved. Loading a source now records "off" as the controller's selection, so
that rendition is corrected away and captions appear only once the app calls
setSubtitleTrack. Apps that relied on a DEFAULT-flagged caption track
turning itself on must select it explicitly.AVPlayerViewController
was created, which is before AVKit runs its own media selection during
presentation — so the correction landed first and AVKit's pick stood. The
item's currentMediaSelection is now the trigger: any selection that deviates
from the recorded choice is put back whenever it happens, so attach-time
selection loses regardless of its timing. A view showing native playback
controls is exempt after its first correction, so a pick from AVKit's own CC
menu still sticks.setSubtitleTrack then failed with NO_SUBTITLES without recording the
choice — leaving nothing to oppose the manifest's DEFAULT rendition for the
rest of the session, since the per-controller re-apply had nothing to re-apply.
The choice is now recorded and applied as soon as the group shows up, and the
call reports success rather than an error the Dart side only logged.currentMediaSelection observer ran wherever AVFoundation happened to be,
touching per-view report state and the Flutter event sink off the platform
thread.iOS: embedded subtitles no longer switch themselves on when the player enters fullscreen (or when a second inline view / the native fullscreen control
AVPlayer). Each attachment re-ran AVKit's
automatic media selection, which picked a legible rendition from the system
language / accessibility caption settings on top of the app's explicit
"off". Shared players now run with appliesMediaSelectionCriteriaAutomatically = false, the app's last setSubtitleTrack choice is remembered per
controller and re-applied after every view attachment, and the Dart
controller re-sends it to each newly created platform view.subtitleTrackChanged is now emitted for native-initiated subtitle
changes (the item's currentMediaSelection is observed), so an app's
subtitle picker can mirror what the player actually renders instead of
trusting its own last write.NativeVideoPlayerSubtitleTrack.toMap()/fromMap() round-trip source, so
a sidecar selection delivered through PlayerControlState.subtitleTrackChanged
is no longer mistaken for an embedded track (old payloads without source
still decode as embedded).The PiP close-button detection sampled the Flutter lifecycle once, 300ms after the PiP flag flipped off. On expand-back, resumed often arrives later t
The PiP close-button detection sampled the Flutter lifecycle once,
300ms after the PiP flag flipped off. On expand-back, resumed often
arrives later than that (the exit animation has to finish and the
window regain focus first), so returning to the app was misclassified
as an X-button dismissal and playback was paused. Wait up to 2s for
resumed (bailing out early on dispose or PiP re-entry) and only
pause when it never arrives. Prep 1.5.2.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_012u551tgpy3zxxjtvjBx77x
resumed often
arrives later than that (the exit animation has to finish first), so the
return was misclassified as a dismissal and playback was paused. The check
now waits up to 2 s for resumed and only pauses when it never arrives
(the genuine X-button dismiss case).Replace dismissible_page with built-in swipe-down dismiss and release…
Replace dismissible_page with built-in swipe-down dismiss and release…
dismissible_page dependency (#50). The fullscreen player's
swipe-down-to-dismiss is now a small built-in implementation with the same
behavior (drag follows the finger with scale/corner-radius/background-fade,
15% release threshold). dismissible_page had been unmaintained since 2023;
dropping it also fixes dependency resolution on pub mirrors that are missing
that package. No API changes.homepage/repository/issue_tracker) now points at the
canonical plug-and-pay/flutter-native-video-player repository.Android PiP could go silent two ways (HAB-836): the background guard read the stale 150ms-polled isPipEnabled at the paused/hidden transition and coul
Android PiP could go silent two ways (HAB-836): the background guard read
the stale 150ms-polled isPipEnabled at the paused/hidden transition and
could pause an active PiP session, and audio focus was abandoned the
instant isPlaying flipped false. The guard now confirms PiP via the new
ground-truth getPictureInPictureStatus() (and re-checks playback state
after the await), the Android PiP poll fires its first check at t=0, and
a plain pause defers the focus abandon by a 30s grace period (cancelled
on resume; stop/end/dispose abandon immediately).
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01DxVHwbqWBb1w27u2Sb1iB5
Playback resilience release: stalled-load watchdog, Android decoder fallback, iOS total-player cap, and PiP audio fixes.
NativeVideoPlayerConfig.loadTimeout (default 30 s) and bufferingTimeout (default off). The native load path only ever reports "ready" or an explicit player error — a stalled decoder emits neither, leaving apps on an infinite spinner. When a player now sits in initializing/loading (or, opted-in, buffering) past the timeout, the controller synthesizes the same error activity event a native failure produces (message: "Load timed out…"/"Buffering timed out…") and best-effort pauses the stuck pipeline, so app error/retry UI actually shows. null disables either watchdog.DefaultRenderersFactory.setEnableDecoderFallback(true): when the primary hardware decoder fails to initialize, ExoPlayer falls back to the next decoder instead of failing playback.androidForceSoftwareDecoders (opt-in, default off). Restricts MediaCodec selection to software decoders (OMX.google.* / c2.android.*) via a custom MediaCodecSelector, falling back to the default list when no software decoder exists for a mime type. Escape hatch for devices whose vendor hardware decoders crash; expect higher CPU use and possibly dropped frames on high-res content.NativeVideoPlayerConfig.iosMaxTotalPlayers (default 6). maxConcurrentPlayingPlayers bounds playing players, but paused players keep their AVPlayerItem alive — and iOS's finite decode pipeline makes NEW items fail once enough accumulate. Creating a player beyond the cap now tears down the least-recently-used player that is not playing, not in PiP, not on AirPlay, and not texture-backed (soft cap: active playback is never killed). The evicted controller is notified (playerEvicted) and transparently re-loads its last source at the eviction position on its next play().getPictureInPictureStatus(). Ground-truth PiP query: isPipEnabled is event-driven on iOS but poll-derived on Android (150 ms, fullscreen-only), so it can be stale exactly when the app backgrounds into PiP. The new call queries the platform directly on Android and refreshes the flag.BackgroundPlaybackGuard read the stale polled isPipEnabled on the paused/hidden transition and could pause an active PiP session — it now confirms via getPictureInPictureStatus() and re-checks the playback state after the await; the Android PiP poll also fires its first check immediately instead of after the first 150 ms tick. (2) Audio focus was abandoned the instant isPlaying flipped false — a plain pause now defers the abandon by a 30 s grace period (cancelled on resume; stop/end/dispose still abandon immediately), so a transient pause during the PiP transition no longer surrenders the audio session.Embedded caption text-size scaling ( #43 ): NativeVideoPlayerSubtitleStyle.embeddedTextScale (default 1.0 = platform default) and the convenience meth
NativeVideoPlayerSubtitleStyle.embeddedTextScale (default 1.0 = platform default) and the convenience method controller.setNativeSubtitleTextScale(double) scale the text size of EMBEDDED (native-rendered) subtitle tracks — ExoPlayer's SubtitleView on Android (both the lightweight and PlayerView display paths), AVPlayerItem.textStyleRules on iOS. Takes effect live without a playback restart, survives quality/audio/variant switches, item reloads, and native view recreation, and scales relative to the user's system caption size preference. Sidecar overlay sizing (subtitleStyle.fontSize) is unaffected.release: 1.3.2 — Android PiP disarm/pause fixes, monochrome notificat…
release: 1.3.2 — Android PiP disarm/pause fixes, monochrome notificat…
cancelOnLeavePiP). Previously the armed state leaked
permanently after one fullscreen visit, so any later app-leave entered PiP — even from
inline playback or with no video playing (Huddle HAB-837).ic_notification drawable, then the FCM default-notification-icon meta-data) instead of
the tinted launcher icon that rendered as a white square (#45).currentTime is omitted from the LOAD request unless explicitly set (#44).iOS Swift Package Manager build fix
Fixes a build failure on iOS when the host app uses Flutter's Swift Package Manager integration (issues #36, #42):
Swift Compiler Error: Cannot find 'npLog' in scope
.../ios/better_native_video_player/Sources/better_native_video_player/View/VideoPlayerView.swift
The plugin ships two parallel iOS source trees. CocoaPods compiles everything under ios/Classes/ (s.source_files = 'Classes/**/*.swift'), while the SwiftPM target (ios/better_native_video_player) symlinks each subdirectory of Classes/ into its Sources/ folder. Logging.swift — which defines npLog and is the only file at the top level of Classes/ — had no symlink, so it was excluded from the SwiftPM target. Any call site then failed to compile under a SwiftPM build. CocoaPods builds were never affected.
Added the missing Logging.swift symlink to the SwiftPM target. Verified by building the example app on an iOS simulator with flutter config --enable-swift-package-manager: it fails with the reported error before the change and builds cleanly after.
Bump to better_native_video_player: ^1.3.1. If you hit this on 1.3.0, also clear caches after upgrading — SwiftPM caches in DerivedData, or cd ios && pod deintegrate && pod install for CocoaPods.
Full changelog: https://github.com/plug-and-pay/flutter-native-video-player/blob/master/CHANGELOG.md
iOS Swift Package Manager build fix.
Cannot find 'npLog' in scope. The plugin keeps two parallel iOS source trees: CocoaPods compiles everything under Classes/ (s.source_files = 'Classes/**/*.swift'), while the Swift Package Manager target (ios/better_native_video_player) symlinks each subdirectory of Classes/ into its Sources/ folder. Logging.swift — which defines npLog and is the only file at the top level of Classes/ — had no symlink, so it was excluded from the SPM target and any call site (e.g. VideoPlayerView.swift) failed to compile under a SwiftPM build. CocoaPods builds were unaffected. Added the missing Logging.swift symlink to the SPM target. (#36, #42)Quality-selection and Android background-playback fixes.
Quality-selection and Android background-playback fixes.
#EXT-X-MEDIA:TYPE=AUDIO), that silenced the audio while video kept playing. The plugin now keeps the master loaded and constrains the video track via the player's track selector (preferredMaximumResolution/preferredPeakBitRate on iOS, setMaxVideoSize/setMaxVideoBitrate on Android), so audio is untouched and the switch is instant (no buffering reload). (#40)qualityChangedStream now emits the selected quality. Native emits the quality fields flat in the event map, but the Dart handler only accepted a nested quality key, so the stream stayed silent and any UI bound to it was stuck on "Auto". The handler now accepts both shapes. (#39)timeUpdate, clobbering position reporting. The monitor is removed — AVPlayer's native ABR handles Auto. (#40)UnknownHostException. Playback now runs as a real foreground service while playing (stopped on pause/stop), and the shared player uses setWakeMode(WAKE_MODE_NETWORK) (partial WakeLock + WifiLock). Adds the WAKE_LOCK permission to the plugin manifest. (#38)Full Changelog: v1.2.1...v1.3.0
Drives isPipEnabled on Android (floating pipStatus polled while fullscreen) so the Flutter caption overlay hides in PiP, and suppresses native embedde
Drives isPipEnabled on Android (floating pipStatus polled while fullscreen)
so the Flutter caption overlay hides in PiP, and suppresses native
embedded/sidecar captions; the prior selection is restored on exit.
iOS unchanged.
Also brings git in sync with the 1.2.0 subtitle e-learning work that was
published from an uncommitted tree.
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01FgKo3tRLn8XMJJDt68wbFa
Android Picture-in-Picture subtitle fix.
floating package's status while fullscreen — entering PiP hides the caption overlay and suppresses native (embedded/sidecar) caption rendering; exiting restores the previous selection. As a side effect, isPipEnabled / isPipEnabledStream now reflect PiP state on Android. iOS unchanged.Orientation- and fullscreen-aware sidecar caption sizing.
Orientation- and fullscreen-aware sidecar caption sizing.
NativeVideoPlayerSubtitleStyle gains three optional overrides — fullscreenLandscapeFontSize, fullscreenLandscapeFontWeight, and fullscreenLandscapeLineHeight — applied only while the player is fullscreen and in landscape orientation. Each falls back to the matching base value (fontSize / fontWeight / lineHeight) when null, so inline and fullscreen-portrait keep the base typography. The widget reads live MediaQuery orientation, so both the inline player and the Dart fullscreen route restyle on rotation.preferredOrientations through FullscreenManager.setPreferredOrientations, which immediately reset the device orientation — fighting the fullscreen player and snapping it out of landscape on rotation. FullscreenManager now tracks whether a player is fullscreen and, while one is, records a constructed player's preferred orientations as the restore baseline without applying them to the device. No effect on the single-player case.Re-release of 1.1.1 with a clean package archive — 1.1.1 accidentally bundled the local build/ directory (~13 MB). No code changes from 1.1.1.
Re-release of 1.1.1 with a clean package archive — 1.1.1 accidentally bundled
the local build/ directory (~13 MB). No code changes from 1.1.1.
Sidecar (VTT/SRT) subtitle fixes for fullscreen and outline rendering.
Sidecar (VTT/SRT) subtitle fixes for fullscreen and outline rendering.
Center + AspectRatio, matching the texture path's letterbox fit) instead of the full widget bounds. Previously, in portrait fullscreen with a 16:9 video, captions sat at the bottom of the screen — well below the letterboxed video.videoSize is now reported from platform-view players too (iOS AVPlayerItem.presentationSize KVO; Android Player.Listener.onVideoSizeChanged on the SurfaceView/PlayerView paths), so controller.videoSize / videoSizeStream is populated in all rendering modes — including Dart fullscreen, which the overlay needs to letterbox-match captions.subtitleStyle is forwarded into the Dart fullscreen player. FullscreenVideoPlayer (and the controller's _enterDartFullscreen) now pass the app's subtitleStyle to the fullscreen NativeVideoPlayer, so fullscreen captions match the inline player instead of falling back to the default style.outlineWidth >= 1. outlineWidth is now an even stroke width in logical pixels at any value.release: 1.1.0 — performance release (light views, disk cache + preca…
release: 1.1.0 — performance release (light views, disk cache + preca…
The performance release: every item of PERFORMANCE_ROADMAP.md is
implemented and verified on physical devices (Galaxy S21, iPhone 13 Pro
Max) — full before/after measurements live in the roadmap's "Final A/B
comparison" section. All new behavior is opt-in; defaults are unchanged
except for the two leak fixes below, which apply automatically.
NativeVideoPlayerConfig.lightweightInlineViews, default off): views created with native controls hidden (showNativeControls: false or a custom overlayBuilder) host a bare video surface — UIView + AVPlayerLayer on iOS instead of a per-tile AVPlayerViewController, SurfaceView + AspectRatioFrameLayout (+ SubtitleView for captions) on Android instead of a full Media3 PlayerView. Measured on the N=6 stress feed (iOS simulator): janky frames 37% → 24%, average frame total 9.1ms → 6.6ms. PiP, Now Playing, fullscreen, subtitles, and the native sidecar caption handoff keep working. Limitation: setShowNativeControls(true) at runtime is ignored for a view created lightweight.androidEnableDiskCache, default off): opt-in Media3 SimpleCache (LRU-evicted, androidDiskCacheMaxBytes, default 100MB) wrapped around playback and quality-switch data sources, plus NativeVideoPlayerCache.precache(url) to warm upcoming feed items up to androidPrecacheBytes (default 2MB; HLS precaching covers the playlists and leading segments). DRM and non-HTTP sources bypass the cache; cached items replay offline. Silent no-op on iOS.androidTextureMode / iosTextureMode, default off): inline tiles with hidden native controls render as Flutter textures — TextureRegistry.createSurfaceProducer() on Android (Impeller-compatible), FlutterTexture + AVPlayerItemVideoOutput on iOS (BT.709-correct, AES-HLS compatible). Feed scrolling stops fighting platform-view gesture claiming and hybrid-composition overhead disappears. Automatic platform-view fallback preserves every capability: tiles with automatic PiP stay (light) platform views; manual enterPictureInPicture() from a texture tile live-swaps the tile to a platform view (same shared player) before starting PiP; fullscreen falls back to the Dart fullscreen player; FairPlay DRM requires platform-view mode. New controller.videoSizeStream reports the natural video size (used for texture letterboxing, available in all modes on Android and in texture mode on iOS).NativeVideoPlayerConfig.viewportCapHeadroom (iOS, default 1.5): controls the headroom multiplier of the viewport quality cap. 1.5 keeps the first HLS variant at-or-above the tile size selectable (visually lossless); 1.0 maximizes savings.deinit could never run — each created view leaked along with its notification observers. The Dart widget now notifies the native side on platform-view disposal (viewDisposed), which deregisters the per-view channels and lets the view deallocate; KVO registration is now bookkept so teardown removes exactly what was added (re-loads no longer stack duplicate item/player observers either). Verified with heap(1): view counts return to baseline after open/close cycles, in both display modes.dispose() no longer leaks the native player when it races platform-view teardown. Disposing a controller routed the native release through the view-bound dispose call, which lands after the platform view unregisters when a tile unmounts (NO_VIEW, silently swallowed) — leaking one fully-buffered native player per disposed controller. On a Galaxy S21 this OOM-killed the app after a few feed visits (Java heap ratcheting ~+1.1MB per visit, MediaCodec allocation failure at the 256MB limit). dispose() now always issues the controller-ID-routed disposeController as the authoritative release (idempotent on both platforms). Measured after the fix: heap flat across six feed re-entries (6.2→6.3MB) vs the baseline's monotonic 4.6→12.6MB climb. Pre-existing on 1.0.1.Removed internal development notes from the repository and package archive; clarified a few doc comments. No functional changes.
Add video looping support with smooth native playback by @DaniKemper010 in #2
Full Changelog: https://github.com/plug-and-pay/flutter-native-video-player/commits/v1.0.0
First stable release. Everything below is additive — no breaking changes to the existing controller/widget API.
load(sidecarSubtitles:) or setSidecarSubtitles(), rendered in a fully styleable Flutter overlay (NativeVideoPlayerSubtitleStyle). Sidecar and embedded HLS tracks are merged into one list (getAvailableSubtitleTracks() / setSubtitleTrack()). On Android, sidecar sources are also sideloaded natively so captions stay visible in PiP and native fullscreen.getAvailableAudioTracks() / setAudioTrack() for multi-audio HLS streams, with an audioTrackChanged control event.package:better_native_video_player/cast.dart entrypoint): pure-Dart CASTV2 session — load with metadata and caption tracks, play/pause/seek/stop, volume/mute, caption switching, looping, and a live statusStream reflecting receiver-side changes. Device discovery uses the system Bonjour browser on iOS (works on physical devices without the restricted multicast entitlement) and pure-Dart mDNS elsewhere.VideoDownloadController with progress streams, persistent index, cancel/remove, and localPathFor() for offline playback.load(startAt:) applied natively before the first frame, plus PositionCheckpoints for throttled position persistence.setPlaybackRange(start, end, loop:) / clearPlaybackRange().NativeVideoPlayerPlaylist with auto-advance and per-item startAt.PlaybackAnalytics QoE event stream (startup time, stalls, watched time, quality switches, completion).BackgroundPlaybackGuard pauses on background and resumes on foreground (PiP/AirPlay exempt).StoryboardThumbnails parses WebVTT storyboards and uniform sprite grids (Vimeo/Bunny style); example ships a drag-to-preview seek bar.NativeVideoPlayerConfig with concurrent-playback caps, viewport-based HLS quality capping, buffer presets, and Android playback prioritization.better_native_video_extractor (in-repo): WebView-free Vimeo/YouTube extraction (Referer support, thumbnails, storyboards, expiry-aware cache, extraction-failure event stream).SocketException on physical iPhones; scans now run natively (iOS) and failures surface as a catchable CastDiscoveryException with actionable guidance.currentTime were treated as position 0; seeking exactly at the duration finished the stream).avc_url HLS variant (AVPlayer could fail to decode the default variant).Swipe to dismiss: Fullscreen video player can now be dismissed by swiping down.
Audio focus timing: Video now requests audio focus only when playback starts, not during initialization. Avoids unnecessary audio focus grabs when the
Initialization Logic Enhancement: Enhanced initialization logic in NativeVideoPlayerController
Android VideoPlayerObserver Refactoring: Refactored VideoPlayerObserver to avoid redundant loading events
onPlaybackStateChanged when the player reaches STATE_READYDRM (Digital Rights Management) Support: Comprehensive DRM support for protected content playback
drmConfig parameter in load(), loadUrl(), and loadFile() methodsEvent Thread Safety: Fixed event sending to ensure thread safety on both platforms
Event Thread Safety: Fixed event sending to ensure thread safety on both platforms
Handler(Looper.getMainLooper())DispatchQueue.main.asyncSubscription Cancellation Safety: Fixed potential exceptions during controller disposal
_safeCancelSubscription() method to gracefully handle MissingPluginException during disposalNative Controls Visibility Control: Added showNativeControls parameter to NativeVideoPlayerController constructor
showNativeControls parameter to NativeVideoPlayerController constructor
true to maintain backward compatibilityfalse, native controls are hiddenNativeVideoPlayerControllerHandler(Looper.getMainLooper())DispatchQueue.main.asyncCode Quality and Formatting: Enhanced code formatting and consistency across the controller
NativeVideoPlayerControllerload() method to use _isInitialized flag instead of state check
iOS Background Playback: Fixed video playback stopping when device goes to background or screen locks
audiovisualBackgroundPlaybackPolicy configuration to allow playback to continue in background (iOS 15.0+)Automatic Inline Picture-in-Picture: New methods to enable/disable automatic PiP when app goes to background
enableAutomaticInlinePip() method to automatically start PiP when app goes to backgrounddisableAutomaticInlinePip() method to turn off automatic PiP behaviorcanStartPictureInPictureAutomaticallyFromInline (requires iOS 14.2+)enterPictureInPicture() remain fully functionalsetAutomaticPiPEnabled() method to persist automatic PiP preference per controllerVersion bump and maintenance release
Subtitle/Closed Caption Support: Comprehensive subtitle functionality for HLS streams
NativeVideoPlayerSubtitleTrack model with language code and display namegetSubtitleTracks() method to retrieve available subtitle trackssetSubtitleTrack() method to select a specific subtitle track or disable subtitlesAVMediaSelectionGroup for native subtitle track managementSubtitlePickerModal widget with beautiful Material Design UIsubtitle_example_screen.dart with multiple subtitle examplesvideo_list_screen_with_players.dart with subtitle track accessNothing published for this version
AirPlay State Manager Initialization: Enhanced initialization flow to support app startup initialization
_initRequested flag to track initialization requests before controllers are createdAirPlayStateManager.init() can now be safely called at app startup before creating any video player controllersinit() was called earlierStateError exception when calling init() without available controllers_initRequested flag in dispose() methodinit() method documentation to clarify safe usage at app startupAndroid AirPlay Method Handlers: Fixed missing method handlers causing errors when calling AirPlay methods on Android
startAirPlayDetection() method handler (no-op on Android, returns success)stopAirPlayDetection() method handler (no-op on Android, returns success)disconnectAirPlay() method handler (no-op on Android, returns success)AirPlayStateManager.instance.init() on Androidinit(), showAirPlayPicker(), disconnectAirPlay(), dispose())HLS Live Stream Support: Full support for HLS live streams with indefinite duration
seekableTimeRanges to calculate duration and position for live contentTimeline.Window to handle dynamic, non-seekable live streamsiOS Autoplay Reliability: Enhanced autoplay functionality with proper initialization
prepareForPlayback() method that consolidates audio session, Now Playing info, and PiP setupiOS Background Audio During Screen Lock: Major improvements to maintain playback when screen locks
desiredPlaybackSpeed when resuming after lockiOS Audio Session Management: Enhanced audio session preparation for remote commands
Android Autoplay Timing: Fixed autoplay to happen after player is ready
player.play() call to execute only after STATE_READY is reachedVideoPlayerObserver after successful playHLS Live Stream Position: Fixed incorrect position and duration reporting for live streams
iOS Screen Lock Playback: Fixed video audio stopping when screen locks during playback
iOS Global AirPlay Device Detection: Enhanced AirPlay monitoring with global route detection
AirPlayStateManager.init() method to start global AirPlay device detection at app startupstartAirPlayDetection() and stopAirPlayDetection() native methods in SharedPlayerManagerAVRouteDetector monitors AirPlay device availability across the entire appsetMethodChannel() to enable event communication from shared manageriOS Audio Session Management: Major enhancements to background audio and lock screen playback
prepareAudioSession() method for centralized audio session configurationplayer.play() to ensure lock screen playbackhandleAppDidEnterBackground() observer to maintain audio session when screen locksiOS Audio Interruption Handling: Enhanced handling of phone calls, alarms, and other audio interruptions
shouldResume flag handlingAndroid Error Handling: Better PiP event listener error management
cancelOnError: false to prevent event stream from stopping on errorMissingPluginException casesAirPlayStateManager Lifecycle: Enhanced initialization and disposal
dispose() now stops AirPlay detection before cleaning up resourcesinit() provides clear API for starting AirPlay detection at app startupAirPlayStateManager.init() in code documentationprepareAudioSession() with detailed explanation of importanceAndroid PiP Enhancement: Integrated floating package (version 6.0.0) for improved Picture-in-Picture functionality on Android
floating package (version 6.0.0) for improved Picture-in-Picture functionality on Android
Version bump and maintenance release
Global AirPlay State Management: Complete refactor to use centralized AirPlay state across all video player instances
AirPlayStateManager singleton service for global AirPlay state managementairPlayDeviceName property with stream support to track connected AirPlay device nameisAirplayConnecting state to track connection progress between device selection and streamingAirPlayStateManager.instance.showAirPlayPicker() - Show AirPlay picker from anywhere in the appAirPlayStateManager.instance.disconnectAirPlay() - Disconnect from AirPlay globallycontroller.disconnectAirPlay() method for programmatic AirPlay disconnectioniOS AirPlay Device Name Detection: Enhanced reliability with robust retry mechanism
iOS AirPlay State Emission: Improved state synchronization across multiple controllers
iOS AirPlay Connection Detection: Enhanced initial connection state handling
iOS AirPlay Connecting State Logic: Fixed inconsistent state detection
player.isExternalPlaybackActive and system audio routeiOS AirPlay Connecting State: Fixed isAirPlayConnecting incorrectly showing true when connected and playing
isConnecting properly calculated as: systemActive AND NOT playerActive AND isConnectediOS Redundant State Updates: Prevented unnecessary global state updates with multiple controllers
updateConnection() and updateAvailability()AirPlayStateManagergetAirPlayDeviceName() method using AVAudioSession to detect device nameshandleAudioRouteChange() observer for device switching and state monitoringairPlayDeviceName and isAirplayConnecting propertiesairPlayConnectionChanged event to include deviceName parameterAirPlayStateManager)iOS Picture-in-Picture Multi-View Handling: Enhanced PiP functionality when using multiple views with shared controllers
iOS Picture-in-Picture Multi-View Handling: Enhanced PiP functionality when using multiple views with shared controllers
iOS Picture-in-Picture Reliability: Major improvements to PiP start/stop mechanisms
iOS Audio Session Interruption Handling: Added robust handling for audio session interruptions
iOS Now Playing Info Persistence: Comprehensive improvements to media control reliability
isPipActiveForController method to check PiP status across all views for a controlleriOS Remote Command Management: Enhanced reliability of media control registration
iOS Audio Session Activation: Enhanced audio session handling for Now Playing info
iOS Now Playing Controls: Fixed media controls becoming unresponsive after various transitions
iOS HDR Performance: Removed outdated HDR correction code for improved performance optimization
HDR Enabled by Default: Changed enableHDR default value from false to true
enableHDR default value from false to true
false if you prefer SDR tone-mapping for HDR contentiOS Media Info Persistence: Enhanced media info handling during Picture-in-Picture transitions
iOS Manual PiP vs Automatic PiP: Added tracking to prevent conflicts between manual and automatic PiP
controllersWithManualPiP tracking in SharedPlayerManageriOS PiP State Tracking: Enhanced PiP state detection and handling
isPipCurrentlyActive flag to VideoPlayerView for reliable PiP state trackingCode Quality: Code formatting improvements across Flutter controller
iOS Picture-in-Picture Event Delivery: Fixed issue where PiP stop events could fail when exiting PiP with disposed views
findAllViewsForController() method to SharedPlayerManager to locate active views for a controllerpictureInPictureController:restoreUserInterfaceForPictureInPictureStopWithCompletionHandler: to handle disposed viewsdeinit to send pipStop events before view disposalVideo Looping Support: Comprehensive video looping functionality with smooth native playback
Video Looping Support: Comprehensive video looping functionality with smooth native playback
enableLooping parameter to NativeVideoPlayerController constructor (default: false)setLooping(bool) method for dynamic looping control during playbackREPEAT_MODE_ONE for seamless native loopingMultiple Video Format Support: Extended support beyond HLS streams
ProgressiveMediaSource for non-HLS contentisHlsUrl() helper function for improved format detection on both platformsConvenience Loading Methods: New explicit methods for improved API clarity
loadUrl({required String url, Map<String, String>? headers}) for remote videosloadFile({required String path}) for local files (automatically constructs file:// URIs)load() method remains for backward compatibilityAsset Video Support: Local video file playback from Flutter assets
iOS Media Controls: Fixed critical issues with native media controls across multiple scenarios
RemoteCommandManager singleton to centralize ownership of remote commandsRemote Command Ownership Transfer: Automatic management between multiple views
iOS Now Playing Info Persistence: Fixed Now Playing info disappearing or showing wrong data
Race Conditions: Consolidated cleanup logic to prevent race conditions
HLS Detection: Improved quality switching and HLS stream detection
Android Auto Picture-in-Picture: Major enhancements to automatic PiP mode when pressing home button
Android Auto Picture-in-Picture: Major enhancements to automatic PiP mode when pressing home button
enterPictureInPictureInternal() method that handles both manual and automatic triggerssetAutoEnterEnabled(true) which was bypassing custom PiP logic and onUserLeaveHint callbackonStop() lifecycle method in example MainActivity to catch auto PiP on devices where onUserLeaveHint isn't reliably calledAndroid MainActivity Configuration: Cleaned up example app MainActivity
android:taskAffinity="" from AndroidManifest which could cause activity lifecycle issuesisInPipMode flag to track PiP state across lifecycle methodstryEnterAutoPip() method called from both onUserLeaveHint and onStopAndroid 12+ PiP Behavior: Fixed setAutoEnterEnabled causing Android to automatically enter PiP without proper custom logic
setSeamlessResizeEnabled for smooth transitions while maintaining control over PiP entryAuto PiP Reliability: Fixed auto PiP not working reliably on all Android devices
onStop() when onUserLeaveHint() isn't called (device-specific behavior)Video Completion Behavior: Enhanced video completion handling to provide better user experience
Video Completion Behavior: Enhanced video completion handling to provide better user experience
Android State Restoration After Exiting PiP: Enhanced state synchronization when exiting Picture-in-Picture mode
emitCurrentState() method to re-emit all player states after PiP exitState Restoration After Exiting PiP: Fixed issue where player state would not be properly restored after exiting Picture-in-Picture mode
emitCurrentState() method on both iOS and Android to re-emit all player states after PiP exitNativeVideoPlayerController
Code Quality (Android): Enhanced import organization in VideoPlayerMethodHandler
Enhanced Initialization Handling: Improved controller initialization with better state tracking
Enhanced Initialization Handling: Improved controller initialization with better state tracking
_isInitialized and _isInitializing flags to prevent duplicate initializationisInitialized getter to check initialization status programmaticallyImmediate State Emission for New Listeners: Event listeners now receive current state immediately upon registration
Duration Persistence During AirPlay: Fixed duration being lost during AirPlay transitions
Unified Event Name Handling: Added support for both 'timeUpdate' and 'timeUpdated' event names
Buffering State Debounce: Added 400ms debounce to buffering state changes to prevent UI flicker
debugPrint statements from the package
releaseResources()
_overlayBuilder is now preserved across releaseResources() calls_state, _url) for quick resumptiondispose(), not on temporary releaseResources()Continuous Buffering State Reporting: Buffering state is now always reported in real-time
isBuffering boolean to timeUpdate events (sent every 500ms)player.playbackState == Player.STATE_BUFFERINGplayer.timeControlStatus == .waitingToPlayAtSpecifiedRatePlayerActivityState.buffering when buffering startsqualitiesCache storage to SharedPlayerManager on both iOS and AndroidreleaseResources() and re-initializingBuffering Not Visible During Playback: Fixed issue where video would buffer while in play state without indicating buffering status
Missing Qualities After Navigation: Fixed issue where video qualities would disappear after navigating away and back
SharedPlayerManager which survives view recreationgetAvailableQualities() automatically restores from cache when view instance is emptyOverlay Lock Feature: Added ability to lock custom overlay to always be visible
lockOverlay() method to keep overlay permanently visibleunlockOverlay() method to restore normal tap-to-hide behaviorisOverlayLocked getter to check current lock stateisOverlayLockedStream for reactive lock state updates_state.isAirplayConnected when receiving airPlayConnectionChanged eventsisAirplayConnectedStream emits correctly when connection state changesAutomatic Overlay Management for PiP: Custom overlays now automatically hide when entering PiP and show when exiting PiP on Android
Enhanced Picture-in-Picture Transitions (Android): Major improvements to PiP mode behavior
wasFullscreenBeforePip tracking to restore correct state after PiPShared Player Surface Reconnection: Enhanced surface handling for shared players
releaseResources() is calledPiP Availability Detection: Improved activity context handling for PiP feature detection
getActivity() helper method to properly unwrap ContextWrapper instancesCustom Overlay in PiP Window: Fixed issue where custom overlays would appear in the PiP window on Android
Fullscreen State After PiP: Fixed incorrect fullscreen state restoration after exiting PiP
Progress Bar Seek Jump: Fixed issue where progress bar would jump back briefly after seeking
_targetSeekPosition to track where we're seeking toQuality List Stream: Added qualitiesStream to track changes in available video qualities
Quality List Stream: Added qualitiesStream to track changes in available video qualities
List<NativeVideoPlayerQuality> whenever quality list changescustom_video_overlay.dart demonstrates reactive quality selector updatesAutomatic Orientation Restoration: Enhanced FullscreenManager with intelligent orientation tracking
setPreferredOrientations() helper method as optional drop-in replacement for SystemChrome.setPreferredOrientations()preferredOrientations parameter to NativeVideoPlayerController for easy orientation configurationTap-to-Hide Overlay: Enhanced custom overlay interaction
HitTestBehavior.deferToChild for proper gesture handling_isDisposed flag to prevent state updates after disposalisClosed checks before adding events to all stream controllersbufferedPositionController, durationController, playerStateController, positionController, speedController, isPipEnabledController, isPipAvailableController, isAirplayAvailableController, isAirplayConnectedController, isFullscreenController, qualityChangedController, and qualitiesControllerbufferedPositionStream for reactive buffer position updatesqualitiesStream for reactive quality list updatesdart:async import for StreamSubscription supportNativeVideoPlayerController documentation with preferredOrientations usage examplesFullscreenManager documentation explaining automatic orientation trackingHDR Control: Added enableHDR parameter to NativeVideoPlayerController
HDR Control: Added enableHDR parameter to NativeVideoPlayerController
false to prevent washed-out/too-white video appearance on HDR contenttrue to enable HDR playback when desiredAirPlay Connection State Tracking: Enhanced AirPlay monitoring with connection state
isAirplayConnected property to NativeVideoPlayerStateisAirplayConnected getter to NativeVideoPlayerControllerisAirplayConnectedStream for real-time connection state updatesPlayerControlState events: airPlayConnected and airPlayDisconnectedConvenience State Getters: Added direct property access to controller state
speed - Current playback speedisPipEnabled - Current Picture-in-Picture stateisPipAvailable - PiP device availabilityisAirplayAvailable - AirPlay device availabilityisAirplayConnected - Active AirPlay connection stateNativeVideoPlayerState model with isAirplayConnected propertyIndividual Property Streams: Added dedicated broadcast streams for convenient property monitoring
Individual Property Streams: Added dedicated broadcast streams for convenient property monitoring
bufferedPositionStream - Stream of buffered position changesdurationStream - Stream of duration changesplayerStateStream - Stream of player state changes (playing, paused, buffering, etc.)positionStream - Stream of playback position changesspeedStream - Stream of playback speed changesisPipEnabledStream - Stream of Picture-in-Picture state changesisPipAvailableStream - Stream of Picture-in-Picture availability changesisAirplayAvailableStream - Stream of AirPlay availability changesisFullscreenStream - Stream of fullscreen state changesqualityChangedStream - Stream of quality changesToggle Picture-in-Picture: Added togglePictureInPicture() method for easy PiP toggling
bool indicating success/failure of the operationtoggleFullScreen() for consistencyEnhanced State Model: Extended NativeVideoPlayerState with new properties
speed property to track playback speedisPipEnabled property to track current PiP stateisPipAvailable property to track PiP device availabilityisAirplayAvailable property to track AirPlay device availabilitycopyWith(), operator==, and hashCode to include new propertiesImproved Event Handling: Enhanced event processing to update state properties
togglePictureInPicture() usage examplesaddActivityListener, addControlListener) continue to work as beforedispose() method to prevent memory leaksEnhanced Player Disposal and Cleanup: Improved SharedPlayerManager on both iOS and Android platforms
stopAllViewsForController() method to properly stop playback and clear player from all views when disposingNativeVideoPlayerControllerCritical Controller Disposal Bug: Fixed incomplete resource cleanup in NativeVideoPlayerController.dispose() method
Critical Controller Disposal Bug: Fixed incomplete resource cleanup in NativeVideoPlayerController.dispose() method
Android Player Reinitialization Issue: Fixed critical bug where Android players could not be reinitialized after disposal
VideoPlayerMethodHandler.handleDispose() to properly remove player from SharedPlayerManagercontrollerId parameter to VideoPlayerMethodHandler constructorVideoPlayerMethodChannel with new dispose() method that calls native platform disposaldispose() to call native cleanup via method channeldispose() method documentation to reflect proper disposal of both Flutter and native platform resourcesAndroid Media Info Management: Refactored VideoPlayerNotificationHandler and VideoPlayerObserver for improved media information management
iOS Automatic Picture-in-Picture for Shared Controllers: Fixed critical issue where automatic PiP would not work correctly when multiple views share t
isPrimaryView() and getPrimaryViewId() methods to prevent observer conflictsFixed Dart formatting issues in native_video_player_controller.dart and fullscreen_manager.dart to comply with pub.dev static analysis requirements
native_video_player_controller.dart and fullscreen_manager.dart to comply with pub.dev static analysis requirementsYour coding agent can read these notes before it upgrades. Set up the MCP server →