flutter_media_viewer 0.3.2
flutter_media_viewer: ^0.3.2 copied to clipboard
Cached image and video gallery viewer with Hero transitions and playback controls.
flutter_media_viewer #
A cached image and video gallery viewer for Flutter with Hero transitions, playback controls, thumbnails, zoom, swipe-to-dismiss, and metadata details.
Features #
- Displays images and videos in a full-screen gallery.
- Opens with a Hero transition from a ready-to-use preview widget.
- Shares cached network images between previews and the viewer.
- Supports network URLs, file-system paths, and
file:URIs. - Provides video play, pause, seek, mute, autoplay, and looping controls.
- Shows available video durations on previews and gallery thumbnails.
- Supports horizontal paging and a centered thumbnail strip.
- Supports pinch-to-zoom for images.
- Dismisses with an animated downward swipe.
- Shows structured technical and location metadata.
- Exposes optional share, delete, header, details, and custom actions.
- Opens as a route or a full-page modal bottom sheet.
- Accepts custom content and thumbnail builders.
- Inherits the active application theme and supports scoped viewer themes.
Installation #
flutter pub add flutter_media_viewer
Import the public library:
import 'package:flutter_media_viewer/flutter_media_viewer.dart';
Quick Start #
Create one or more media items and render a FlutterMediaViewerPreview:
final List<FlutterMediaViewerItem> items = <FlutterMediaViewerItem>[
const FlutterMediaViewerItem.image(
id: 'photo-1',
source: 'https://example.com/photo.jpg',
thumbnailSource: 'https://example.com/photo-thumbnail.jpg',
aspectRatio: 4 / 3,
title: 'Coast',
subtitle: 'Sunday, 4:53 PM',
),
];
FlutterMediaViewerPreview(
items: items,
initialIndex: 0,
borderRadius: BorderRadius.circular(12),
fit: BoxFit.cover,
);
FlutterMediaViewerPreview renders the selected item, owns the Hero transition,
and opens the viewer when tapped. Set useModalBottomSheet to false to open it
as a route instead. Previews use a 3 / 4 aspect ratio by default. Override the
preview independently for square grids or other layouts without changing the
media's intrinsic viewer ratio:
FlutterMediaViewerPreview(
items: items,
aspectRatio: 1,
);
Open Manually #
Use showModal when the preview already handles its own tap gesture:
await FlutterMediaViewer.showModal<void>(
context,
items: items,
initialIndex: selectedIndex,
config: FlutterMediaViewerConfig(
showThumbnails: items.length > 1,
closeTooltip: 'Close',
),
);
Use FlutterMediaViewer.show for route presentation, or place
FlutterMediaViewer directly in an existing widget tree.
Video #
Create a video item with its source and an optional thumbnail:
const FlutterMediaViewerItem.video(
id: 'video-1',
source: 'https://example.com/video.mp4',
thumbnailSource: 'https://example.com/video-thumbnail.jpg',
aspectRatio: 9 / 16,
);
The built-in player pauses when its page is no longer active. Playback behavior
is configured through FlutterMediaViewerConfig:
const FlutterMediaViewerConfig(
enableZoom: false,
enableVideoAutoplay: true,
enableVideoLooping: true,
showVideoControls: true,
startVideosMuted: true,
);
Disable enableZoom for galleries containing videos or custom gesture-heavy
content. Built-in videos start muted by default; set startVideosMuted to
false when sound should start immediately.
Cached Previews And Placeholders #
Use FlutterMediaPreview when only the media preview is needed. Use
FlutterMediaHero to wrap a completely custom preview while retaining the
viewer transition.
Each item can define cache keys, request headers, and file-system placeholders:
const FlutterMediaViewerItem.image(
id: 'photo-2',
source: 'https://example.com/photo-2.jpg',
thumbnailSource: 'https://example.com/photo-2-thumbnail.jpg',
localPlaceholderSource: '/temporary/photo-2.jpg',
thumbnailLocalPlaceholderSource: '/temporary/photo-2-thumbnail.jpg',
cacheKey: 'photo-2',
thumbnailCacheKey: 'photo-2-thumbnail',
httpHeaders: <String, String>{
'Authorization': 'Bearer token',
},
);
The local placeholder remains visible while the remote source loads and fades smoothly into the cached remote image.
Theming #
The viewer inherits the active ThemeData.colorScheme by default, including
the application's current light or dark mode.
Use themeMode only when a viewer must use a specific brightness independently
from the surrounding application:
const FlutterMediaViewerConfig(
themeMode: ThemeMode.dark,
);
Use FlutterMediaViewerThemeData for a complete per-viewer color scheme:
const FlutterMediaViewerConfig(
theme: FlutterMediaViewerThemeData(
colorScheme: ColorScheme.dark(
primary: Color(0xFF8AB4F8),
),
mediaBackgroundColor: Colors.black,
),
);
Add the same extension to an application's ThemeData.extensions to configure
all viewers under that theme:
ThemeData(
colorScheme: const ColorScheme.dark(),
extensions: const <ThemeExtension<dynamic>>[
FlutterMediaViewerThemeData(
colorScheme: ColorScheme.dark(),
),
],
);
The optional backgroundColor and controlsBackgroundColor configuration
values remain available as focused overrides. Leaving them unset allows the
viewer theme and application color scheme to resolve them.
Relative Sources #
Absolute HTTP(S) URLs are recommended. Relative sources can be resolved with
sourceUrlBuilder:
final FlutterMediaViewerConfig config = FlutterMediaViewerConfig(
sourceUrlBuilder: (String source) {
return Uri.parse('https://cdn.example.com/').resolve(source).toString();
},
);
The same resolver is used for full media and thumbnail sources.
Metadata #
Use FlutterMediaViewerMetadata for the built-in details panel:
const FlutterMediaViewerItem.image(
id: 'photo-3',
source: 'https://example.com/photo-3.jpg',
metadata: FlutterMediaViewerMetadata(
dateText: 'Sunday, September 6, 2026 at 4:53 PM',
fileName: 'IMG_3957.HEIC',
device: 'Phone camera',
format: 'HEIF',
camera: 'Wide camera',
summary: '12 MP - 3024 x 4032 - 3.3 MB',
badges: <String>['LIVE'],
exposure: <String>['ISO 125', '26 mm', 'f/1.8', '1/97 s'],
locationName: 'Coast',
latitude: 37.77,
longitude: -122.42,
),
);
All metadata values are display-ready. Empty fields and sections are omitted. The package validates coordinates but does not extract EXIF data, request location permission, geocode coordinates, or select a map provider.
Use locationPreviewBuilder to render a map preview and onLocationPressed to
handle the location row. Use detailsBuilder to replace the complete details
panel.
A Map<String, String> can be supplied as metadata when a simple list of
localized labels and values is sufficient.
Optional Actions #
Share and delete controls are shown only when their callbacks are configured:
final FlutterMediaViewerConfig config = FlutterMediaViewerConfig(
onSharePressed: handleShare,
onDeletePressed: handleDelete,
);
Callbacks receive the active FlutterMediaViewerOverlayInfo. File sharing,
confirmation, and deletion remain explicit so the package never changes stored
media by itself.
The package uses Hugeicons and a platform-aware BackButtonIcon as fallbacks.
Every built-in action icon can be replaced without rebuilding the action bars:
final FlutterMediaViewerConfig config = FlutterMediaViewerConfig(
onSharePressed: handleShare,
closeIconBuilder: (BuildContext context) => const Icon(Icons.arrow_back),
shareIconBuilder: (BuildContext context) => const Icon(Icons.ios_share),
infoIconBuilder: (BuildContext context) => const Icon(Icons.info_outline),
deleteIconBuilder: (BuildContext context) => const Icon(Icons.delete_outline),
playIconBuilder: (BuildContext context) => const Icon(Icons.play_arrow),
pauseIconBuilder: (BuildContext context) => const Icon(Icons.pause),
muteIconBuilder: (BuildContext context) => const Icon(Icons.volume_off),
unmuteIconBuilder: (BuildContext context) => const Icon(Icons.volume_up),
);
Configuration #
| Option | Default | Description |
|---|---|---|
showControlsInitially |
true |
Shows controls when the viewer opens. |
showThumbnails |
true |
Shows thumbnail navigation for galleries. |
showHeader |
true |
Shows non-empty item titles and subtitles. |
showCloseButton |
true |
Shows the platform-aware back button. |
enableSwipeDownToDismiss |
true |
Enables downward drag dismissal. |
backgroundColor |
null |
Overrides the media background, which defaults to black. |
controlsBackgroundColor |
null |
Overrides the themed controls and details surface. |
themeMode |
null |
Optionally forces light, dark, or system brightness. |
theme |
null |
Overrides the inherited FlutterMediaViewerThemeData. |
constrainedMediaBorderRadius |
24 px |
Radius applied only when tall media is constrained. |
imageFit |
BoxFit.contain |
Fit used by built-in media. |
thumbnailFit |
BoxFit.cover |
Fit used by built-in thumbnails. |
enableZoom |
true |
Enables InteractiveViewer. |
minScale |
1 |
Minimum zoom scale. |
maxScale |
4 |
Maximum zoom scale. |
enableVideoAutoplay |
true |
Autoplays the active video. |
enableVideoLooping |
true |
Loops built-in videos. |
showVideoControls |
true |
Shows playback, timeline, and mute controls. |
startVideosMuted |
true |
Starts built-in videos muted. |
showBottomActionBar |
true |
Allows configured bottom actions. |
sourceUrlBuilder |
null |
Resolves relative sources. |
topActionsBuilder |
null |
Builds additional top actions. |
bottomActionsBuilder |
null |
Builds additional bottom actions. |
onSharePressed |
null |
Shows and handles the share action. |
onDeletePressed |
null |
Shows and handles the delete action. |
detailsBuilder |
null |
Replaces the details panel. |
locationPreviewBuilder |
null |
Builds an optional location preview. |
onLocationPressed |
null |
Handles the location row. |
The close, share, info, delete, play, pause, muted, and unmuted icons each have
an optional WidgetBuilder in FlutterMediaViewerConfig. Leaving a builder
unset keeps the package fallback icon.
All visible labels and tooltips can be supplied through
FlutterMediaViewerConfig for localization.
Custom Content #
Use the base FlutterMediaViewerItem constructor with contentBuilder and
thumbnailBuilder for unsupported formats, DRM, or specialized rendering.
Built-in image and video rendering is used when those builders are omitted.
Layout Behavior #
Pass the media width-to-height aspectRatio whenever it is available. The
built-in image and video renderers can resolve it after loading when omitted.
Tall media is constrained only when its full-width height would overlap the top
or bottom controls. Its intrinsic aspect ratio determines the resulting width,
with rounded corners and a subtle border in that constrained state. Media that
fits remains full width. Opening details expands the active media over the
available upper area with BoxFit.cover; closing details restores its previous
frame and fit. Hiding the controls removes the constrained border and radius,
expands the media canvas to the available viewer area, and uses
BoxFit.contain for both images and videos.
Example #
The example directory demonstrates images, videos, metadata, thumbnails,
custom builders, actions, and both presentation modes.
License #
MIT License. See LICENSE.