photo_view_plus 1.1.1 copy "photo_view_plus: ^1.1.1" to clipboard
photo_view_plus: ^1.1.1 copied to clipboard

Photo View provides a gesture sensitive zoomable widget. Photo View is largely used to show interactive images and other stuff such as SVG.

Flutter Photo View #

Pub

photo_view_plus provides a gesture-aware zoomable widget for images and custom content. This fork is updated for modern Flutter, adds a stronger typed API, and exposes new configuration points for overlays, gallery behavior, and interaction policies.

Requirements #

  • Flutter >=3.14.5
  • Dart >=3.1.0
dependencies:
  photo_view_plus: ^1.1.0

What's New #

  • Flutter 3.14.5+ and Dart 3.1+ baseline
  • typed scale API with PhotoViewScale.fixed(...)
  • new PhotoViewOptions for consolidated widget configuration
  • new PhotoViewGalleryOptions for gallery preload and retention
  • richer customization with overlayBuilder, backgroundBuilder, loadingStateBuilder, and errorStateBuilder
  • childWrapper for wrapping the final rendered widget in single view or gallery
  • new gesture callbacks: onLongPress, onScaleStart, and onScaleUpdate
  • disableDoubleTap to keep pan and pinch active while disabling the internal double tap zoom cycle
  • PhotoViewComputedScale.containedNoScaleUp for contain-fit without enlarging smaller assets
  • injectable PhotoViewInteractionPolicy for clamp, gesture-end, and dynamic filter quality rules
  • gallery page option caching and configurable image preloading
  • desktop/web pointer support with wheel pan, Ctrl+scroll zoom, trackpad pan/zoom, and contextual cursors
  • internal architecture split into ui/, domain/, data/, core/, and shared/

Basic Usage #

import 'package:photo_view_plus/photo_view_plus.dart';

PhotoView(
  imageProvider: const AssetImage('assets/large-image.jpg'),
  initialScale: PhotoViewScale.contained,
  minScale: PhotoViewScale.contained * 0.8,
  maxScale: PhotoViewScale.covered * 1.8,
);

Use PhotoView.customChild to zoom any widget:

PhotoView.customChild(
  child: const FlutterLogo(size: 200),
  childSize: const Size(200, 200),
  initialScale: const PhotoViewScale.fixed(1),
);

Configuration with PhotoViewOptions #

Prefer options for new integrations. Legacy constructor parameters still work and override values from options when both are provided.

PhotoView(
  imageProvider: const AssetImage('assets/large-image.jpg'),
  options: PhotoViewOptions(
    filterQuality: FilterQuality.high,
    strictScale: true,
    overlayBuilder: (context, details) => Align(
      alignment: Alignment.bottomRight,
      child: Text(details.scaleState.name),
    ),
  ),
);

PhotoViewOptions supports:

  • backgroundDecoration
  • wantKeepAlive
  • customSize
  • gestureDetectorBehavior
  • tightMode
  • filterQuality
  • disableGestures
  • disableDoubleTap
  • enablePanAlways
  • strictScale
  • interactionPolicy
  • overlayBuilder
  • backgroundBuilder
  • loadingStateBuilder
  • errorStateBuilder
  • childWrapper

New Interaction Hooks #

PhotoView(
  imageProvider: provider,
  options: const PhotoViewOptions(disableDoubleTap: true),
  onLongPress: (context, value) {
    debugPrint('long press at scale ${value.scale}');
  },
  onScaleStart: (context, details, value) {},
  onScaleUpdate: (context, details, value) {},
);

For desktop and web, mouse wheel pan, Ctrl + scroll zoom-to-cursor, and trackpad pan/zoom now work out of the box.

import 'package:photo_view_plus/photo_view_plus.dart';
import 'package:photo_view_plus/photo_view_plus_gallery.dart';

PhotoViewGallery.builder(
  itemCount: galleryItems.length,
  options: const PhotoViewGalleryOptions(
    preloadPagesCount: 2,
    pageRetentionPolicy: PhotoViewGalleryPageRetentionPolicy.keepAlive,
  ),
  builder: (context, index) {
    final item = galleryItems[index];
    return PhotoViewGalleryPageOptions(
      imageProvider: AssetImage(item.image),
      initialScale: PhotoViewScale.contained,
      heroAttributes: PhotoViewHeroAttributes(tag: item.id),
      options: PhotoViewOptions(
        overlayBuilder: (_, details) => Align(
          alignment: Alignment.topRight,
          child: Text(details.scaleState.name),
        ),
      ),
    );
  },
);

PhotoViewGalleryOptions adds:

  • preloadPagesCount
  • pageRetentionPolicy
  • scrollPhysics
  • scrollDirection
  • allowImplicitScrolling
  • pageSnapping
  • shared options for all pages
  • shared childWrapper for all pages

PhotoViewGalleryPageOptions now also accepts:

  • pageKey
  • options
  • childWrapper
  • disableDoubleTap
  • strictScale
  • onLongPress
  • onScaleStart
  • onScaleUpdate

Wrapping The Final Child #

Use childWrapper when you need interactive UI around the final rendered result instead of a passive overlay:

PhotoViewGallery.builder(
  itemCount: items.length,
  options: PhotoViewGalleryOptions(
    childWrapper: (context, index, child) {
      return Stack(
        fit: StackFit.expand,
        children: [
          child,
          Positioned(
            top: 16,
            right: 16,
            child: IconButton(
              onPressed: () {},
              icon: const Icon(Icons.more_vert, color: Colors.white),
            ),
          ),
        ],
      );
    },
  ),
  builder: (context, index) => PhotoViewGalleryPageOptions(
    imageProvider: AssetImage(items[index]),
  ),
);

Interaction Policies #

PhotoViewInteractionPolicy lets you customize interaction rules without forking the widget.

const policy = PhotoViewInteractionPolicy(
  filterQuality: defaultFilterQualityProvider,
  clampPosition: defaultClampPositionPolicy,
  onGestureEnd: defaultGestureEndPolicy,
);

You can replace:

  • position clamp behavior
  • post-gesture return/fling behavior
  • filter quality while gestures are active

Migration Guide #

1. Update SDK constraints #

Use Flutter >=3.14.5 and Dart >=3.1.0.

2. Replace dynamic scale values #

Old:

PhotoView(
  minScale: 0.8,
  maxScale: 3.0,
  initialScale: 1.0,
);

New:

PhotoView(
  minScale: const PhotoViewScale.fixed(0.8),
  maxScale: const PhotoViewScale.fixed(3.0),
  initialScale: const PhotoViewScale.fixed(1.0),
);

Viewport-relative scales still work:

PhotoView(
  minScale: PhotoViewComputedScale.contained * 0.8,
  maxScale: PhotoViewComputedScale.covered * 1.8,
  initialScale: PhotoViewScale.contained,
);

There is also a no-upscale fit mode:

PhotoView(
  imageProvider: provider,
  initialScale: PhotoViewComputedScale.containedNoScaleUp,
);

3. Move optional flags into options #

Old:

PhotoView(
  imageProvider: provider,
  filterQuality: FilterQuality.high,
  strictScale: true,
  enablePanAlways: false,
);

New:

PhotoView(
  imageProvider: provider,
  options: const PhotoViewOptions(
    filterQuality: FilterQuality.high,
    strictScale: true,
    enablePanAlways: false,
  ),
);

Old gallery-wide state retention relied on wantKeepAlive.

New code can use:

const PhotoViewGalleryOptions(
  preloadPagesCount: 2,
  pageRetentionPolicy: PhotoViewGalleryPageRetentionPolicy.keepAlive,
)

5. Adopt rich loading and error builders #

Old:

loadingBuilder: (context, event) => const CircularProgressIndicator(),
errorBuilder: (context, error, stackTrace) => const Icon(Icons.error),

New:

options: PhotoViewOptions(
  loadingStateBuilder: (context, details) {
    return CircularProgressIndicator(
      value: details.progress == null
          ? null
          : details.progress!.cumulativeBytesLoaded /
              details.progress!.expectedTotalBytes!,
    );
  },
  errorStateBuilder: (context, details) => Text('${details.error}'),
),

Breaking Changes Summary #

  • Flutter and Dart minimum versions increased
  • scale inputs are now typed as PhotoViewScale
  • new options objects are the preferred configuration path
  • gallery adds preload/retention concepts beyond wantKeepAlive

Controllers #

PhotoViewController exposes viewport state updates.
PhotoViewScaleStateController exposes scale-state transitions.

Both follow the standard Flutter controller lifecycle: create externally when needed, listen to their streams, and dispose them when no longer used.

Internal Architecture #

The package internals are organized as:

  • lib/src/ui/: widgets, view models, coordinators
  • lib/src/domain/: immutable models and interaction rules
  • lib/src/data/: image stream resolution
  • lib/src/core/: low-level rendering and layout primitives
  • lib/src/shared/: small reusable foundation utilities

This matters mainly for contributors. Public integrations should keep using the exports from lib/photo_view_plus.dart and lib/photo_view_plus_gallery.dart.

Validation #

The current package state is validated with:

  • flutter analyze
  • flutter test

The automated suite currently covers:

  • controller and scale-state controller behavior
  • typed scale resolution, including PhotoViewComputedScale.containedNoScaleUp
  • PhotoViewOptions hooks such as overlayBuilder and childWrapper
  • gesture callbacks: onLongPress, onScaleStart, and onScaleUpdate
  • disableDoubleTap behavior in single view and gallery page overrides
  • enablePanAlways drag behavior
  • gallery preload, page-option caching, and childWrapper
  • desktop/web pointer interactions: PointerScrollEvent pan, Ctrl + scroll zoom, and trackpad PointerPanZoomUpdateEvent
  • error-state rendering with heroAttributes still keeping a Hero in the tree

Example App #

Run the example locally:

flutter run -d <device> example/lib/main.dart

The gallery example demonstrates preload, retention, and overlay support.

1
likes
150
points
193
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Photo View provides a gesture sensitive zoomable widget. Photo View is largely used to show interactive images and other stuff such as SVG.

Homepage

License

MIT (license)

Dependencies

flutter

More

Packages that depend on photo_view_plus