mirror of
https://github.com/immich-app/immich.git
synced 2026-02-28 09:38:43 +03:00
feat(mobile): inline asset details (#25952)
The existing implementation for showing asset details uses a bottom sheet, and is not in sync with the preview or scroll intent. Other apps use inline details, which is much cleaner and feels better to use.
This commit is contained in:
107
mobile/lib/widgets/map/asset_market_icon.dart
Normal file
107
mobile/lib/widgets/map/asset_market_icon.dart
Normal file
@@ -0,0 +1,107 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/images/remote_image_provider.dart';
|
||||
import 'package:immich_mobile/utils/image_url_builder.dart';
|
||||
|
||||
class AssetMarkerIcon extends StatelessWidget {
|
||||
const AssetMarkerIcon({required this.id, required this.thumbhash, super.key});
|
||||
|
||||
final String id;
|
||||
final String thumbhash;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final imageUrl = getThumbnailUrlForRemoteId(id);
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final pinHeight = constraints.maxHeight * 0.14;
|
||||
final pinWidth = constraints.maxWidth * 0.14;
|
||||
return SizedOverflowBox(
|
||||
size: Size(pinWidth, pinHeight),
|
||||
child: Stack(
|
||||
// alignment: AlignmentGeometry.center,
|
||||
children: [
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
left: constraints.maxWidth * 0.5,
|
||||
child: CustomPaint(
|
||||
painter: _PinPainter(
|
||||
primaryColor: context.colorScheme.onSurface,
|
||||
secondaryColor: context.colorScheme.surface,
|
||||
primaryRadius: constraints.maxHeight * 0.06,
|
||||
secondaryRadius: constraints.maxHeight * 0.038,
|
||||
),
|
||||
child: SizedBox(height: pinHeight, width: pinWidth),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: constraints.maxHeight * 0.07,
|
||||
left: constraints.maxWidth * 0.17,
|
||||
child: CircleAvatar(
|
||||
radius: constraints.maxHeight * 0.40,
|
||||
backgroundColor: context.colorScheme.onSurface,
|
||||
child: CircleAvatar(
|
||||
radius: constraints.maxHeight * 0.37,
|
||||
backgroundImage: RemoteImageProvider(url: imageUrl),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PinPainter extends CustomPainter {
|
||||
final Color primaryColor;
|
||||
final Color secondaryColor;
|
||||
final double primaryRadius;
|
||||
final double secondaryRadius;
|
||||
|
||||
const _PinPainter({
|
||||
required this.primaryColor,
|
||||
required this.secondaryColor,
|
||||
required this.primaryRadius,
|
||||
required this.secondaryRadius,
|
||||
});
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
Paint primaryBrush = Paint()
|
||||
..color = primaryColor
|
||||
..style = PaintingStyle.fill;
|
||||
|
||||
Paint secondaryBrush = Paint()
|
||||
..color = secondaryColor
|
||||
..style = PaintingStyle.fill;
|
||||
|
||||
Paint lineBrush = Paint()
|
||||
..color = primaryColor
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 2;
|
||||
|
||||
canvas.drawCircle(Offset(size.width / 2, size.height), primaryRadius, primaryBrush);
|
||||
canvas.drawCircle(Offset(size.width / 2, size.height), secondaryRadius, secondaryBrush);
|
||||
canvas.drawPath(getTrianglePath(size.width, size.height), primaryBrush);
|
||||
// The line is to make the above triangluar path more prominent since it has a slight curve
|
||||
canvas.drawLine(Offset(size.width / 2, 0), Offset(size.width / 2, size.height), lineBrush);
|
||||
}
|
||||
|
||||
Path getTrianglePath(double x, double y) {
|
||||
final firstEndPoint = Offset(x / 2, y);
|
||||
final controlPoint = Offset(x / 2, y * 0.3);
|
||||
final secondEndPoint = Offset(x, 0);
|
||||
|
||||
return Path()
|
||||
..quadraticBezierTo(controlPoint.dx, controlPoint.dy, firstEndPoint.dx, firstEndPoint.dy)
|
||||
..quadraticBezierTo(controlPoint.dx, controlPoint.dy, secondEndPoint.dx, secondEndPoint.dy)
|
||||
..lineTo(0, 0);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(_PinPainter old) {
|
||||
return old.primaryColor != primaryColor || old.secondaryColor != secondaryColor;
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/maplibrecontroller_extensions.dart';
|
||||
import 'package:immich_mobile/widgets/map/map_theme_override.dart';
|
||||
import 'package:immich_mobile/widgets/map/positioned_asset_marker_icon.dart';
|
||||
import 'package:immich_mobile/widgets/map/asset_market_icon.dart';
|
||||
import 'package:maplibre_gl/maplibre_gl.dart';
|
||||
|
||||
/// A non-interactive thumbnail of a map in the given coordinates with optional markers
|
||||
@@ -45,21 +45,12 @@ class MapThumbnail extends HookConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final offsettedCentre = LatLng(centre.latitude + 0.002, centre.longitude);
|
||||
final controller = useRef<MapLibreMapController?>(null);
|
||||
final styleLoaded = useState(false);
|
||||
final position = useValueNotifier<Point<num>?>(null);
|
||||
|
||||
Future<void> onMapCreated(MapLibreMapController mapController) async {
|
||||
controller.value = mapController;
|
||||
styleLoaded.value = false;
|
||||
if (assetMarkerRemoteId != null) {
|
||||
// The iOS impl returns wrong toScreenLocation without the delay
|
||||
Future.delayed(
|
||||
const Duration(milliseconds: 100),
|
||||
() async => position.value = await mapController.toScreenLocation(centre),
|
||||
);
|
||||
}
|
||||
onCreated?.call(mapController);
|
||||
}
|
||||
|
||||
@@ -90,11 +81,11 @@ class MapThumbnail extends HookConsumerWidget {
|
||||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(15)),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
alignment: AlignmentGeometry.topCenter,
|
||||
children: [
|
||||
style.widgetWhen(
|
||||
onData: (style) => MapLibreMap(
|
||||
initialCameraPosition: CameraPosition(target: offsettedCentre, zoom: zoom),
|
||||
initialCameraPosition: CameraPosition(target: centre, zoom: zoom),
|
||||
styleString: style,
|
||||
onMapCreated: onMapCreated,
|
||||
onStyleLoadedCallback: onStyleLoaded,
|
||||
@@ -109,17 +100,16 @@ class MapThumbnail extends HookConsumerWidget {
|
||||
attributionButtonMargins: showAttribution == false ? const Point(-100, 0) : null,
|
||||
),
|
||||
),
|
||||
ValueListenableBuilder(
|
||||
valueListenable: position,
|
||||
builder: (_, value, __) => value != null && assetMarkerRemoteId != null && assetThumbhash != null
|
||||
? PositionedAssetMarkerIcon(
|
||||
size: height / 2,
|
||||
point: value,
|
||||
assetRemoteId: assetMarkerRemoteId!,
|
||||
assetThumbhash: assetThumbhash!,
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
if (assetMarkerRemoteId != null && assetThumbhash != null)
|
||||
Container(
|
||||
width: width,
|
||||
height: height / 2,
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: SizedBox.square(
|
||||
dimension: height / 2.5,
|
||||
child: AssetMarkerIcon(id: assetMarkerRemoteId!, thumbhash: assetThumbhash!),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -3,8 +3,7 @@ import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/presentation/widgets/images/remote_image_provider.dart';
|
||||
import 'package:immich_mobile/utils/image_url_builder.dart';
|
||||
import 'package:immich_mobile/widgets/map/asset_market_icon.dart';
|
||||
|
||||
class PositionedAssetMarkerIcon extends StatelessWidget {
|
||||
final Point<num> point;
|
||||
@@ -36,106 +35,9 @@ class PositionedAssetMarkerIcon extends StatelessWidget {
|
||||
onTap: () => onTap?.call(),
|
||||
child: SizedBox.square(
|
||||
dimension: size,
|
||||
child: _AssetMarkerIcon(id: assetRemoteId, thumbhash: assetThumbhash, key: Key(assetRemoteId)),
|
||||
child: AssetMarkerIcon(id: assetRemoteId, thumbhash: assetThumbhash, key: Key(assetRemoteId)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _AssetMarkerIcon extends StatelessWidget {
|
||||
const _AssetMarkerIcon({required this.id, required this.thumbhash, super.key});
|
||||
|
||||
final String id;
|
||||
final String thumbhash;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final imageUrl = getThumbnailUrlForRemoteId(id);
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
return Stack(
|
||||
children: [
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
left: constraints.maxWidth * 0.5,
|
||||
child: CustomPaint(
|
||||
painter: _PinPainter(
|
||||
primaryColor: context.colorScheme.onSurface,
|
||||
secondaryColor: context.colorScheme.surface,
|
||||
primaryRadius: constraints.maxHeight * 0.06,
|
||||
secondaryRadius: constraints.maxHeight * 0.038,
|
||||
),
|
||||
child: SizedBox(height: constraints.maxHeight * 0.14, width: constraints.maxWidth * 0.14),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: constraints.maxHeight * 0.07,
|
||||
left: constraints.maxWidth * 0.17,
|
||||
child: CircleAvatar(
|
||||
radius: constraints.maxHeight * 0.40,
|
||||
backgroundColor: context.colorScheme.onSurface,
|
||||
child: CircleAvatar(
|
||||
radius: constraints.maxHeight * 0.37,
|
||||
backgroundImage: RemoteImageProvider(url: imageUrl),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PinPainter extends CustomPainter {
|
||||
final Color primaryColor;
|
||||
final Color secondaryColor;
|
||||
final double primaryRadius;
|
||||
final double secondaryRadius;
|
||||
|
||||
const _PinPainter({
|
||||
required this.primaryColor,
|
||||
required this.secondaryColor,
|
||||
required this.primaryRadius,
|
||||
required this.secondaryRadius,
|
||||
});
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
Paint primaryBrush = Paint()
|
||||
..color = primaryColor
|
||||
..style = PaintingStyle.fill;
|
||||
|
||||
Paint secondaryBrush = Paint()
|
||||
..color = secondaryColor
|
||||
..style = PaintingStyle.fill;
|
||||
|
||||
Paint lineBrush = Paint()
|
||||
..color = primaryColor
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 2;
|
||||
|
||||
canvas.drawCircle(Offset(size.width / 2, size.height), primaryRadius, primaryBrush);
|
||||
canvas.drawCircle(Offset(size.width / 2, size.height), secondaryRadius, secondaryBrush);
|
||||
canvas.drawPath(getTrianglePath(size.width, size.height), primaryBrush);
|
||||
// The line is to make the above triangluar path more prominent since it has a slight curve
|
||||
canvas.drawLine(Offset(size.width / 2, 0), Offset(size.width / 2, size.height), lineBrush);
|
||||
}
|
||||
|
||||
Path getTrianglePath(double x, double y) {
|
||||
final firstEndPoint = Offset(x / 2, y);
|
||||
final controlPoint = Offset(x / 2, y * 0.3);
|
||||
final secondEndPoint = Offset(x, 0);
|
||||
|
||||
return Path()
|
||||
..quadraticBezierTo(controlPoint.dx, controlPoint.dy, firstEndPoint.dx, firstEndPoint.dy)
|
||||
..quadraticBezierTo(controlPoint.dx, controlPoint.dy, secondEndPoint.dx, secondEndPoint.dy)
|
||||
..lineTo(0, 0);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(_PinPainter old) {
|
||||
return old.primaryColor != primaryColor || old.secondaryColor != secondaryColor;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,6 +257,7 @@ class PhotoView extends StatefulWidget {
|
||||
this.onDragStart,
|
||||
this.onDragEnd,
|
||||
this.onDragUpdate,
|
||||
this.onDragCancel,
|
||||
this.onScaleEnd,
|
||||
this.onLongPressStart,
|
||||
this.customSize,
|
||||
@@ -299,6 +300,7 @@ class PhotoView extends StatefulWidget {
|
||||
this.onDragStart,
|
||||
this.onDragEnd,
|
||||
this.onDragUpdate,
|
||||
this.onDragCancel,
|
||||
this.onScaleEnd,
|
||||
this.onLongPressStart,
|
||||
this.customSize,
|
||||
@@ -417,6 +419,9 @@ class PhotoView extends StatefulWidget {
|
||||
/// location.
|
||||
final PhotoViewImageDragUpdateCallback? onDragUpdate;
|
||||
|
||||
/// A callback when a drag gesture is canceled by the system.
|
||||
final VoidCallback? onDragCancel;
|
||||
|
||||
/// A pointer that will trigger a scale has stopped contacting the screen at a
|
||||
/// particular location.
|
||||
final PhotoViewImageScaleEndCallback? onScaleEnd;
|
||||
@@ -543,7 +548,7 @@ class _PhotoViewState extends State<PhotoView> with AutomaticKeepAliveClientMixi
|
||||
return LayoutBuilder(
|
||||
builder: (BuildContext context, BoxConstraints constraints) {
|
||||
final computedOuterSize = widget.customSize ?? constraints.biggest;
|
||||
final backgroundDecoration = widget.backgroundDecoration ?? const BoxDecoration(color: Colors.black);
|
||||
final backgroundDecoration = widget.backgroundDecoration ?? const BoxDecoration(color: Colors.transparent);
|
||||
|
||||
return widget._isCustomChild
|
||||
? CustomChildWrapper(
|
||||
@@ -564,6 +569,7 @@ class _PhotoViewState extends State<PhotoView> with AutomaticKeepAliveClientMixi
|
||||
onDragStart: widget.onDragStart,
|
||||
onDragEnd: widget.onDragEnd,
|
||||
onDragUpdate: widget.onDragUpdate,
|
||||
onDragCancel: widget.onDragCancel,
|
||||
onScaleEnd: widget.onScaleEnd,
|
||||
onLongPressStart: widget.onLongPressStart,
|
||||
outerSize: computedOuterSize,
|
||||
@@ -596,6 +602,7 @@ class _PhotoViewState extends State<PhotoView> with AutomaticKeepAliveClientMixi
|
||||
onDragStart: widget.onDragStart,
|
||||
onDragEnd: widget.onDragEnd,
|
||||
onDragUpdate: widget.onDragUpdate,
|
||||
onDragCancel: widget.onDragCancel,
|
||||
onScaleEnd: widget.onScaleEnd,
|
||||
onLongPressStart: widget.onLongPressStart,
|
||||
outerSize: computedOuterSize,
|
||||
|
||||
@@ -284,6 +284,7 @@ class _PhotoViewGalleryState extends State<PhotoViewGallery> {
|
||||
onDragStart: pageOption.onDragStart,
|
||||
onDragEnd: pageOption.onDragEnd,
|
||||
onDragUpdate: pageOption.onDragUpdate,
|
||||
onDragCancel: pageOption.onDragCancel,
|
||||
onScaleEnd: pageOption.onScaleEnd,
|
||||
onLongPressStart: pageOption.onLongPressStart,
|
||||
gestureDetectorBehavior: pageOption.gestureDetectorBehavior,
|
||||
@@ -321,6 +322,7 @@ class _PhotoViewGalleryState extends State<PhotoViewGallery> {
|
||||
onDragStart: pageOption.onDragStart,
|
||||
onDragEnd: pageOption.onDragEnd,
|
||||
onDragUpdate: pageOption.onDragUpdate,
|
||||
onDragCancel: pageOption.onDragCancel,
|
||||
onScaleEnd: pageOption.onScaleEnd,
|
||||
onLongPressStart: pageOption.onLongPressStart,
|
||||
gestureDetectorBehavior: pageOption.gestureDetectorBehavior,
|
||||
@@ -367,6 +369,7 @@ class PhotoViewGalleryPageOptions {
|
||||
this.onDragStart,
|
||||
this.onDragEnd,
|
||||
this.onDragUpdate,
|
||||
this.onDragCancel,
|
||||
this.onScaleEnd,
|
||||
this.onLongPressStart,
|
||||
this.gestureDetectorBehavior,
|
||||
@@ -397,6 +400,7 @@ class PhotoViewGalleryPageOptions {
|
||||
this.onDragStart,
|
||||
this.onDragEnd,
|
||||
this.onDragUpdate,
|
||||
this.onDragCancel,
|
||||
this.onScaleEnd,
|
||||
this.onLongPressStart,
|
||||
this.gestureDetectorBehavior,
|
||||
@@ -454,9 +458,12 @@ class PhotoViewGalleryPageOptions {
|
||||
/// Mirror to [PhotoView.onDragDown]
|
||||
final PhotoViewImageDragEndCallback? onDragEnd;
|
||||
|
||||
/// Mirror to [PhotoView.onDraUpdate]
|
||||
/// Mirror to [PhotoView.onDragUpdate]
|
||||
final PhotoViewImageDragUpdateCallback? onDragUpdate;
|
||||
|
||||
/// Mirror to [PhotoView.onDragCancel]
|
||||
final VoidCallback? onDragCancel;
|
||||
|
||||
/// Mirror to [PhotoView.onTapDown]
|
||||
final PhotoViewImageTapDownCallback? onTapDown;
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ class PhotoViewCore extends StatefulWidget {
|
||||
required this.onDragStart,
|
||||
required this.onDragEnd,
|
||||
required this.onDragUpdate,
|
||||
required this.onDragCancel,
|
||||
required this.onScaleEnd,
|
||||
required this.onLongPressStart,
|
||||
required this.gestureDetectorBehavior,
|
||||
@@ -62,6 +63,7 @@ class PhotoViewCore extends StatefulWidget {
|
||||
this.onDragStart,
|
||||
this.onDragEnd,
|
||||
this.onDragUpdate,
|
||||
this.onDragCancel,
|
||||
this.onScaleEnd,
|
||||
this.onLongPressStart,
|
||||
this.gestureDetectorBehavior,
|
||||
@@ -100,6 +102,7 @@ class PhotoViewCore extends StatefulWidget {
|
||||
final PhotoViewImageDragStartCallback? onDragStart;
|
||||
final PhotoViewImageDragEndCallback? onDragEnd;
|
||||
final PhotoViewImageDragUpdateCallback? onDragUpdate;
|
||||
final VoidCallback? onDragCancel;
|
||||
|
||||
final PhotoViewImageLongPressStartCallback? onLongPressStart;
|
||||
|
||||
@@ -386,6 +389,7 @@ class PhotoViewCoreState extends State<PhotoViewCore>
|
||||
onDragUpdate: widget.onDragUpdate != null
|
||||
? (details) => widget.onDragUpdate!(context, details, widget.controller.value)
|
||||
: null,
|
||||
onDragCancel: widget.onDragCancel,
|
||||
hitDetector: this,
|
||||
onTapUp: widget.onTapUp != null ? (details) => widget.onTapUp!(context, details, value) : null,
|
||||
onTapDown: widget.onTapDown != null ? (details) => widget.onTapDown!(context, details, value) : null,
|
||||
|
||||
@@ -16,6 +16,7 @@ class PhotoViewGestureDetector extends StatelessWidget {
|
||||
this.onDragStart,
|
||||
this.onDragEnd,
|
||||
this.onDragUpdate,
|
||||
this.onDragCancel,
|
||||
this.onLongPressStart,
|
||||
this.child,
|
||||
this.onTapUp,
|
||||
@@ -34,6 +35,7 @@ class PhotoViewGestureDetector extends StatelessWidget {
|
||||
final GestureDragEndCallback? onDragEnd;
|
||||
final GestureDragStartCallback? onDragStart;
|
||||
final GestureDragUpdateCallback? onDragUpdate;
|
||||
final GestureDragCancelCallback? onDragCancel;
|
||||
|
||||
final GestureTapUpCallback? onTapUp;
|
||||
final GestureTapDownCallback? onTapDown;
|
||||
@@ -73,7 +75,8 @@ class PhotoViewGestureDetector extends StatelessWidget {
|
||||
instance
|
||||
..onStart = onDragStart
|
||||
..onUpdate = onDragUpdate
|
||||
..onEnd = onDragEnd;
|
||||
..onEnd = onDragEnd
|
||||
..onCancel = onDragCancel;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ class ImageWrapper extends StatefulWidget {
|
||||
required this.onDragStart,
|
||||
required this.onDragEnd,
|
||||
required this.onDragUpdate,
|
||||
required this.onDragCancel,
|
||||
required this.onScaleEnd,
|
||||
required this.onLongPressStart,
|
||||
required this.outerSize,
|
||||
@@ -62,6 +63,7 @@ class ImageWrapper extends StatefulWidget {
|
||||
final PhotoViewImageDragStartCallback? onDragStart;
|
||||
final PhotoViewImageDragEndCallback? onDragEnd;
|
||||
final PhotoViewImageDragUpdateCallback? onDragUpdate;
|
||||
final VoidCallback? onDragCancel;
|
||||
final PhotoViewImageScaleEndCallback? onScaleEnd;
|
||||
final PhotoViewImageLongPressStartCallback? onLongPressStart;
|
||||
final Size outerSize;
|
||||
@@ -203,6 +205,7 @@ class _ImageWrapperState extends State<ImageWrapper> {
|
||||
onDragStart: widget.onDragStart,
|
||||
onDragEnd: widget.onDragEnd,
|
||||
onDragUpdate: widget.onDragUpdate,
|
||||
onDragCancel: widget.onDragCancel,
|
||||
onScaleEnd: widget.onScaleEnd,
|
||||
onLongPressStart: widget.onLongPressStart,
|
||||
outerSize: widget.outerSize,
|
||||
@@ -233,6 +236,7 @@ class _ImageWrapperState extends State<ImageWrapper> {
|
||||
onDragStart: widget.onDragStart,
|
||||
onDragEnd: widget.onDragEnd,
|
||||
onDragUpdate: widget.onDragUpdate,
|
||||
onDragCancel: widget.onDragCancel,
|
||||
onScaleEnd: widget.onScaleEnd,
|
||||
onLongPressStart: widget.onLongPressStart,
|
||||
gestureDetectorBehavior: widget.gestureDetectorBehavior,
|
||||
@@ -281,6 +285,7 @@ class CustomChildWrapper extends StatelessWidget {
|
||||
this.onDragStart,
|
||||
this.onDragEnd,
|
||||
this.onDragUpdate,
|
||||
this.onDragCancel,
|
||||
this.onScaleEnd,
|
||||
this.onLongPressStart,
|
||||
required this.outerSize,
|
||||
@@ -313,6 +318,7 @@ class CustomChildWrapper extends StatelessWidget {
|
||||
final PhotoViewImageDragStartCallback? onDragStart;
|
||||
final PhotoViewImageDragEndCallback? onDragEnd;
|
||||
final PhotoViewImageDragUpdateCallback? onDragUpdate;
|
||||
final VoidCallback? onDragCancel;
|
||||
final PhotoViewImageScaleEndCallback? onScaleEnd;
|
||||
final PhotoViewImageLongPressStartCallback? onLongPressStart;
|
||||
final Size outerSize;
|
||||
@@ -348,6 +354,7 @@ class CustomChildWrapper extends StatelessWidget {
|
||||
onDragStart: onDragStart,
|
||||
onDragEnd: onDragEnd,
|
||||
onDragUpdate: onDragUpdate,
|
||||
onDragCancel: onDragCancel,
|
||||
onScaleEnd: onScaleEnd,
|
||||
onLongPressStart: onLongPressStart,
|
||||
gestureDetectorBehavior: gestureDetectorBehavior,
|
||||
|
||||
Reference in New Issue
Block a user