viewfinder 1.0.2
viewfinder: ^1.0.2 copied to clipboard
Modern Flutter photo viewer / gallery. Pinch / double-tap / wheel zoom with rubber-band edges, fling, drag-to-dismiss, loop paging, thumbnails, page indicator, keyboard shortcuts.
Changelog #
1.0.2 #
- A zoomed page no longer flashes back to its initial scale while it slides away: the reset now waits until the pager settles, off screen.
1.0.1 #
- Trim the package description to pub.dev's 180-character limit.
1.0.0 #
First stable release; the public API is now covered by semantic versioning. Includes the unpublished 0.4.0 changes.
The library imports only package:flutter/widgets.dart — no Material dependency, ready for the Flutter 3.44 material/SDK split.
Breaking changes #
minScale/maxScale/doubleTapScales,ViewfinderImageController.scale/animateToScale, andonScaleChangedare relative to the initial scale (1.0= initial). Raw-matrix APIs stay absolute. Out-of-range bounds are rejected at construction, including per-item overrides.SwipeEdgeModeis removed.canSwipe/canSwipeTowardanswer the swipe-handoff question in screen space, the frame a pager scrolls in.ViewfinderImage.canPan/claimPanare replaced by a singlepanGatereturningViewfinderPanVerdict(release/compete/claim). TheZoomableCanPan/ZoomableClaimPantypedefs are gone.- An empty
doubleTapScalesalso disables double-tap-drag zoom.
Bug fixes #
- Edge handoff is direction-aware: a zoomed photo flush against one edge pans toward its hidden side instead of swiping pages, and such pans are no longer stolen by the pager or dismiss recognizers. Drag-to-dismiss is disabled while zoomed.
- Content that overflows the viewport at its initial state (a
contain/coverfactor above 1, or rotation) owns pager-axis pans and can fling, instead of being unreachable behind page swipes; edge hand-off still applies. - Drag-to-dismiss yields to pinches: a second finger landing before the drag is accepted hands both pointers to the zoom.
- Two moving fingers claim the gesture immediately, so the pager's drag can no longer steal a pinch off the first finger's drift.
reset()/animateToTransformfrom a rotated state no longer dip in scale mid-animation.contain(factor)/cover(factor)center the photo instead of anchoring it to the top-left.- The two-stage Android back / Esc no longer stays blocked after a zoom whose pan released the swipe lock.
- The thumbnail strip overlays the full-bleed viewer instead of reserving layout space — hiding the chrome reveals the whole photo; bottom/top indicators and chrome overlays are inset past the strip automatically.
reverse: trueand RTL layouts hand off to the correct neighbor page; the dots indicator and the thumbnail strip mirror their visual order to match.- Lifting one finger of a two-finger gesture keeps edge handoff for the remaining finger.
- Programmatic transform writes stop an in-flight fling/snap-back; starting a gesture stops a double-tap animation.
- The transform re-clamps when the viewport resizes and when
minScale/maxScalenarrow at runtime. - Swapping the current page's provider while zoomed no longer throws during build.
- Turning
loopon at runtime keeps the current page. - Provider-backed heroes fly a viewer-fit shuttle by default, so a pop flight no longer flickers against a differently-fitted source thumbnail.
gaplessPlaybackwith athumbImageno longer flashes the thumb on a provider swap.- Page indicators keep clear of system intrusions (
ViewfinderPageIndicator.safeArea, defaulttrue). - The thumbnail strip scrolls the selected tile to center on first render, including the strip's leading padding.
- Disabling dismiss mid-drag springs back instead of leaving the page displaced; the dismiss threshold signal re-arms when
onDismisskeeps the widget mounted, and a dismissal whose callback doesn't navigate away springs back instead of leaving the gallery dragged out. ViewfinderController.currentIndexis clamped after an out-of-rangeinitialIndex; a swapped-in controller adopts the current page.pageSpacingpads along the pager axis on vertical pagers.- Dismiss
threshold/onProgressdivide by the viewport height inslideType: onlyImage, matching the visuals. - Browser pinch zoom (
PointerScaleEvent) is handled; trackpad two-finger scroll follows the wheel-zoom setting, somouseWheelBehavior: pagingpages on trackpads too. mouseWheelBehavior: pagingsplits scrolling by axis: along the pager turns one page per scroll gesture (the momentum tail no longer stutters the transition or skips pages), across it zooms. The paging direction tracksreverse/ RTL like the arrow keys.- A tap followed by a horizontal drag swipes the page; double-tap-drag zoom claims only vertically dominant drags.
API additions #
loop: true— wrap-around paging;jumpTo/animateTotravel the shortest direction.ViewfinderHero.thumbnailFit— the default shuttle interpolates between the thumbnail's fit and the viewer's over the flight, landing exactly on the thumbnail's crop.Viewfinder.filterQuality— sampling quality for every page.onLongPress/onLongPressStart/onSecondaryTapUponViewfinderImageandViewfinderItem; forwarded byViewfinder.images(index-aware) andViewfinder.single.Viewfinder.singlealso forwardsrubberBandPan.Viewfinder.onScaleStateChanged— coalesced initial ⇄ zoomed transitions of the current page.ViewfinderImageController.canSwipeToward(AxisDirection);ViewfinderImageController.contentFits— whether the content currently fits the viewport.ViewfinderImageController.rotation/jumpToRotation/animateToRotation— programmatic rotation about a focal point, preserving scale and pan; the animation runs in angle space.ViewfinderImageController.jumpToScale— instant counterpart ofanimateToScale, for slider-style control.- Screen-reader page announcements (
announcePageChanges,pageAnnouncementBuilder); semantics on thumbnail tiles and the dots indicator (semanticLabelBuilder). - Reduce-motion support: animations jump when
MediaQuery.disableAnimationsis set. mouseWheelBehavior(.zoom/.paging);ViewfinderImage.enableMouseWheelZoomanddoubleTapDragZoomare public.decodeSizeMultiplier— decode pages and their precache at viewport × N physical pixels.dismissOnOverscroll— overscrolling past the first/last page dismisses.ViewfinderDismiss.onThresholdCrossed— edge-triggered threshold signal for haptics.keepAlivePages,restorationId,immersiveSystemUi.- Per-item
doubleTapScalesoverride. ViewfinderKeys— stablepage(i)/thumbnail(i)keys for widget tests.- Up/Down arrow keys navigate vertical pagers.
0.3.1 #
API additions #
ViewfinderThumbnails.errorBuilder— custom placeholder for thumbnail tiles whose provider fails to decode (default tile only;itemBuilderowns its own error handling).
0.3.0 #
Breaking changes #
ViewfinderImageController.canSwipeHorizontally/canSwipeVerticallygetters are replaced bycanSwipe(Axis axis). Migration:c.canSwipeHorizontally→c.canSwipe(Axis.horizontal).
API additions #
SwipeEdgeMode— frame-of-reference selector forcanSwipeunder rotation (removed again in 1.0.0).
0.2.1 #
API additions #
thumbCrossFadeCurveonViewfinderImage/ViewfinderItem(defaultCurves.easeOut).Viewfinder.images/Viewfinder.singleforwardthumbCrossFadeDuration,thumbCrossFadeCurve, andgaplessPlayback.
0.2.0 #
Breaking changes #
- Removed
ViewfinderInitialScale.value(scale)— identical tocontain(scale); use that instead. ViewfinderImage.child/ViewfinderItem.childrequire acontentKeyso the gallery can reset the in-page transform on a content swap. Any constant works for a single static child.
Bug fixes #
canSwipeHorizontally/canSwipeVerticallyreport the correct edge state under rotation.- Built
ViewfinderItems are no longer cached by index; dynamic galleries (re-order, swap-in) rebuild lazily. - The in-page transform resets when the content identity changes (provider
==/contentKey), instead of leaking to the new content.
API additions #
Viewfinder.imagesforwardsreverse,allowEdgeHandoff, andrubberBandPan.
Validation #
pagerAxis: Axis.verticalwith a non-nulldismissis rejected (both consume vertical drags).- Attaching one
ViewfinderImageControllerto multiple viewers trips a debug assert.
0.1.0 #
- Initial release.
Viewfindergallery (PageView-backed, horizontal or vertical) andViewfinderImage/.childsingle viewer.- Pinch, pan, double-tap ladder, double-tap-drag continuous zoom, opt-in two-finger rotation; post-release fling on pan and scale; rubber-band elastic edges.
ViewfinderInitialScale.contain([factor])/.cover([factor]).- Edge pans yield to the parent scrollable; mouse/trackpad drags swipe pages on web/desktop (
swipeDragDevices). ViewfinderItem.thumbImagelow-res preview cross-fade.ViewfinderThumbnails(4 positions,.custom()); sealedViewfinderPageIndicatorwithDots/Label/Adaptivevariants.ViewfinderDismissdrag-to-dismiss (wholePage/onlyImage,onProgress).ViewfinderChromeController— tap-to-toggle, auto-hide.- Keyboard navigation, two-stage Escape / Android back,
PopScope-based Hero coherence on pop. - Mouse wheel + trackpad pinch zoom;
precacheAdjacent; semantics labels;reverse;allowEdgeHandoff;rubberBandPan;onScaleStart/onScaleEnd;gaplessPlayback;currentTransform/jumpToTransform/animateToTransform.
