snap_reels 3.1.0
snap_reels: ^3.1.0 copied to clipboard
A powerful, customizable Flutter widget for creating TikTok/Instagram-style vertical video reels with advanced features like caching, analytics, and rich interactions.
3.1.0 #
New platforms #
- iOS, macOS, Linux, Windows, and Web are now declared in
flutter.plugin.platformswith no-op shim plugins. 3.0.0 declared only Android, which made pub.dev list the package as Android-only; 3.1.0 restores the wide platform coverage that pre-3.0.0 releases got transitively throughmedia_kit.
Documentation #
- CHANGELOG translated to English (was a mix of Russian and English).
- README clarifies the Android-emulator caveat: the underlying
media_kitworks in macOS / Linux / Windows / Web VMs and browsers, but Android's emulator codec path does not deliver a usable hardware decoder.
3.0.0 #
Major refactor across the public API, models, internals, and packaging. See the 2.x → 3.0.0 section in README.md for the field mapping table and a before/after code example.
Breaking changes #
ReelConfigflattened fields grouped into sub-configs. Colors / button visibility / sizes / builders / callbacks moved understyling: ReelStylingConfig,actions: ReelActionsConfig,builders: ReelOverlayBuildersConfig,callbacks: ReelInteractionCallbacks. See README migration table.- Video frame size:
Size→VideoSize. Avoids the clash withdart:ui.Size;VideoSource.dimensionsis nowVideoSize?. - Analytics bool parameters are named.
trackLike(reelId, position, {required bool isLiked}),trackFollow(..., {required bool isFollowing}),setEnabled({required bool enabled}). - Removed dead controller stubs:
toggleLike,incrementShare,downloadReel,blockUser,followUser, therefresh()override, andStreamingService.dispose(). Hosts already receive the events through callbacks /onRefresh. - Removed unwired
ReelConfig.preloadRange— configure preload viapreloadConfig: PreloadConfig(preloadAhead, preloadBehind). - Models live in dedicated files:
video_format.dart,video_size.dart,video_source.dart,reel_user.dart,reel_audio.dart.reel_model.dartre-exports them for source compatibility. ReelController.dispose()is now best-effort. It firesclose()without awaiting (Flutter's syncdispose()cannot await). Useawait controller.close()from a navigation hook when deterministic teardown is required.
New API #
- Android Flutter plugin packaging. Adds
android/consumer-rules.prowith media_kit / libmpv keep-rules merged automatically throughconsumerProguardFiles. Host apps can drop the duplicates from their ownproguard-rules.pro. ReelController.close()— awaitable teardown of native (libmpv) handles.ReelController.pauseAll()— pauses every player in the pool (used by hosts when the feed becomes hidden through a non-route mechanism like bottom-nav tab switch).ReelController.setAppVisibility()/setVisibility()split — independent app-lifecycle and widget-visibility flags. Resuming the app while the widget is still hidden by a tab no longer triggers playback.SnapReelItem,SnapReelsExtension.fromUrls— moved into separate files.SnapReelsExtension.fromUrlsre-exported through the package barrel.- Analytics shortcut extensions —
trackVideoStarted,trackLike, etc. moved intoAnalyticsServicePlaybackShortcuts/AnalyticsServiceInteractionShortcutsextensions. Re-exported throughanalytics_service.dartso call sites still work via the singleton. - Focused
*Utilsclasses —FormatUtils,ColorUtils,MathUtils,ResponsiveUtils,TimingUtils,StringUtils,PlatformUtils,VideoUrlUtils.ReelUtilsremains as a delegating facade so existing call sites compile unchanged.
Bug fixes #
_showCommentsBottomSheetleakedTextEditingController/FocusNode. Now wrapped intry/finallyso the controllers are disposed when the sheet closes.- Late visibility callback could pause a recycled player from the pool. Pause now revalidates
controller.getPlayerForReel(reel) == _assignedPlayerbefore callingplayer.pause(). - Follow action no longer dropped the
onFollowcallback. The default snackbar handler invokes the host callback after rendering. - Block / follow callbacks no longer crash for reels without a
user. Null-guard added before invoking host callbacks. - Cache LRU dual-budget bug. Removed the parallel
_evictIfOverCacheSizepath (hardcoded 200 MB) that ran alongside_enforceCacheSize(configurable). A singleCacheEvictor.evictToFitenforcesCacheConfig.maxCacheSize. - Cache alias double-counting and orphan entries. Aliases created by
linkCachedUrlstorefileSize: 0; eviction collapses entries byfilePathand removes every key sharing the deleted path. evictToFitcould over-evict when a zero-size alias was the oldest entry for its path. Pre-aggregation by path is now done before sorting and budget subtraction.enablePullToRefreshwas broken. The internalrefresh()override only printed'Refresh called'; replaced with a direct call towidget.config.onRefreshfrom theRefreshIndicator.
Internal #
- All models migrated to
freezed; generated*.freezed.dart/*.g.dartmarkedlinguist-generatedin.gitattributes. - Lint set switched to
very_good_analysis; 207public_member_api_docsdocstrings added across the public API. - Large files decomposed:
reel_actions.dart,reel_overlay.dart,reel_progress_indicator.dart,analytics_service.dart,cache_manager.dart,snap_reels_widget.dart— split into focused widgets / services (ReelCommentsSheet,ReelMoreOptionsSheet,ReelMusicAvatar,FloatingHeartOverlay,ReelActionButton,ReelUserInfoOverlay,ReelBottomControls,ReelOverlayGestureLayer,ProgressTrackBar,ReelProgressTimeLabels,ScrubThumbnailPreview,CacheIndexStorage,CacheEvictor,AnalyticsSummary,AnalyticsCalculations,AnalyticsDeviceInfoCollector). ReelActionsandReelOverlayare nowStatelessWidgets; animation state moved into the dedicated child widgets.- README rewritten in English with a 2.x → 3.0.0 migration guide.
2.3.3 #
Bug fixes #
- Streaming video stalled after 1–2 seconds with no recovery. libavformat doesn't reconnect on transient HTTP errors or keep-alive close by default. A set of streaming tunables is now applied before
player.open():demuxer-lavf-o=reconnect=1,reconnect_streamed=1,reconnect_on_network_error=1,reconnect_delay_max=2,network-timeout=30,hwdec=auto-safe,force-seekable=yes,cache=yes,cache-secs=10,demuxer-readahead-secs=20. See media-kit/media-kit#959.
2.3.2 #
Bug fixes #
- Video stalled 1–2 seconds after start in Android release builds.
media_kit_video'sVideowidget rendered only after the first decoded frame; before that the tree held aSizedBox.shrink(). The libmpv surface texture is created with the widget, so in AOT release builds the player could initialize before Flutter insertedVideointo the tree, leaving libmpv without a render sink. The fix keepsVideoin the tree from the first build and hides the thumbnail on the first frame. See media-kit/media-kit#909.
2.3.1 #
Follow-up to the thumbnail cache from 2.3.0: prefetch adjacent thumbnails in parallel with video preload, host-side fallback URL hook, and cache aliasing between two URLs of the same resource.
Bug fixes #
- Adjacent reel thumbnails were not prefetched. In 2.3.0 the
CacheManagerwas initialized, but_preloadAdjacentVideosonly fed the decoder; thumbnails fetched onCachedThumbnailmount, so users saw the fallback placeholder during the network round-trip._PreloadManagerMixinnow firesCacheManager.downloadAndCachefor thumbnails in the window[currentIndex − thumbnailPreloadBehind; currentIndex + thumbnailPreloadAhead](defaults+5/-2). - No host-side recovery for thumbnail failures.
CachedThumbnailsilently fell into thefallbackwidget on network/decode errors. AddedReelConfig.thumbnailProxyUrlBuilder(String? Function(ReelModel)) andReelConfig.thumbnailLoadTimeout(default 3 s): when the primary URL doesn't deliver a frame in time or fails,CachedThumbnailretries with the builder URL. The builder receivesReelModelso hosts can derive the URL fromreel.id. - Re-opening a viewed reel went back to the network. If the primary fetch went through
thumbnailProxyUrlBuilder, the cache index stored only under the fallback key. The next mount hit a miss on the primary URL. After a successful fallback fetch,CachedThumbnailnow callsCacheManager.linkCachedUrl(primary, fallback)to register an alias entry that points at the samefilePath. The next mount returns the cached image synchronously.
New API #
ReelConfig.thumbnailProxyUrlBuilder—String? Function(ReelModel reel)?, defaultnull. Returningnullkeeps the primary URL.ReelConfig.thumbnailLoadTimeout—Duration, default3s. No effect whenthumbnailProxyUrlBuilderis unset.PreloadConfig.thumbnailPreloadAhead/thumbnailPreloadBehind—int, defaults5/2.0/0disables thumbnail prefetch.CacheManager.linkCachedUrl(aliasUrl, existingUrl)— adds a second cache index entry pointing at the samefilePath. The file is not copied.
Breaking changes #
CachedThumbnailconstructor:url: Stringreplaced byreel: ReelModelso the widget can hand the model toproxyUrlBuilder. Update direct uses ofCachedThumbnail(url:, fallback:)toCachedThumbnail(reel:, fallback:). Internal callers (reel_video_player,reel_error_overlay) are already updated.
2.3.0 #
New features #
ReelConfig.httpClient— optionalDio?forwarded toCacheManager.initialize(). Lets hosts share their HTTP stack (e.g.NativeAdapter+CronetEngine) with thumbnail / video prefetch, including connection pool, TLS session cache, and interceptors.ReelConfig.errorDialogBuilder— customize the full-screen error popup. The builder receivesReelModel,errorMessage, andonRetry/onCancelcallbacks.ReelConfig.bufferingBuilder— custom buffering indicator instead of the default spinner.- New public widgets —
ReelErrorOverlayandReelBufferingIndicatorextracted fromreel_overlay.dartas reusable building blocks; the builders above call them or host UI. ReelConfig.thumbnailFallbackBuilder— placeholder widget whenthumbnailUrlis missing or fails. ReceivesReelModelso hosts can render a content-specific placeholder.- Thumbnails go through
CacheManager. A new internalCachedThumbnailwidget replacesImage.networkinReelVideoPlayer. Thumbnails now share the hostDioand the disk LRU. - Configurable tap targets without changing visuals. Five new fields in
ReelConfig:actionMinTapTargetSize(default44),actionIconSize(28),likeButtonSize(32),actionSpacing(16),hashtagMinTapTargetSize(0). The min-tap-target sizes set the hit area viaContainer(constraints:, alignment: center)+HitTestBehavior.opaquewithout resizing the icon/text.
Bug fixes #
CacheManagerinitialization. Fixed a latent bug:_dio/_configwerelatebut never assigned, and_isInitializedwas afinal bool = false. EverydownloadAndCachecall was effectively a no-op (errors swallowed byFuture.microtask).CacheManager.instance.initialize()is now called fromReelController.initialize()with parameters fromReelConfig, is idempotent, and sets_isInitializedcorrectly.- Validation order in
ReelController.initialize. Emptyreelslist validation moved beforeMediaKit.ensureInitialized(), soSnapReels(reels: [])no longer requires libmpv on the host. - Black
Videono longer covers the thumbnail. Until the first decoded frame,ReelVideoPlayerkeepsVideohidden (subscribed toplayer.stream.width). Previously media_kit rendered a solid black background over the thumbnail during buffering and after errors. Buffering…no longer renders on top of the error dialog. The overlay usesif/else if: whenhasError == truethe buffering indicator is not shown.
Dependencies #
- Removed
share_plus(declared inpubspec.yamlbut unused). wakelock_plus^1.5.1 → ^1.6.0 (transitively pullswin32 ^6.0.0andpackage_info_plus ^10).device_info_plus^12.4.0 → ^13.0.0 (consumer API unchanged).- Environment raised to
Dart ^3.10.0,Flutter >=3.38.1.
Maintenance #
- Added
cache_manager_test.dartcoveringinitialize()idempotency and absence ofLateInitializationError. CachedThumbnail.errorBuilderupdated for theunnecessary_underscoreslint added in Dart ≥3.10.
2.2.0 #
New features #
ReelConfig.showHashtags— controls whether hashtag chips are rendered under the caption. Defaults totrue(backward compatible).
Bug fixes #
- SafeArea in the more menu —
_showMoreOptions()bottom sheet is now wrapped inSafeArea(top: false). The bottom menu items no longer hide behind the gesture navigation bar on devices like Samsung A54.
Maintenance #
- Bumped
connectivity_plus^7.1.1,lottie^3.3.3,lints^6.1.0.
2.1.3 #
Bug fixes #
- "No active player with ID" crash. When a pre-initialized
ReelControllerwas passed toSnapReels, the widget calledinitialize()again. The double init triggered_resetPool()mid-preload and surfaced asStateError: Bad state: No active player with ID 1from media_kit. The widget now skips re-initialization when the controller is already initialized. - Guard in
_preloadVideoand_initializeCurrentVideo— empty-pool and disposed checks added before any Player call, preventing races during teardown.
Maintenance #
- Replaced deprecated
flutter_lintswithlints: ^5.1.1. - Bumped
connectivity_plus^7.1.0,device_info_plus^12.4.0,share_plus^12.0.2,mockito^5.6.4,build_runner^2.13.1.
2.1.2 #
New features #
ReelConfig.contentBottomPadding— bottom inset for overlay content (user info, action stack). Use to lift content above a tab bar.showProgressIndicatornow works. The field existed in the config but wasn't honored by the overlay. Whenfalse, the progress bar is hidden and content shifts down into the freed space.
2.1.1 #
Performance #
- Adaptive pool size. Player pool size is derived from device class: low → 2, medium → 3, high → 4. High-end devices (Android SDK 31+, iPhone 11+) preload up to 2 videos ahead.
2.1.0 #
⚠️ Breaking changes #
- Migrated to
media_kit. Replacesvideo_playerwithmedia_kit+media_kit_video+media_kit_libs_video. Apps must callMediaKit.ensureInitialized()before use (called automatically fromReelController.initialize()). - Doesn't work on the Android emulator — media_kit doesn't support texture rendering on the emulator. Test on real devices only.
Performance #
- Player pool (3 slots). Replaces per-swipe create/dispose with a fixed pool of 3
Players. Each swipe callsplayer.open()on an existing Player so the hardware decoder is reused. - Slot recycling. On swipe, the furthest slot is recycled for the next preload — no native resource dispose/recreate.
Architecture #
reel_controller.dartdecomposition — 770 lines split into 5 part files:_reel_state_mixin.dart,_video_lifecycle_mixin.dart,_preload_manager_mixin.dart,_playback_mixin.dart, plus the thin orchestratorreel_controller.dart.
Bug fixes #
_isVideoInitializingrace. Cancelled inits (serial mismatch) no longer clear_isVideoInitializinginfinally, fixing a bug where videos didn't load after 4–5 fast swipes.- Re-initialization race. The pool is created once; repeated
initialize()calls run_resetPool()(stop + clear assignments) instead of dispose/recreate, removing the "Player has been disposed" error.
Maintenance #
- Removed
video_playerdependency. - Removed hardcoded
User-Agentfromcache_manager.dart. streaming_service.dartreduced toresolveStreamingUrl()(format selection only).reel_progress_indicator.dartswitched fromValueListenableBuilder<VideoPlayerValue>toObxovercurrentPosition/totalDuration.reel_overlay.dart— directVideoPlayerControlleraccess removed in favor of Rx getters.
2.0.0 #
⚠️ Breaking changes #
AwesomeReelsrenamed toSnapReels. Replace allAwesomeReels(...)usages withSnapReels(...).
Deprecations #
StreamingConfig.enableAdaptiveBitratemarked@Deprecated(no effect, to be removed in v3.0.0).
Performance #
- SHA-256 cache keys. Cache keys are now SHA-256 of the URL instead of
url.hashCode. Removes collisions and platform non-determinism on web. - URL normalization. CDN tokens (
token,sig,expires,auth, etc.) are stripped before hashing, so the same asset behind different signed URLs is cached once. - Memory pressure handling. On system memory-pressure signals all preloaded controllers are disposed and the in-memory cache cleared. Prevents OOM-kill on 2–3 GB devices.
- Adaptive preload. On low-end devices (Android SDK < 28, iPhone < 11)
preloadAheaddrops to 1 andpreloadBehindto 0, saving ~2 hardware decoders. - Preload prioritization.
nextis awaited first;previs fire-and-forget (80 % of scrolls go down). - Debounced preload. Fast scrolling skips intermediate-page preload via a 200 ms debounce.
- Serial-based init cancellation. Each
onPageChangedassigns a unique serial; superseded inits are cancelled and their controllers disposed. Fixes the bug where 4–5 rapid swipes left the player on a thumbnail-only state.
Maintenance #
- Added
crypto: ^3.0.6dependency for SHA-256. reel_config.dartsplit:CacheConfig/PreloadConfig→cache_config.dart,StreamingConfig→streaming_config.dart,VideoPlayerConfig→video_player_config.dart,ProgressIndicatorConfig→progress_config.dart. Public API unchanged.CacheItemandCacheStatsmoved tomodels/cache_item.dart.- Added
DeviceClassifier(utils/device_classifier.dart) withlow / medium / highclassification. - User-Agent changed from
AwesomeReels/1.0.0toSnapReels/1.3.0.
1.2.0 #
New Features #
peekNext()method onReelController— animate a partial scroll to preview the next reel and snap back, useful for onboarding hints
1.1.0 #
New Features #
- Thumbnail preview: show
thumbnailUrlas background image while video loads, replacing black screen with a smooth visual transition
1.0.0 #
Fork & Rename #
- Forked from flutter_awesome_reels by wailashraf71
- Published as
snap_reels— maintained independently
New Features #
appendReels()method onReelController— append new reels without reinitializing the controlleronReportTap,onBlockTap,onCopyLinkTapcallbacks inReelConfigwith customizable labels (reportLabel,blockLabel,copyLinkLabel)showCommentButtonparameter inReelConfig— conditionally show/hide comment button
Bug Fixes #
- Fixed hardware video decoder exhaustion: dispose far-away preloaded
VideoPlayerControllers during scroll, keeping max 3 alive (previous, current, next)
Maintenance #
- Updated
share_plusto ^12.0.0,connectivity_plusto ^7.0.0,device_info_plusto ^12.0.0,wakelock_plusto ^1.4.0 - Replaced deprecated
activeColorwithactiveThumbColorin example app - Fixed string interpolation in
ReelModel.toString()
0.0.5 #
🎨 Example App Redesign #
- Modern blue-gradient theme for Home and Playground screens
- New gradient feature cards, decorative orbs, and improved typography
- Refreshed buttons and section cards with professional look-and-feel
🔧 Core Playback Improvements #
- Ensure only a single video plays at a time when switching reels
- Pause and detach previous controller on page change and controller switch
- Preloaded controllers are initialized paused to prevent background audio
- Pause off-screen reels via visibility detection
🐛 Fixes #
- Mitigated black-screen-with-audio by guarding rendering until valid video size
- Cleaned up controller listeners on dispose/switch
- Replaced deprecated Color.withOpacity with Color.withValues
0.0.4 #
- Updated README.md to reflect new features and usage examples
0.0.3 #
⚠️ Breaking Changes #
- Multi-Format Video Support: Plugin now supports HLS, MPEG-DASH, and MP4 video sources. This may require changes to your video URLs and backend delivery.
- New Event Callbacks: Added
onPressandonLongPressevent callbacks for advanced interaction handling. Update your widget usage to handle these events if needed.
🎯 Major Progress Bar Overhaul #
-
Perfect Seeking Logic: Complete redesign of video seeking functionality
- Tap anywhere on progress bar for instant seeking
- Smooth drag-to-seek with real-time preview
- Release-to-seek mechanism for better user control
- Maintains playback state correctly (pause during drag, resume after)
-
Enhanced Draggable Thumb: Professional circular progress indicator
- Animated circular dot that grows during interaction
- Proper positioning using LayoutBuilder for accuracy
- Visual feedback with shadows, borders, and smooth animations
- 60px hit area for much easier touch interaction
-
Live Thumbnail Preview: Instagram/TikTok-style seeking preview
- Shows preview window above progress bar during drag
- Displays current time position in real-time
- Smart positioning to stay within screen bounds
- Elegant animations with scale and opacity effects
-
Production-Ready Video Controller: Robust video management system
- Index-based controller tracking instead of unreliable ID-based system
- Preloading and caching for instant video transitions
- Enhanced error handling with retry logic
- Optimized memory management and disposal
🎨 UI/UX Improvements #
- Better Visual Design: Light grey progress bar background for improved visibility
- Responsive Touch Areas: Increased hit areas with
HitTestBehavior.opaquefor better responsiveness - Smooth Animations: Multiple animation controllers for professional feel
- Performance Optimizations: Efficient
ValueListenableBuilderandLayoutBuilderusage
🚀 Crash Prevention & Stability #
- Removed All Loading Indicators: Eliminated stuck "loading..." states
- Robust Error Recovery: Comprehensive error handling prevents crashes
- Controller Lifecycle Management: Proper initialization and disposal
- Video State Tracking: Accurate playback state management
🔧 Technical Improvements #
- Flutter Analyze Clean: Fixed all critical errors and warnings
- Memory Optimizations: Efficient animation controller management
- Gesture Detection: Enhanced touch responsiveness and interaction
- Code Quality: Improved error handling and state management
🗑️ Removed #
- Loading Indicators: Removed all loading widgets that caused UI blocks
- Deprecated Dependencies: Updated to use
withValues()instead of deprecatedwithOpacity() - Complex Progress Classes: Simplified to essential, optimized components
🐛 Bug Fixes #
- Fixed thumb not moving during progress bar interaction
- Resolved thumbnail preview not showing during seek
- Improved drag sensitivity and touch responsiveness
- Fixed video controller initialization timing issues
- Corrected progress calculation and positioning bugs
0.0.2 #
🚀 New Features #
- Long-press Controls: Added long-press to pause/play functionality with proper state tracking
- Intelligent Retry System: Enhanced video loading with exponential backoff and automatic cache clearing on failures
- Configuration Enhancements: Added new ReelConfig options:
bookmarkInMoreMenu- Move bookmark button to more menu (default: true)downloadInMoreMenu- Move download button to more menu (default: true)followButtonColor- Configurable follow button color (default: white)followingButtonColor- Color when user is following (default: white70)
- Improved UI Organization: Better button placement with configurable more menu options
- Enhanced Comment System: Redesigned comment bottom sheet with improved UI and keyboard handling
- Live Preview Playground: Added mini reels player in playground screen for real-time configuration testing
🔧 Improvements #
- Follow Button Styling: Updated to use configurable colors instead of fixed accent color
- Error Handling: Better retry logic with cache management for failed video loads
- Comment Interface: Removed external dialog dependency, improved native comment experience
- Code Organization: Cleaned up duplicate imports and optimized widget structure
🗑️ Removed #
- Premium Features: Removed premium-only features to maintain open-source nature
- External Dependencies: Reduced reliance on external dialog packages
🐛 Bug Fixes #
- Fixed loading overlay persistence issues
- Improved video state management during long-press interactions
- Better handling of video controller lifecycle
0.0.1 #
- Initial release: TikTok/Instagram-style vertical video reels widget with caching, analytics, and rich interactions.