Skip to content

Commit

Permalink
feat! drag item image API clean-up (#105)
Browse files Browse the repository at this point in the history
  • Loading branch information
knopp committed Mar 28, 2023
1 parent be185cb commit 8153ea2
Show file tree
Hide file tree
Showing 10 changed files with 564 additions and 511 deletions.
3 changes: 0 additions & 3 deletions super_drag_and_drop/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,6 @@ class MyDraggableWidget extends StatelessWidget {
dragItemProvider: (request) async {
// DragItem represents the content bein dragged.
final item = DragItem(
// snapshot() will return image snapshot of the DragItemWidget.
// You can use any other drag image if your wish.
image: request.dragImage(),
// This data is only accessible when dropping within same
// application. (optional)
localData: {'x': 3, 'y': 4},
Expand Down
10 changes: 3 additions & 7 deletions super_drag_and_drop/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -209,9 +209,9 @@ class _MyHomePageState extends State<MyHomePage> {
return null;
}
final item = DragItem(
image: request.dragImage(),
localData: 'text-item',
suggestedName: 'PlainText.txt');
localData: 'text-item',
suggestedName: 'PlainText.txt',
);
item.add(Formats.plainText('Plain Text Value'));
return item;
}
Expand All @@ -222,7 +222,6 @@ class _MyHomePageState extends State<MyHomePage> {
return null;
}
final item = DragItem(
image: request.dragImage(),
localData: 'image-item',
suggestedName: 'Green.png',
);
Expand All @@ -236,7 +235,6 @@ class _MyHomePageState extends State<MyHomePage> {
return null;
}
final item = DragItem(
image: await request.dragImage(),
localData: 'lazy-image-item',
suggestedName: 'LazyBlue.png',
);
Expand All @@ -253,7 +251,6 @@ class _MyHomePageState extends State<MyHomePage> {
return null;
}
final item = DragItem(
image: request.dragImage(),
localData: 'virtual-file-item',
suggestedName: 'VirtualFile.txt',
);
Expand Down Expand Up @@ -283,7 +280,6 @@ class _MyHomePageState extends State<MyHomePage> {
return null;
}
final item = DragItem(
image: request.dragImage(),
localData: 'multiple-representations-item',
);
item.add(Formats.png(await createImageData(Colors.pink)));
Expand Down
10 changes: 5 additions & 5 deletions super_drag_and_drop/lib/src/base_draggable_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import 'drag_configuration.dart';
typedef LocationIsDraggable = bool Function(Offset position);
typedef DragConfigurationProvider = Future<DragConfiguration?> Function(
Offset position, DragSession session);
typedef AdditionalItemsProvider = Future<List<DragItem>?> Function(
typedef AdditionalItemsProvider = Future<List<DragConfigurationItem>?> Function(
Offset position, DragSession session);

/// This is the most basic draggable widget. It gives you complete control
Expand Down Expand Up @@ -44,7 +44,7 @@ class BaseDraggableWidget extends StatelessWidget {
/// dragging item for current session.
final AdditionalItemsProvider additionalItems;

static Future<List<DragItem>?> _defaultAdditionalItems(
static Future<List<DragConfigurationItem>?> _defaultAdditionalItems(
Offset position, DragSession session) async {
return null;
}
Expand Down Expand Up @@ -77,19 +77,19 @@ class BaseDraggableWidget extends StatelessWidget {
child = Listener(
behavior: HitTestBehavior.translucent,
onPointerDown: (_) {
Snapshotter.of(context)?.prepareFor({
Snapshotter.of(context)?.prepare({
SnapshotType.drag,
if (defaultTargetPlatform == TargetPlatform.iOS) SnapshotType.lift,
});
},
onPointerCancel: (_) {
if (context.mounted) {
Snapshotter.of(context)?.prepareFor({});
Snapshotter.of(context)?.prepare({});
}
},
onPointerUp: (_) {
if (context.mounted) {
Snapshotter.of(context)?.prepareFor({});
Snapshotter.of(context)?.prepare({});
}
},
child: child,
Expand Down
45 changes: 26 additions & 19 deletions super_drag_and_drop/lib/src/drag_configuration.dart
Original file line number Diff line number Diff line change
@@ -1,29 +1,13 @@
import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:super_clipboard/super_clipboard.dart';
import 'package:super_drag_and_drop/super_drag_and_drop.dart';
import 'package:super_native_extensions/raw_drag_drop.dart' as raw;

class DragImage {
DragImage({
required this.image,
this.liftImage,
});

/// Image used while dragging
raw.TargetedImage image;

/// If specified this image will be used for lift animation on iOS.
raw.TargetedImage? liftImage;
}

/// Represent single item being dragged in a [DragSession].
class DragItem extends DataWriterItem {
DragItem({
super.suggestedName,
required this.image,
this.localData,
});

Expand All @@ -34,15 +18,38 @@ class DragItem extends DataWriterItem {
defaultTargetPlatform == TargetPlatform.windows ||
defaultTargetPlatform == TargetPlatform.iOS);

final FutureOr<DragImage> image;

/// Data associated with this drag item that can be only read when dropping
/// within same application. The data must be serializable with
/// [StandardMessageCodec]. It is possible to read [localData] from
/// one isolate in another isolate.
final Object? localData;
}

class DragImage {
DragImage({
required this.image,
this.liftImage,
});

/// Image used while dragging
raw.TargetedImage image;

/// If specified this image will be used for lift animation on iOS.
raw.TargetedImage? liftImage;
}

/// Single item of [DragConfiguration] consisting of drag item and coresponing
/// image.
class DragConfigurationItem {
DragConfigurationItem({
required this.item,
required this.image,
});

final DragItem item;
final DragImage image;
}

/// Addtional options for drag session.
class DragOptions {
const DragOptions({
Expand All @@ -66,7 +73,7 @@ class DragConfiguration {
});

/// List of items in this session.
final List<DragItem> items;
final List<DragConfigurationItem> items;

/// Allowed drop operation for this session.
final List<raw.DropOperation> allowedOperations;
Expand Down
64 changes: 27 additions & 37 deletions super_drag_and_drop/lib/src/draggable_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,18 @@ import 'base_draggable_widget.dart';

class DragItemRequest {
DragItemRequest({
required this.dragImage,
required this.location,
required this.session,
});

/// Provides snapshot image of the containing [DragItemWidget].
///
/// If you want to customize the drag image you can wrap the [DragItemWidget]
/// in a [CustomSnapshotWidget].
///
/// Note that using [dragImage] is optional, you can generate your own drag
/// image from scratch when constructing [DragItem].
final Future<DragImage> Function() dragImage;

/// Drag location in global coordinates.
final Offset location;

/// Current drag session.
final raw.DragSession session;
}

typedef DragItemProvider = Future<DragItem?> Function(DragItemRequest);
typedef DragItemProvider = FutureOr<DragItem?> Function(DragItemRequest);

/// Widget that provides [DragItem] for a [DraggableWidget].
///
Expand Down Expand Up @@ -96,17 +86,8 @@ class DragItemWidget extends StatefulWidget {
State<StatefulWidget> createState() => DragItemWidgetState();
}

class _SnapshotException implements Exception {
_SnapshotException(this.message);

final String message;

@override
String toString() => message;
}

class DragItemWidgetState extends State<DragItemWidget> {
Future<DragImage> _getSnapshot(Offset location) async {
Future<DragImage?> _getSnapshot(Offset location) async {
final snapshotter = Snapshotter.of(_innerContext!)!;
final dragSnapshot =
await snapshotter.getSnapshot(location, SnapshotType.drag);
Expand All @@ -132,25 +113,30 @@ class DragItemWidgetState extends State<DragItemWidget> {
}

if (snapshot == null) {
// This might happen if widget is removed before snapshot is ready.
// TODO(knopp): Handle this better.
throw _SnapshotException('Failed get drag snapshot.');
return null;
}

return DragImage(image: snapshot, liftImage: liftSnapshot);
}

Future<DragItem?> createItem(Offset location, raw.DragSession session) async {
Future<DragConfigurationItem?> createItem(
Offset location, raw.DragSession session) async {
final request = DragItemRequest(
dragImage: () => _getSnapshot(location),
location: location,
session: session,
);
try {
return widget.dragItemProvider(request);
} on _SnapshotException {
return null;

final item = await widget.dragItemProvider(request);
if (item != null) {
final image = await _getSnapshot(location);
if (image != null) {
return DragConfigurationItem(
item: item,
image: image,
);
}
}
return null;
}

Future<List<raw.DropOperation>> getAllowedOperations() async {
Expand All @@ -176,8 +162,8 @@ typedef DragItemsProvider = List<DragItemWidgetState> Function(
typedef OnDragConfiguration = FutureOr<DragConfiguration?> Function(
DragConfiguration configuration, raw.DragSession session);

typedef OnAdditonalItems = FutureOr<List<DragItem>?> Function(
List<DragItem> items, raw.DragSession session);
typedef OnAdditonalItems = FutureOr<List<DragConfigurationItem>?> Function(
List<DragConfigurationItem> items, raw.DragSession session);

/// Widget that represents user-draggable area.
Expand Down Expand Up @@ -240,7 +226,7 @@ class DraggableWidget extends StatelessWidget {
}

if (allowedOperations?.isNotEmpty == true) {
final dragItems = <DragItem>[];
final dragItems = <DragConfigurationItem>[];
for (final item in items) {
final dragItem = await item.createItem(location, session);
if (dragItem != null) {
Expand All @@ -249,7 +235,9 @@ class DraggableWidget extends StatelessWidget {
}
if (dragItems.isNotEmpty) {
final configuration = DragConfiguration(
items: dragItems, allowedOperations: allowedOperations!);
items: dragItems,
allowedOperations: allowedOperations!,
);
if (onDragConfiguration != null) {
return onDragConfiguration!(configuration, session);
} else {
Expand All @@ -260,9 +248,11 @@ class DraggableWidget extends StatelessWidget {
return null;
}

Future<List<DragItem>?> additionalItems(List<DragItemWidgetState> items,
Offset location, raw.DragSession session) async {
final dragItems = <DragItem>[];
Future<List<DragConfigurationItem>?> additionalItems(
List<DragItemWidgetState> items,
Offset location,
raw.DragSession session) async {
final dragItems = <DragConfigurationItem>[];
for (final item in items) {
if (item.widget.canAddItemToExistingSession) {
final dragItem = await item.createItem(location, session);
Expand Down
13 changes: 5 additions & 8 deletions super_drag_and_drop/lib/src/into_raw.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,24 @@ extension DragImageIntoRaw on DragImage {
}
}

extension DragItemsIntoRaw on List<DragItem> {
extension DragItemsIntoRaw on List<DragConfigurationItem> {
Future<List<raw.DragItem>> intoRaw(double devicePixelRatio) async {
final providers = <raw.DataProvider>[];
for (final item in this) {
providers.add(await item.asDataProvider());
providers.add(await item.item.asDataProvider());
}
final handles = <raw.DataProviderHandle>[];
for (final item in indexed()) {
final handle =
await item.value.registerWithDataProvider(providers[item.index]);
await item.value.item.registerWithDataProvider(providers[item.index]);
handles.add(handle);
}
final items = <raw.DragItem>[];
for (final item in indexed()) {
final image = item.value.image is Future
? await item.value.image
: item.value.image as DragImage;
items.add(raw.DragItem(
dataProvider: handles[item.index],
image: await image.intoRaw(devicePixelRatio),
localData: item.value.localData,
image: await item.value.image.intoRaw(devicePixelRatio),
localData: item.value.item.localData,
));
}
return items;
Expand Down
1 change: 1 addition & 0 deletions super_drag_and_drop/lib/super_drag_and_drop.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export 'package:super_clipboard/src/format.dart';
export 'package:super_clipboard/src/standard_formats.dart';
export 'package:super_native_extensions/widgets.dart'
show
RawCustomSnapshotWidget,
CustomSnapshotWidget,
SnapshotType,
SnapshotBuilder,
Expand Down
Loading

0 comments on commit 8153ea2

Please sign in to comment.