我的上一篇文章 教你实战Flutter Deskstop之Tinypng(熊猫图片压缩)GUI工具 基于Flutter Deskstop 实现初版的图片压缩功能,可以支持macOS、以及windows。但是美中不足的是,macOS下依然要点击选择文件去压缩,而不是像Finder一样随意拖动文件。在文末我也是立了Flag要支持,经过一周时间的调研,顺利实现并且开源了此插件 file_drag_and_drop 。目前仅支持macOS,由于此功能非常依赖原生桌面,我对Windows Visual Studio编程是在是不熟,Flutter接口已经写好,期待有缘人可以贡献。话不多说,基于此插件,我也对我的图片压缩工具macOS版本做了版本更新,效果如下。

插件实现的代码过程解析

第一步等待初始化window

由于macOS桌面不像iOS原生可以使用PlatforView. 实际拖动接受文件和iOS差不多,要实现NSView的一个drag协议。 这里用了个取巧的方法,先在flutter端main函数 await一个 initializedMainView初始化方法。我们直接盖一个drop view到 NSWindow上即可。由于用户可能放大缩小窗口,布局就不用frame了,直接用原生约束,也不要SnapKit了,还要导入库,很简单的约束而已。

Flutter代码

void main() async { WidgetsFlutterBinding.ensureInitialized(); await dragAndDropChannel.initializedMainView(); runApp(GetMaterialApp( navigatorKey: Get.key, home: OKToast( child: MyApp(),

macOS 原生 Swift代码

private var mainWindow: NSWindow {
        get {
            return (self.registrar.view?.window)!;
private var mainView: NSView {
        get {
            return self.registrar.view!
private func _initializedMainView() {
        if (!_initialized) {
            _initialized = true
            mainView.addSubview(mainDropView)
            mainDropView.frame = mainView.bounds
            mainDropView.translatesAutoresizingMaskIntoConstraints = false
            mainView.addConstraints(
                    NSLayoutConstraint(item: mainDropView, attribute: .leading, relatedBy: .equal, toItem: mainView, attribute: .leading, multiplier: 1, constant: 0),
                    NSLayoutConstraint(item: mainDropView, attribute: .trailing, relatedBy: .equal, toItem: mainView, attribute: .trailing, multiplier: 1, constant: 0),
                    NSLayoutConstraint(item: mainDropView, attribute: .top, relatedBy: .equal, toItem: mainView, attribute: .top, multiplier: 1, constant: 0),
                    NSLayoutConstraint(item: mainDropView, attribute: .bottom, relatedBy: .equal, toItem: mainView, attribute: .bottom, multiplier: 1, constant: 0)
第二步实现协议
Swift protocol FlutterDragContainerDelegate { func draggingFileEntered() func draggingFileExit() func prepareForDragFileOperation() func performDragFileOperation(_ results : [FileResult]) Flutter 添加监听 abstract class DragContainerListener { void draggingFileEntered() {} void draggingFileExit() {} void prepareForDragFileOperation() {} void performDragFileOperation(List<DragFileResult> fileResults) {}

原生几个重要协议方法,通过Channel 转为Flutter的监听

Swift

override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation { if let delegate = self.delegate { delegate.draggingFileEntered(); return NSDragOperation.generic override func draggingExited(_ sender: NSDraggingInfo?) { if let delegate = self.delegate { delegate.draggingFileExit(); override func prepareForDragOperation(_ sender: NSDraggingInfo) -> Bool { if self.delegate != nil { self.delegate?.prepareForDragFileOperation() return true override func performDragOperation(_ sender: NSDraggingInfo) -> Bool { var files = Array<FileResult>() if let board = sender.draggingPasteboard.propertyList(forType: NSFilenamesPboardType) as? NSArray { for path in board { print(path) if let p = path as? String { let isDirectory = FlutterFileUtil.isDirectory(p) let fileExtension = FlutterFileUtil.fileExtension(p) files.append((path: p,isDirectory: isDirectory, fileExtension: fileExtension)) if self.delegate != nil { self.delegate?.performDragFileOperation(files) return true

Flutter端

ObserverList<DragContainerListener>? _listeners = ObserverList<DragContainerListener>(); Future<void> _methodCallHandler(MethodCall call) async { if (_listeners == null) return; for (final DragContainerListener listener in listeners) { if (!_listeners!.contains(listener)) { return; if (call.method != 'onEvent') throw UnimplementedError(); String eventName = call.arguments['eventName']; Map<String, Function> funcMap = { kFileDragAndDropEventEntered: listener.draggingFileEntered, kFileDragAndDropEventExit: listener.draggingFileExit, kFileDragAndDropEventPrepareDragTask: listener.prepareForDragFileOperation, kFileDragAndDropEventPerformDragTask: listener.performDragFileOperation, if (eventName == kFileDragAndDropEventPerformDragTask) { List fileResult = call.arguments['fileResult']; var resultList = <DragFileResult>[]; fileResult.forEach((element) { var result = DragFileResult.fromJson(element); resultList.add(result); funcMap[eventName]!(resultList); } else { funcMap[eventName]!();
第三步Window Home Page添加监听及处理
@override
  void initState() {
    super.initState();
    dragAndDropChannel.addListener(this);
  @override
  void dispose() {
    dragAndDropChannel.removeListener(this);
    super.dispose();

flutter监听的处理(相当于触发了原生的协议),这里简单做了个遮罩,拖进去显示,退出隐藏。

@override void draggingFileEntered() { print("flutter: draggingFileEntered"); setState(() { visibilityTips = true; @override void draggingFileExit() { print("flutter: draggingFileExit"); setState(() { visibilityTips = false; @override void prepareForDragFileOperation() { print("flutter: prepareForDragFileOperation"); setState(() { visibilityTips = false; @override void performDragFileOperation(List<DragFileResult> fileResults) { print("flutter: performDragFileOperation"); checkCanPicker().then((canPicker) { if (canPicker) { var collectionFiles = <File>[]; fileResults.forEach((element) { if (element.isDirectory == false) { collectionFiles.add(File(element.path)); //TODO Also can collect the image file in Directory var chooseFiles = chooseImageFiles(collectionFiles); if (chooseFiles.isNotEmpty) { controller.refreshWithFileList(chooseFiles);

此次插件仅实现了macOS从外部拖文件到应用内部,如何从应用内部拖文件去其他地方?由于deskstop版不支持Platform View。这感觉像是变成了一个死循环,还有待研究。另外写作不易,每次写作都耗费了不少时间,如果此文对你有帮助,希望点赞三连,Github也是Star顶起来,感谢🙏。

前言我的上一篇文章教你实战Flutter Deskstop之Tinypng(熊猫图片压缩)GUI工具基于Flutter Deskstop 实现初版的图片压缩功能,可以支持macOS、以及windows。但是美中不足的是,macOS下依然要点击选择文件去压缩,而不是像Finder一样随意拖动文件。在文末我也是立了Flag要支持,经过一周时间的调研,顺利实现并且开源了此插件file_drag_and_drop。目前仅支持macOS,由于此功能非常依赖原生桌面,我对Windows Visual Studio编程
Draggable 最近做了一个Flutter项目,其中有一个需求是做出三个可以互相拖拽的任务列表,平时在做Android项目时,拖动的控件基本上都是自定义View来实现的,想看看在Fluter上大家都是怎么实现的,没想到flutter提供了一个非常方便的拖拽控件Draggable。 Draggable的构造函数 我个人在Flutter开发时,遇到没有见过的控件时,点开源码观察它的构造函数一定是了解它的功能的最优解,我们看Draggable的构造函数: const Draggable({
flutter_list_drag_and_drop The Flutter team has also pushed this feature to the framework, https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/material/reorderable_list.dart A new Flutter package which handles all aspects of drag and drop inside a listView. In your pubspec.yaml dependencies: flutter_list_drag_and_drop: "^0.1.3" Features When dragging an item to the top/ bottom the list scroll accordingly Works with different sized items material like behaviour
flutter_document_picker 允许用户选择文档。 选取的文档将复制到应用程序临时目录。 (可选)仅允许选择具有特定扩展名的文档。 选取文件后,将使用allowedFileExtensions参数检查其扩展名。 然后将文件复制到应用程序临时目录。 作为结果返回复制文件路径。 如果选择的文件扩展名不在allowedFileExtensions文件扩展名列表中,则返回extension_mismatch错误。 在Android中,使用Intent.ACTION_OPEN_DOCUMENT 。 仅Android 19(KitKat)SDK版本支持此意图。 因此,仅当应用程序minSdkVersion为19或更高时,才能使用此插件。 在iOS中,使用UIDocumentPickerViewController 。 可以使用allowedUtiTypes参数按UTI类型列表
易于扩展的自定义布局 已知的问题 当前(从flutter v。1.24.0-1.0.pre起)仅在Web上存在问题,其中拖动带有后代的项目(包括带有onTap方法的InkWell小部件的项目)将引发异常。 这包括定义一个带有onTap方法的ListTile。 这似乎可以通过使用GestureDetector及其onTap方法而不是InkWell来解决。 请参阅以下问题: 要使用此插件,请 中将drag_and_drop_lists添加为 依赖项。 例如: dependencies: drag_and_drop_lists: ^0.2.1 class DraggableDemo extends StatefulWidget{ @override _DraggableDemoState createState() { // TODO: implement...
拖动组件 (1)若要组件位置变成拖动后位置,需要配合Positioned组件和Stack组件实现 (2)若只是可以拖动,但最终组件位置不改变,不使用Positioned组件 Draggable( data:, 拖拽传递的数据,任意类型,对应接收拖动组件的容器的onAccept回调的第一个参数 feedback:拖动过程中的组件, child: ...