From 7957e4894a1d6e20cec3bb649a78162bbfa1e92f Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sat, 15 Nov 2025 13:22:05 +0800 Subject: [PATCH] :sparkles: File list drag and drop --- lib/screens/files/file_list.dart | 2 - lib/widgets/file_list_view.dart | 83 +++++++++++++++---- linux/flutter/generated_plugin_registrant.cc | 4 + linux/flutter/generated_plugins.cmake | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 2 + macos/Podfile.lock | 6 ++ pubspec.lock | 8 ++ pubspec.yaml | 1 + .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 1 + 10 files changed, 94 insertions(+), 17 deletions(-) diff --git a/lib/screens/files/file_list.dart b/lib/screens/files/file_list.dart index 297137a7..cd90156e 100644 --- a/lib/screens/files/file_list.dart +++ b/lib/screens/files/file_list.dart @@ -99,9 +99,7 @@ class FileListScreen extends HookConsumerWidget { completer.future .then((uploadedFile) { if (uploadedFile != null) { - // Refresh the file list after successful upload ref.invalidate(cloudFileListNotifierProvider); - showSnackBar('File uploaded successfully'); } }) .catchError((error) { diff --git a/lib/widgets/file_list_view.dart b/lib/widgets/file_list_view.dart index c9571ec3..acbf53b3 100644 --- a/lib/widgets/file_list_view.dart +++ b/lib/widgets/file_list_view.dart @@ -1,3 +1,4 @@ +import 'package:desktop_drop/desktop_drop.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; @@ -5,8 +6,10 @@ import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:island/models/file_list_item.dart'; +import 'package:island/models/file.dart'; import 'package:island/pods/file_list.dart'; import 'package:island/pods/network.dart'; +import 'package:island/services/file_uploader.dart'; import 'package:island/utils/format.dart'; import 'package:island/widgets/alert.dart'; import 'package:island/widgets/content/cloud_files.dart'; @@ -36,6 +39,8 @@ class FileListView extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final dragging = useState(false); + useEffect(() { if (mode.value == FileListMode.normal) { final notifier = ref.read(cloudFileListNotifierProvider.notifier); @@ -291,22 +296,70 @@ class FileListView extends HookConsumerWidget { ), }; - return Column( - children: [ - const Gap(8), - _buildPathNavigation(ref, currentPath), - const Gap(8), - Expanded( - child: CustomScrollView( - slivers: [ - bodyWidget, - const SliverGap(12), - if (mode.value == FileListMode.normal && currentPath.value == '/') - SliverToBoxAdapter(child: _buildUnindexedFilesEntry(ref)), - ], - ), + return DropTarget( + onDragDone: (details) async { + dragging.value = false; + // Handle file upload + for (final file in details.files) { + final universalFile = UniversalFile( + data: file, + type: UniversalFileType.file, + displayName: file.name, + ); + + final completer = FileUploader.createCloudFile( + fileData: universalFile, + ref: ref, + path: currentPath.value, + onProgress: (progress, _) { + // Progress is handled by the upload tasks system + if (progress != null) { + debugPrint('Upload progress: ${(progress * 100).toInt()}%'); + } + }, + ); + + completer.future + .then((uploadedFile) { + if (uploadedFile != null) { + ref.invalidate(cloudFileListNotifierProvider); + } + }) + .catchError((error) { + showSnackBar('Failed to upload file: $error'); + }); + } + }, + onDragEntered: (details) { + dragging.value = true; + }, + onDragExited: (details) { + dragging.value = false; + }, + child: Container( + color: + dragging.value + ? Theme.of(context).primaryColor.withOpacity(0.1) + : null, + child: Column( + children: [ + const Gap(8), + _buildPathNavigation(ref, currentPath), + const Gap(8), + Expanded( + child: CustomScrollView( + slivers: [ + bodyWidget, + const SliverGap(12), + if (mode.value == FileListMode.normal && + currentPath.value == '/') + SliverToBoxAdapter(child: _buildUnindexedFilesEntry(ref)), + ], + ), + ), + ], ), - ], + ), ); } diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 92b32344..b3468650 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -6,6 +6,7 @@ #include "generated_plugin_registrant.h" +#include #include #include #include @@ -29,6 +30,9 @@ #include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) desktop_drop_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopDropPlugin"); + desktop_drop_plugin_register_with_registrar(desktop_drop_registrar); g_autoptr(FlPluginRegistrar) file_saver_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "FileSaverPlugin"); file_saver_plugin_register_with_registrar(file_saver_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index ee7cd27f..0331b61c 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + desktop_drop file_saver file_selector_linux flutter_platform_alert diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 83553073..a84217ed 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,6 +6,7 @@ import FlutterMacOS import Foundation import connectivity_plus +import desktop_drop import device_info_plus import file_picker import file_saver @@ -48,6 +49,7 @@ import window_manager func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin")) + DesktopDropPlugin.register(with: registry.registrar(forPlugin: "DesktopDropPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin")) FileSaverPlugin.register(with: registry.registrar(forPlugin: "FileSaverPlugin")) diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 860b6397..fd19b1f4 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -3,6 +3,8 @@ PODS: - FlutterMacOS - croppy (0.0.1): - FlutterMacOS + - desktop_drop (0.0.1): + - FlutterMacOS - device_info_plus (0.0.1): - FlutterMacOS - file_picker (0.0.1): @@ -258,6 +260,7 @@ PODS: DEPENDENCIES: - connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos`) - croppy (from `Flutter/ephemeral/.symlinks/plugins/croppy/macos`) + - desktop_drop (from `Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos`) - device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`) - file_picker (from `Flutter/ephemeral/.symlinks/plugins/file_picker/macos`) - file_saver (from `Flutter/ephemeral/.symlinks/plugins/file_saver/macos`) @@ -327,6 +330,8 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos croppy: :path: Flutter/ephemeral/.symlinks/plugins/croppy/macos + desktop_drop: + :path: Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos device_info_plus: :path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos file_picker: @@ -411,6 +416,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: connectivity_plus: 4adf20a405e25b42b9c9f87feff8f4b6fde18a4e croppy: d9bfc8c02f3cd1851f669a421df298a474b78f43 + desktop_drop: 10a3e6a7fa9dbe350541f2574092fecfa345a07b device_info_plus: 4fb280989f669696856f8b129e4a5e3cd6c48f76 file_picker: 7584aae6fa07a041af2b36a2655122d42f578c1a file_saver: e35bd97de451dde55ff8c38862ed7ad0f3418d0f diff --git a/pubspec.lock b/pubspec.lock index 2890ab8d..dbc43525 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -393,6 +393,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.11" + desktop_drop: + dependency: "direct main" + description: + name: desktop_drop + sha256: e70b46b2d61f1af7a81a40d1f79b43c28a879e30a4ef31e87e9c27bea4d784e8 + url: "https://pub.dev" + source: hosted + version: "0.7.0" device_info_plus: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 9922b6cc..2fc861aa 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -166,6 +166,7 @@ dependencies: flutter_expandable_fab: ^2.5.2 event_bus: ^2.0.1 convert: ^3.1.2 + desktop_drop: ^0.7.0 dev_dependencies: flutter_test: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 25bc2bcd..d2b4a843 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -42,6 +43,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); DartIpcPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("DartIpcPluginCApi")); + DesktopDropPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("DesktopDropPlugin")); FileSaverPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("FileSaverPlugin")); FileSelectorWindowsRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 4df5b4cd..63233402 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -5,6 +5,7 @@ list(APPEND FLUTTER_PLUGIN_LIST connectivity_plus dart_ipc + desktop_drop file_saver file_selector_windows firebase_core