feat: Paste image from clipboard on desktop/web

This commit is contained in:
Alexey 2026-03-20 14:38:41 +03:00
commit aea906f801
11 changed files with 56 additions and 1 deletions

View file

@ -1618,6 +1618,11 @@
"type": "String", "type": "String",
"placeholders": {} "placeholders": {}
}, },
"pasteImage": "Image from clipboard",
"@pasteImage": {
"type": "String",
"placeholders": {}
},
"sendImages": "Send {count} image", "sendImages": "Send {count} image",
"@sendImages": { "@sendImages": {
"type": "String", "type": "String",

View file

@ -1452,6 +1452,11 @@
"type": "String", "type": "String",
"placeholders": {} "placeholders": {}
}, },
"pasteImage": "Изображение из буфера обмена",
"@pasteImage": {
"type": "String",
"placeholders": {}
},
"sendMessages": "Отправить сообщения", "sendMessages": "Отправить сообщения",
"@sendMessages": { "@sendMessages": {
"type": "String", "type": "String",

View file

@ -14,6 +14,7 @@ import 'package:go_router/go_router.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:scroll_to_index/scroll_to_index.dart'; import 'package:scroll_to_index/scroll_to_index.dart';
import 'package:pasteboard/pasteboard.dart';
import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/config/setting_keys.dart';
import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/config/themes.dart';
@ -643,6 +644,11 @@ class ChatController extends State<ChatPageWithRoom>
); );
} }
Future<void> sendImageFromClipBoardAction() async {
final image = await Pasteboard.image;
await sendImageFromClipBoard(image);
}
Future<void> sendImageFromClipBoard(Uint8List? image) async { Future<void> sendImageFromClipBoard(Uint8List? image) async {
if (image == null) return; if (image == null) return;
await showAdaptiveDialog( await showAdaptiveDialog(
@ -1219,6 +1225,9 @@ class ChatController extends State<ChatPageWithRoom>
case AddPopupMenuActions.location: case AddPopupMenuActions.location:
sendLocationAction(); sendLocationAction();
return; return;
case AddPopupMenuActions.pasteImage:
sendImageFromClipBoardAction();
return;
} }
} }
@ -1421,4 +1430,5 @@ enum AddPopupMenuActions {
photoCamera, photoCamera,
videoCamera, videoCamera,
location, location,
pasteImage,
} }

View file

@ -172,6 +172,21 @@ class ChatInputRow extends StatelessWidget {
contentPadding: const EdgeInsets.all(0), contentPadding: const EdgeInsets.all(0),
), ),
), ),
if (!PlatformInfos.isMobile)
PopupMenuItem(
value: AddPopupMenuActions.pasteImage,
child: ListTile(
leading: CircleAvatar(
backgroundColor:
theme.colorScheme.onPrimaryContainer,
foregroundColor:
theme.colorScheme.primaryContainer,
child: const Icon(Icons.content_paste),
),
title: Text(L10n.of(context).pasteImage),
contentPadding: const EdgeInsets.all(0),
),
),
PopupMenuItem( PopupMenuItem(
value: AddPopupMenuActions.image, value: AddPopupMenuActions.image,
child: ListTile( child: ListTile(

View file

@ -16,6 +16,7 @@
#include <handy_window/handy_window_plugin.h> #include <handy_window/handy_window_plugin.h>
#include <media_kit_libs_linux/media_kit_libs_linux_plugin.h> #include <media_kit_libs_linux/media_kit_libs_linux_plugin.h>
#include <media_kit_video/media_kit_video_plugin.h> #include <media_kit_video/media_kit_video_plugin.h>
#include <pasteboard/pasteboard_plugin.h>
#include <record_linux/record_linux_plugin.h> #include <record_linux/record_linux_plugin.h>
#include <screen_retriever_linux/screen_retriever_linux_plugin.h> #include <screen_retriever_linux/screen_retriever_linux_plugin.h>
#include <sqlcipher_flutter_libs/sqlite3_flutter_libs_plugin.h> #include <sqlcipher_flutter_libs/sqlite3_flutter_libs_plugin.h>
@ -55,6 +56,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) media_kit_video_registrar = g_autoptr(FlPluginRegistrar) media_kit_video_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitVideoPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitVideoPlugin");
media_kit_video_plugin_register_with_registrar(media_kit_video_registrar); media_kit_video_plugin_register_with_registrar(media_kit_video_registrar);
g_autoptr(FlPluginRegistrar) pasteboard_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "PasteboardPlugin");
pasteboard_plugin_register_with_registrar(pasteboard_registrar);
g_autoptr(FlPluginRegistrar) record_linux_registrar = g_autoptr(FlPluginRegistrar) record_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "RecordLinuxPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "RecordLinuxPlugin");
record_linux_plugin_register_with_registrar(record_linux_registrar); record_linux_plugin_register_with_registrar(record_linux_registrar);

View file

@ -13,6 +13,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
handy_window handy_window
media_kit_libs_linux media_kit_libs_linux
media_kit_video media_kit_video
pasteboard
record_linux record_linux
screen_retriever_linux screen_retriever_linux
sqlcipher_flutter_libs sqlcipher_flutter_libs

View file

@ -22,6 +22,7 @@ import geolocator_apple
import just_audio import just_audio
import media_kit_video import media_kit_video
import package_info_plus import package_info_plus
import pasteboard
import record_macos import record_macos
import screen_retriever_macos import screen_retriever_macos
import share_plus import share_plus
@ -53,6 +54,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
JustAudioPlugin.register(with: registry.registrar(forPlugin: "JustAudioPlugin")) JustAudioPlugin.register(with: registry.registrar(forPlugin: "JustAudioPlugin"))
MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin")) MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin"))
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin"))
RecordMacOsPlugin.register(with: registry.registrar(forPlugin: "RecordMacOsPlugin")) RecordMacOsPlugin.register(with: registry.registrar(forPlugin: "RecordMacOsPlugin"))
ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverMacosPlugin")) ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverMacosPlugin"))
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))

View file

@ -1229,6 +1229,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.9.3" version: "1.9.3"
pasteboard:
dependency: "direct main"
description:
name: pasteboard
sha256: fedbe8da188d2f713aa8b01260737342e6e1087534a3ab26e1a719f8d3e8f32f
url: "https://pub.dev"
source: hosted
version: "0.5.0"
path: path:
dependency: "direct main" dependency: "direct main"
description: description:

View file

@ -60,6 +60,7 @@ dependencies:
opus_caf_converter_dart: ^1.0.1 opus_caf_converter_dart: ^1.0.1
package_info_plus: ^9.0.0 package_info_plus: ^9.0.0
particles_network: ^1.9.3 particles_network: ^1.9.3
pasteboard: ^0.5.0
path: ^1.9.0 path: ^1.9.0
path_provider: ^2.1.2 path_provider: ^2.1.2
pretty_qr_code: ^3.6.0 pretty_qr_code: ^3.6.0

View file

@ -16,6 +16,7 @@
#include <geolocator_windows/geolocator_windows.h> #include <geolocator_windows/geolocator_windows.h>
#include <media_kit_libs_windows_video/media_kit_libs_windows_video_plugin_c_api.h> #include <media_kit_libs_windows_video/media_kit_libs_windows_video_plugin_c_api.h>
#include <media_kit_video/media_kit_video_plugin_c_api.h> #include <media_kit_video/media_kit_video_plugin_c_api.h>
#include <pasteboard/pasteboard_plugin.h>
#include <record_windows/record_windows_plugin_c_api.h> #include <record_windows/record_windows_plugin_c_api.h>
#include <screen_retriever_windows/screen_retriever_windows_plugin_c_api.h> #include <screen_retriever_windows/screen_retriever_windows_plugin_c_api.h>
#include <share_plus/share_plus_windows_plugin_c_api.h> #include <share_plus/share_plus_windows_plugin_c_api.h>
@ -46,6 +47,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("MediaKitLibsWindowsVideoPluginCApi")); registry->GetRegistrarForPlugin("MediaKitLibsWindowsVideoPluginCApi"));
MediaKitVideoPluginCApiRegisterWithRegistrar( MediaKitVideoPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("MediaKitVideoPluginCApi")); registry->GetRegistrarForPlugin("MediaKitVideoPluginCApi"));
PasteboardPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PasteboardPlugin"));
RecordWindowsPluginCApiRegisterWithRegistrar( RecordWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("RecordWindowsPluginCApi")); registry->GetRegistrarForPlugin("RecordWindowsPluginCApi"));
ScreenRetrieverWindowsPluginCApiRegisterWithRegistrar( ScreenRetrieverWindowsPluginCApiRegisterWithRegistrar(

View file

@ -13,6 +13,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
geolocator_windows geolocator_windows
media_kit_libs_windows_video media_kit_libs_windows_video
media_kit_video media_kit_video
pasteboard
record_windows record_windows
screen_retriever_windows screen_retriever_windows
share_plus share_plus