nc-photos/app/lib/widget/draggable.dart

130 lines
3.9 KiB
Dart
Raw Normal View History

2021-07-08 10:57:20 +02:00
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
2021-07-23 22:05:57 +02:00
class Draggable<T extends Object> extends StatelessWidget {
2021-09-15 08:58:06 +02:00
const Draggable({
2021-07-23 22:05:57 +02:00
Key? key,
required this.data,
required this.child,
this.feedback,
2021-07-08 10:57:20 +02:00
this.onDropBefore,
this.onDropAfter,
this.onDragStarted,
this.onDragEndedAny,
this.feedbackSize,
}) : super(key: key);
@override
build(BuildContext context) {
2021-09-15 08:58:06 +02:00
buildIndicator(alignment, isActive) {
2021-07-08 10:57:20 +02:00
return Stack(
children: [
Container(),
Visibility(
visible: isActive,
child: Align(
alignment: alignment,
child: Container(
2021-09-15 08:58:06 +02:00
constraints: const BoxConstraints.tightFor(width: 2),
2021-07-08 10:57:20 +02:00
color: Theme.of(context).colorScheme.primary,
),
),
),
],
);
2021-09-15 08:58:06 +02:00
}
2021-07-08 10:57:20 +02:00
return Stack(
fit: StackFit.expand,
children: [
Padding(
padding: const EdgeInsets.all(8),
child: LongPressDraggable<T>(
data: data,
dragAnchorStrategy: pointerDragAnchorStrategy,
onDragStarted: onDragStarted,
onDragEnd: (_) => onDragEndedAny?.call(),
onDragCompleted: onDragEndedAny,
onDraggableCanceled: (v, o) => onDragEndedAny?.call(),
feedback: FractionalTranslation(
translation: const Offset(-.5, -.5),
child: SizedBox(
width: feedbackSize?.width ?? 128,
height: feedbackSize?.height ?? 128,
child: Opacity(
opacity: .5,
child: feedback ?? child,
2021-07-08 10:57:20 +02:00
),
),
),
childWhenDragging: Opacity(
opacity: .25,
child: child,
),
2022-07-08 16:52:18 +02:00
child: child,
2021-07-08 10:57:20 +02:00
),
),
if (onDropBefore != null || onDropAfter != null)
Positioned.fill(
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
if (onDropBefore != null)
Expanded(
child: DragTarget<T>(
builder: (context, candidateItems, rejectedItems) {
return buildIndicator(AlignmentDirectional.centerStart,
candidateItems.isNotEmpty);
},
onAccept: (item) {
_log.fine("[build] Dropping $item before $data");
2021-07-23 22:05:57 +02:00
onDropBefore!(item);
2021-07-08 10:57:20 +02:00
},
),
),
if (onDropAfter != null)
Expanded(
child: DragTarget<T>(
builder: (context, candidateItems, rejectedItems) {
return buildIndicator(AlignmentDirectional.centerEnd,
candidateItems.isNotEmpty);
},
onAccept: (item) {
_log.fine("[build] Dropping $item after $data");
2021-07-23 22:05:57 +02:00
onDropAfter!(item);
2021-07-08 10:57:20 +02:00
},
),
),
],
),
),
],
);
}
final T data;
final Widget child;
2021-07-23 22:05:57 +02:00
final Widget? feedback;
2021-07-08 10:57:20 +02:00
/// Called when some item dropped before this item
2021-07-23 22:05:57 +02:00
final DragTargetAccept<T>? onDropBefore;
2021-07-08 10:57:20 +02:00
/// Called when some item dropped after this item
2021-07-23 22:05:57 +02:00
final DragTargetAccept<T>? onDropAfter;
2021-07-08 10:57:20 +02:00
2021-07-23 22:05:57 +02:00
final VoidCallback? onDragStarted;
2021-07-08 10:57:20 +02:00
/// Called when either one of onDragEnd, onDragCompleted or
/// onDraggableCanceled is called.
///
/// The callback might be called multiple times per each drag event
2021-07-23 22:05:57 +02:00
final VoidCallback? onDragEndedAny;
2021-07-08 10:57:20 +02:00
/// Size of the feedback widget that appears under the pointer.
///
/// Right now a translucent version of [child] is being shown
2021-07-23 22:05:57 +02:00
final Size? feedbackSize;
2021-07-08 10:57:20 +02:00
static final _log = Logger("widget.draggable.Draggable");
}