Skip to content

Commit

Permalink
Expose listeners (#3)
Browse files Browse the repository at this point in the history
* export events + listeners via singleton

Co-authored-by: gian <gf@skalio.com>
  • Loading branch information
ltOgt and gian committed Jun 30, 2022
1 parent 2cf78fc commit d6d556a
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 64 deletions.
44 changes: 31 additions & 13 deletions example/lib/main.dart
Expand Up @@ -28,14 +28,40 @@ class _MyAppState extends State<MyApp> {
_imageData = imgData;
});
});

/// This is how any part of your app could listen to drag events
/// E.g. to update other UI once something is dragged
FlutterNativeDragNDrop.instance.addDragEventListener(onDragEvent);
}

bool isDragging = false;
void onDragEvent(DragEvent e) {
if (e is DragBeginEvent) {
setState(() {
isDragging = true;
});
return;
}
if (e is DragEndedEvent) {
setState(() {
isDragging = false;
});
return;
}
}

@override
void dispose() {
FlutterNativeDragNDrop.instance.removeDragEventListener(onDragEvent);
super.dispose();
}

@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Native drag & drop example'),
title: isDragging ? const Text('Its dragging time') : const Text('Native drag & drop example'),
),
body: Center(
child: Row(
Expand Down Expand Up @@ -63,8 +89,7 @@ class _MyAppState extends State<MyApp> {
});
},
onDragDone: (details) {
AssetImage droppedImage =
details.items.first.data! as AssetImage;
AssetImage droppedImage = details.items.first.data! as AssetImage;
setState(() {
_dragging = false;
_img = droppedImage;
Expand All @@ -76,16 +101,12 @@ class _MyAppState extends State<MyApp> {
),
const SizedBox(width: 15),
NativeDraggable(
child: const Image(
height: 200,
width: 200,
image: AssetImage("assets/maldives.jpg")),
child: const Image(height: 200, width: 200, image: AssetImage("assets/maldives.jpg")),
fileStreamCallback: passFileContent,
fileItems: [
NativeDragFileItem(
fileName: "maldives.jpeg",
fileSize:
_imageData != null ? _imageData!.lengthInBytes : 0,
fileSize: _imageData != null ? _imageData!.lengthInBytes : 0,
data: const AssetImage("assets/maldives.jpg"))
],
)
Expand All @@ -94,10 +115,7 @@ class _MyAppState extends State<MyApp> {
}

Stream<Uint8List> passFileContent(
NativeDragItem<Object> item,
String fileName,
String url,
ProgressController progressController) async* {
NativeDragItem<Object> item, String fileName, String url, ProgressController progressController) async* {
final buffer = _imageData!.buffer.asUint8List();
final range = buffer.length ~/ 10;

Expand Down
2 changes: 2 additions & 0 deletions lib/flutter_native_drag_n_drop.dart
Expand Up @@ -2,3 +2,5 @@ export 'src/native_draggable.dart';
export 'src/native_drop_target.dart';
export 'src/native_drag_item.dart';
export 'src/progress_controller.dart';
export 'src/channel.dart';
export 'src/events.dart';
139 changes: 88 additions & 51 deletions lib/src/channel.dart
Expand Up @@ -6,20 +6,29 @@ import 'native_drag_item.dart';
import 'native_draggable.dart' as d;

typedef RawDropListener = void Function(DropEvent);
typedef DragEventListener = void Function(DragEvent event);
typedef UniqueKeyString = String;

class FlutterNativeDragNDrop {
static const MethodChannel _channel =
MethodChannel('flutter_native_drag_n_drop');
static const MethodChannel _channel = MethodChannel('flutter_native_drag_n_drop');
static final instance = FlutterNativeDragNDrop._();

FlutterNativeDragNDrop._();

/// Used by drop targets
final _dropListeners = <RawDropListener>{};
final _draggableListeners = <d.DraggableState>{};

/// Used by draggables
final _draggableListeners = <UniqueKeyString, d.DraggableState>{};

/// Used by other widgets that want to be informed about drag events
final _dragEventListeners = <DragEventListener>{};

late List<NativeDragItem> _draggedItems;
Offset? _offset;

var _initialized = false;
@protected
init() {
if (_initialized) {
return;
Expand All @@ -31,8 +40,9 @@ class FlutterNativeDragNDrop {
}

/// This method creates the draggable view and dropTargetView on native side
setDraggableView<T extends Object>(String id, Offset position, Size size,
Uint8List? image, List<NativeDragItem> items) async {
@protected
setDraggableView<T extends Object>(
String id, Offset position, Size size, Uint8List? image, List<NativeDragItem> items) async {
_draggedItems = items;

if (items.first is NativeDragFileItem) {
Expand All @@ -46,7 +56,7 @@ class FlutterNativeDragNDrop {
"image": image,
"names": fileItems.map((e) => e.name).toList(),
"fileNames": fileItems.map((e) => e.fileName).toList(),
"fileSizes": fileItems.map((e) => e.fileSize).toList()
"fileSizes": fileItems.map((e) => e.fileSize).toList(),
});
} else {
await _channel.invokeMethod("setDraggableView", <String, dynamic>{
Expand All @@ -56,74 +66,76 @@ class FlutterNativeDragNDrop {
"width": size.width,
"height": size.height,
"image": image,
"names": _draggedItems.map((e) => e.name).toList()
"names": _draggedItems.map((e) => e.name).toList(),
});
}
}

/// This method removes the draggable view and dropTargetView on native side
@protected
removeDraggableView(String id) async {
await _channel
.invokeMethod("removeDraggableView", <String, dynamic>{"id": id});
await _channel.invokeMethod(
"removeDraggableView",
<String, dynamic>{"id": id},
);
}

@protected
updateProgress(String id, String fileName, int count) {
_channel.invokeMethod("updateProgress",
<String, dynamic>{"id": id, "fileName": fileName, "count": count});
_channel.invokeMethod(
"updateProgress",
<String, dynamic>{
"id": id,
"fileName": fileName,
"count": count,
},
);
}

Future<dynamic> _handleMethodChannel(MethodCall call) async {
switch (call.method) {
case "draggingEntered":
final position = (call.arguments as List).cast<double>();
_offset = Offset(position[0], position[1]);
_notifyDropEvent(
DropEnterEvent(location: _offset!, items: _draggedItems));
_notifyDropEvent(DropEnterEvent(location: _offset!, items: _draggedItems));
break;
case "draggingUpdated":
final position = (call.arguments as List).cast<double>();
_offset = Offset(position[0], position[1]);
_notifyDropEvent(
DropUpdateEvent(location: _offset!, items: _draggedItems));
_notifyDropEvent(DropUpdateEvent(location: _offset!, items: _draggedItems));
break;
case "draggingExited":
_notifyDropEvent(DropExitEvent(
location: _offset ?? Offset.zero, items: _draggedItems));
_notifyDropEvent(DropExitEvent(location: _offset ?? Offset.zero, items: _draggedItems));
_offset = null;
break;
case "performDragOperation":
_notifyDropEvent(DropDoneEvent(
location: _offset ?? Offset.zero, items: _draggedItems));
_notifyDropEvent(DropDoneEvent(location: _offset ?? Offset.zero, items: _draggedItems));
_offset = null;
break;
case "draggingBegin":
final arguments = Map.from(call.arguments);
final id = arguments["id"] as String;
final position = (arguments["position"] as List).cast<double>();
_notifyDragEvent(
id, DragBeginEvent(location: Offset(position[0], position[1])));
_notifyDragEvent(id, DragBeginEvent(location: Offset(position[0], position[1])));
break;
case "draggingMoved":
final arguments = Map.from(call.arguments);
final id = arguments["id"] as String;
final position = (arguments["position"] as List).cast<double>();
_notifyDragEvent(
id, DragMovedEvent(location: Offset(position[0], position[1])));
_notifyDragEvent(id, DragMovedEvent(location: Offset(position[0], position[1])));
break;
case "draggingEnded":
final arguments = Map.from(call.arguments);
final id = arguments["id"] as String;
final position = (arguments["position"] as List).cast<double>();
_notifyDragEvent(
id, DragEndedEvent(location: Offset(position[0], position[1])));
_notifyDragEvent(id, DragEndedEvent(location: Offset(position[0], position[1])));
break;
case "fileStreamCallback":
final arguments = Map.from(call.arguments);
final id = arguments["id"] as String;
final fileName = arguments["fileName"] as String;
final url = arguments["url"] as String;
final item = _draggedItems.firstWhere((i) => i.name == fileName)
as NativeDragFileItem;
final item = _draggedItems.firstWhere((i) => i.name == fileName) as NativeDragFileItem;
_notifyFileStreamEvent(id, FileStreamEvent(item, fileName, url));
break;
default:
Expand All @@ -137,57 +149,82 @@ class FlutterNativeDragNDrop {
}
}

/// Used by native drop target
@protected
void addRawDropEventListener(RawDropListener listener) {
_dropListeners.add(listener);
}

/// Used by native drop target
@protected
void removeRawDropEventListener(RawDropListener listener) {
_dropListeners.remove(listener);
}

void _notifyDragEvent(String id, DragEvent event) {
for (final listener in _draggableListeners) {
if (id == listener.uniqueKey.toString()) {
listener.onDragEvent(event);
}
for (final listener in _dragEventListeners) {
listener(event);
}
final listener = _draggableListeners[id];
assert(listener != null, "Drag Event for non existent listener");
listener?.onDragEvent(event);
}

_notifyFileStreamEvent(String id, FileStreamEvent event) async {
for (final listener in _draggableListeners) {
if (id == listener.uniqueKey.toString()) {
_listenToFileStream(
id, event.fileName, listener.onFileStreamEvent(event));
}
}
final listener = _draggableListeners[id];
assert(listener != null, "File Stream Event for non existent listener");
if (listener == null) return;
_listenToFileStream(id, event.fileName, listener.onFileStreamEvent(event));
}

_listenToFileStream(String id, String fileName, Stream<Uint8List> stream) {
stream.listen(
(data) {
_channel.invokeMethod("feedFileStream", <String, dynamic>{
"id": id,
"fileName": fileName,
"data": data,
"status": "kWriting"
});
_channel.invokeMethod(
"feedFileStream",
<String, dynamic>{
"id": id,
"fileName": fileName,
"data": data,
"status": "kWriting",
},
);
},
onDone: () {
_channel.invokeMethod("feedFileStream", <String, dynamic>{
"id": id,
"fileName": fileName,
"data": null,
"status": "kEnded"
});
_channel.invokeMethod(
"feedFileStream",
<String, dynamic>{
"id": id,
"fileName": fileName,
"data": null,
"status": "kEnded",
},
);
},
);
}

/// Used by native draggable
@protected
void addDraggableListener(d.DraggableState listener) {
_draggableListeners.add(listener);
_draggableListeners[listener.uniqueKey.toString()] = listener;
}

/// Used by native draggable
@protected
void removeDraggableListener(d.DraggableState listener) {
_draggableListeners.remove(listener);
_draggableListeners.remove(listener.uniqueKey.toString());
}

/// Meant for listners outside of this package to listen for general drag events
///
/// Be sure to unsubscribe via [removeDragEventListener].
void addDragEventListener(DragEventListener listener) {
_dragEventListeners.add(listener);
}

/// Unsubsribe after subscription via [addDragEventListener].
void removeDragEventListener(DragEventListener listener) {
_dragEventListeners.remove(listener);
}
}

0 comments on commit d6d556a

Please sign in to comment.