1
0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2025-01-09 21:36:46 +01:00
immich/mobile/lib/widgets/photo_view/src/photo_view_wrappers.dart
Alex 5806a3ce25
refactor(mobile): widgets (#9291)
* refactor(mobile): widgets

* update
2024-05-06 23:04:21 -05:00

336 lines
9.7 KiB
Dart

import 'package:flutter/widgets.dart';
import '../photo_view.dart';
import 'core/photo_view_core.dart';
import 'photo_view_default_widgets.dart';
import 'utils/photo_view_utils.dart';
class ImageWrapper extends StatefulWidget {
const ImageWrapper({
super.key,
required this.imageProvider,
required this.loadingBuilder,
required this.backgroundDecoration,
required this.gaplessPlayback,
required this.heroAttributes,
required this.scaleStateChangedCallback,
required this.enableRotation,
required this.controller,
required this.scaleStateController,
required this.maxScale,
required this.minScale,
required this.initialScale,
required this.basePosition,
required this.scaleStateCycle,
required this.onTapUp,
required this.onTapDown,
required this.onDragStart,
required this.onDragEnd,
required this.onDragUpdate,
required this.onScaleEnd,
required this.onLongPressStart,
required this.outerSize,
required this.gestureDetectorBehavior,
required this.tightMode,
required this.filterQuality,
required this.disableGestures,
required this.errorBuilder,
required this.enablePanAlways,
required this.index,
});
final ImageProvider imageProvider;
final LoadingBuilder? loadingBuilder;
final ImageErrorWidgetBuilder? errorBuilder;
final BoxDecoration backgroundDecoration;
final bool gaplessPlayback;
final PhotoViewHeroAttributes? heroAttributes;
final ValueChanged<PhotoViewScaleState>? scaleStateChangedCallback;
final bool enableRotation;
final dynamic maxScale;
final dynamic minScale;
final dynamic initialScale;
final PhotoViewControllerBase controller;
final PhotoViewScaleStateController scaleStateController;
final Alignment? basePosition;
final ScaleStateCycle? scaleStateCycle;
final PhotoViewImageTapUpCallback? onTapUp;
final PhotoViewImageTapDownCallback? onTapDown;
final PhotoViewImageDragStartCallback? onDragStart;
final PhotoViewImageDragEndCallback? onDragEnd;
final PhotoViewImageDragUpdateCallback? onDragUpdate;
final PhotoViewImageScaleEndCallback? onScaleEnd;
final PhotoViewImageLongPressStartCallback? onLongPressStart;
final Size outerSize;
final HitTestBehavior? gestureDetectorBehavior;
final bool? tightMode;
final FilterQuality? filterQuality;
final bool? disableGestures;
final bool? enablePanAlways;
final int index;
@override
createState() => _ImageWrapperState();
}
class _ImageWrapperState extends State<ImageWrapper> {
ImageStreamListener? _imageStreamListener;
ImageStream? _imageStream;
ImageChunkEvent? _loadingProgress;
ImageInfo? _imageInfo;
bool _loading = true;
Size? _imageSize;
Object? _lastException;
StackTrace? _lastStack;
@override
void dispose() {
super.dispose();
_stopImageStream();
}
@override
void didChangeDependencies() {
_resolveImage();
super.didChangeDependencies();
}
@override
void didUpdateWidget(ImageWrapper oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.imageProvider != oldWidget.imageProvider) {
_resolveImage();
}
}
// retrieve image from the provider
void _resolveImage() {
final ImageStream newStream = widget.imageProvider.resolve(
const ImageConfiguration(),
);
_updateSourceStream(newStream);
}
ImageStreamListener _getOrCreateListener() {
void handleImageChunk(ImageChunkEvent event) {
setState(() {
_loadingProgress = event;
_lastException = null;
});
}
void handleImageFrame(ImageInfo info, bool synchronousCall) {
setupCB() {
_imageSize = Size(
info.image.width.toDouble(),
info.image.height.toDouble(),
);
_loading = false;
_imageInfo = _imageInfo;
_loadingProgress = null;
_lastException = null;
_lastStack = null;
}
synchronousCall ? setupCB() : setState(setupCB);
}
void handleError(dynamic error, StackTrace? stackTrace) {
setState(() {
_loading = false;
_lastException = error;
_lastStack = stackTrace;
});
assert(() {
if (widget.errorBuilder == null) {
throw error;
}
return true;
}());
}
_imageStreamListener = ImageStreamListener(
handleImageFrame,
onChunk: handleImageChunk,
onError: handleError,
);
return _imageStreamListener!;
}
void _updateSourceStream(ImageStream newStream) {
if (_imageStream?.key == newStream.key) {
return;
}
_imageStream?.removeListener(_imageStreamListener!);
_imageStream = newStream;
_imageStream!.addListener(_getOrCreateListener());
}
void _stopImageStream() {
_imageStream?.removeListener(_imageStreamListener!);
}
@override
Widget build(BuildContext context) {
if (_loading) {
return _buildLoading(context);
}
if (_lastException != null) {
return _buildError(context);
}
final scaleBoundaries = ScaleBoundaries(
widget.minScale ?? 0.0,
widget.maxScale ?? double.infinity,
widget.initialScale ?? PhotoViewComputedScale.contained,
widget.outerSize,
_imageSize!,
);
return PhotoViewCore(
imageProvider: widget.imageProvider,
backgroundDecoration: widget.backgroundDecoration,
gaplessPlayback: widget.gaplessPlayback,
enableRotation: widget.enableRotation,
heroAttributes: widget.heroAttributes,
basePosition: widget.basePosition ?? Alignment.center,
controller: widget.controller,
scaleStateController: widget.scaleStateController,
scaleStateCycle: widget.scaleStateCycle ?? defaultScaleStateCycle,
scaleBoundaries: scaleBoundaries,
onTapUp: widget.onTapUp,
onTapDown: widget.onTapDown,
onDragStart: widget.onDragStart,
onDragEnd: widget.onDragEnd,
onDragUpdate: widget.onDragUpdate,
onScaleEnd: widget.onScaleEnd,
onLongPressStart: widget.onLongPressStart,
gestureDetectorBehavior: widget.gestureDetectorBehavior,
tightMode: widget.tightMode ?? false,
filterQuality: widget.filterQuality ?? FilterQuality.none,
disableGestures: widget.disableGestures ?? false,
enablePanAlways: widget.enablePanAlways ?? false,
);
}
Widget _buildLoading(BuildContext context) {
if (widget.loadingBuilder != null) {
return widget.loadingBuilder!(context, _loadingProgress, widget.index);
}
return PhotoViewDefaultLoading(
event: _loadingProgress,
);
}
Widget _buildError(
BuildContext context,
) {
if (widget.errorBuilder != null) {
return widget.errorBuilder!(context, _lastException!, _lastStack);
}
return PhotoViewDefaultError(
decoration: widget.backgroundDecoration,
);
}
}
class CustomChildWrapper extends StatelessWidget {
const CustomChildWrapper({
super.key,
this.child,
required this.childSize,
required this.backgroundDecoration,
this.heroAttributes,
this.scaleStateChangedCallback,
required this.enableRotation,
required this.controller,
required this.scaleStateController,
required this.maxScale,
required this.minScale,
required this.initialScale,
required this.basePosition,
required this.scaleStateCycle,
this.onTapUp,
this.onTapDown,
this.onDragStart,
this.onDragEnd,
this.onDragUpdate,
this.onScaleEnd,
this.onLongPressStart,
required this.outerSize,
this.gestureDetectorBehavior,
required this.tightMode,
required this.filterQuality,
required this.disableGestures,
required this.enablePanAlways,
});
final Widget? child;
final Size? childSize;
final Decoration backgroundDecoration;
final PhotoViewHeroAttributes? heroAttributes;
final ValueChanged<PhotoViewScaleState>? scaleStateChangedCallback;
final bool enableRotation;
final PhotoViewControllerBase controller;
final PhotoViewScaleStateController scaleStateController;
final dynamic maxScale;
final dynamic minScale;
final dynamic initialScale;
final Alignment? basePosition;
final ScaleStateCycle? scaleStateCycle;
final PhotoViewImageTapUpCallback? onTapUp;
final PhotoViewImageTapDownCallback? onTapDown;
final PhotoViewImageDragStartCallback? onDragStart;
final PhotoViewImageDragEndCallback? onDragEnd;
final PhotoViewImageDragUpdateCallback? onDragUpdate;
final PhotoViewImageScaleEndCallback? onScaleEnd;
final PhotoViewImageLongPressStartCallback? onLongPressStart;
final Size outerSize;
final HitTestBehavior? gestureDetectorBehavior;
final bool? tightMode;
final FilterQuality? filterQuality;
final bool? disableGestures;
final bool? enablePanAlways;
@override
Widget build(BuildContext context) {
final scaleBoundaries = ScaleBoundaries(
minScale ?? 0.0,
maxScale ?? double.infinity,
initialScale ?? PhotoViewComputedScale.contained,
outerSize,
childSize ?? outerSize,
);
return PhotoViewCore.customChild(
customChild: child,
backgroundDecoration: backgroundDecoration,
enableRotation: enableRotation,
heroAttributes: heroAttributes,
controller: controller,
scaleStateController: scaleStateController,
scaleStateCycle: scaleStateCycle ?? defaultScaleStateCycle,
basePosition: basePosition ?? Alignment.center,
scaleBoundaries: scaleBoundaries,
onTapUp: onTapUp,
onTapDown: onTapDown,
onDragStart: onDragStart,
onDragEnd: onDragEnd,
onDragUpdate: onDragUpdate,
onScaleEnd: onScaleEnd,
onLongPressStart: onLongPressStart,
gestureDetectorBehavior: gestureDetectorBehavior,
tightMode: tightMode ?? false,
filterQuality: filterQuality ?? FilterQuality.none,
disableGestures: disableGestures ?? false,
enablePanAlways: enablePanAlways ?? false,
);
}
}