From 743c829e4867ebb5bf2a08cf9d4290ab30cfc7ac Mon Sep 17 00:00:00 2001 From: Ellet <73608287+ellet0@users.noreply.github.com> Date: Thu, 13 Jun 2024 13:33:16 +0300 Subject: [PATCH] Refactor(plugins)!: move super clipboard to extensions package (#1914) * fix(scripts): add execution permission for scripts/pub_get.sh * chore(plugins)!: move super_clipboard plugin to be part of flutter_quill_extensions * docs: add a comment for _pasteHtml() function in QuillController * docs(extensions-package): update usage section to describe HTML and Markdown clipboard paste --- README.md | 5 +- example/analysis_options.yaml | 2 +- example/lib/main.dart | 5 + .../lib/screens/home/widgets/home_screen.dart | 2 +- .../lib/screens/quill/my_quill_editor.dart | 5 +- example/lib/screens/quill/quill_screen.dart | 2 +- flutter_quill_extensions/README.md | 94 +++++++------ .../lib/flutter_quill_extensions.dart | 18 +++ .../clipboard/super_clipboard_service.dart | 127 ++++++++++++++++++ .../services/clipboard/clipboard_service.dart | 19 +++ .../clipboard/clipboard_service_provider.dart | 19 +++ .../clipboard/default_clipboard_service.dart | 60 +++++++++ lib/src/widgets/quill/quill_controller.dart | 44 +++--- lib/src/widgets/quill/text_line.dart | 1 - .../widgets/raw_editor/raw_editor_state.dart | 64 ++++----- .../toolbar/buttons/clipboard_button.dart | 13 +- pubspec.yaml | 1 - scripts/pub_get.sh | 0 18 files changed, 364 insertions(+), 117 deletions(-) create mode 100644 flutter_quill_extensions/lib/services/clipboard/super_clipboard_service.dart create mode 100644 lib/src/services/clipboard/clipboard_service.dart create mode 100644 lib/src/services/clipboard/clipboard_service_provider.dart create mode 100644 lib/src/services/clipboard/default_clipboard_service.dart mode change 100644 => 100755 scripts/pub_get.sh diff --git a/README.md b/README.md index e37e1dea..e391b51a 100644 --- a/README.md +++ b/README.md @@ -97,12 +97,11 @@ Before using the package, we must inform you the package use the following plugi url_launcher flutter_keyboard_visibility device_info_plus - super_clipboard ``` -All of them doesn't require any platform specific setup, except [super_clipboard](https://pub.dev/packages/super_clipboard) which needs some setup on Android only, it's used to support copying images and pasting them into editor then you must setup it, open the page in pub.dev and read the `README.md` to get the instructions. +All of them doesn't require any platform specific setup. -The minSdkVersion is `23` as `super_clipboard` requires it +> Starting from Flutter Quill `9.4.x`, [super_clipboard](https://pub.dev/packages/super_clipboard) has been moved to [FlutterQuill Extensions], to use rich text features, support pasting images, gif files, take a look at `flutter_quill_extensions` Readme. ## Usage diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml index 735e0400..f20ebe56 100644 --- a/example/analysis_options.yaml +++ b/example/analysis_options.yaml @@ -11,7 +11,7 @@ linter: annotate_overrides: true avoid_empty_else: true avoid_escaping_inner_quotes: true - avoid_print: false + avoid_print: true avoid_redundant_argument_values: false avoid_types_on_closure_parameters: true avoid_void_async: true diff --git a/example/lib/main.dart b/example/lib/main.dart index fbca48eb..2a8ed52c 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -8,6 +8,7 @@ import 'package:flutter_localizations/flutter_localizations.dart' GlobalWidgetsLocalizations; import 'package:flutter_quill/flutter_quill.dart' show Document; import 'package:flutter_quill/translations.dart' show FlutterQuillLocalizations; +import 'package:flutter_quill_extensions/flutter_quill_extensions.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart' show HydratedBloc, HydratedStorage; import 'package:path_provider/path_provider.dart' @@ -29,6 +30,7 @@ void main() async { ? HydratedStorage.webStorageDirectory : await getApplicationDocumentsDirectory(), ); + FlutterQuillExtensions.useSuperClipboardPlugin(); runApp(const MyApp()); } @@ -69,6 +71,9 @@ class MyApp extends StatelessWidget { GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, + // Uncomment this line to use provide flutter quill localizations + // in your widgets app, otherwise the quill widgets will provide it + // internally: // FlutterQuillLocalizations.delegate, ], supportedLocales: FlutterQuillLocalizations.supportedLocales, diff --git a/example/lib/screens/home/widgets/home_screen.dart b/example/lib/screens/home/widgets/home_screen.dart index 5d7a4369..6bfd6472 100644 --- a/example/lib/screens/home/widgets/home_screen.dart +++ b/example/lib/screens/home/widgets/home_screen.dart @@ -155,7 +155,7 @@ class HomeScreen extends StatelessWidget { ), ); } catch (e) { - print( + debugPrint( 'Error while loading json delta file: ${e.toString()}', ); scaffoldMessenger.showText( diff --git a/example/lib/screens/quill/my_quill_editor.dart b/example/lib/screens/quill/my_quill_editor.dart index a0617f74..be3c48ef 100644 --- a/example/lib/screens/quill/my_quill_editor.dart +++ b/example/lib/screens/quill/my_quill_editor.dart @@ -68,7 +68,8 @@ class MyQuillEditor extends StatelessWidget { return null; } // We will save it to system temporary files - final newFileName = '${DateTime.now().toIso8601String()}.png'; + final newFileName = + 'imageFile-${DateTime.now().toIso8601String()}.png'; final newPath = path.join( io.Directory.systemTemp.path, newFileName, @@ -83,7 +84,7 @@ class MyQuillEditor extends StatelessWidget { return null; } // We will save it to system temporary files - final newFileName = '${DateTime.now().toIso8601String()}.gif'; + final newFileName = 'gifFile-${DateTime.now().toIso8601String()}.gif'; final newPath = path.join( io.Directory.systemTemp.path, newFileName, diff --git a/example/lib/screens/quill/quill_screen.dart b/example/lib/screens/quill/quill_screen.dart index ec97b5d8..9cae766b 100644 --- a/example/lib/screens/quill/quill_screen.dart +++ b/example/lib/screens/quill/quill_screen.dart @@ -127,7 +127,7 @@ class _QuillScreenState extends State { IconButton( tooltip: 'Print to log', onPressed: () { - print( + debugPrint( jsonEncode(_controller.document.toDelta().toJson()), ); ScaffoldMessenger.of(context).showText( diff --git a/flutter_quill_extensions/README.md b/flutter_quill_extensions/README.md index 6bcbb4cc..3e52c777 100644 --- a/flutter_quill_extensions/README.md +++ b/flutter_quill_extensions/README.md @@ -13,7 +13,8 @@ to support embedding widgets like images, formulas, videos, and more. - [Installation](#installation) - [Platform Specific Configurations](#platform-specific-configurations) - [Usage](#usage) - - [Embed Blocks](#embed-blocks) + - [Configurations](#configurations) + - [Embed Blocks](#embed-blocks) - [Element properties](#element-properties) - [Custom Element properties](#custom-element-properties) - [Image Assets](#image-assets) @@ -51,52 +52,68 @@ dependencies: ## Platform Specific Configurations +The package use the following plugins: + +1. [`gal`](https://github.com/natsuk4ze/) plugin to save images. +For this to work, you need to add the appropriate configurations +See to add the needed lines. +1. [`image_picker`](https://pub.dev/packages/image_picker) plugin for picking images so please make sure to follow the instructions +2. [youtube_player_flutter](https://pub.dev/packages/youtube_player_flutter) plugin which uses [flutter_inappwebview](https://pub.dev/packages/flutter_inappwebview) which has requirement on web, please follow this [link](https://pub.dev/packages/flutter_inappwebview#installation) in order to setup the support for web +3. [image_picker](https://pub.dev/packages/image_picker) which also +requires some configurations, follow this [link](https://pub.dev/packages/image_picker#installation). It's needed for Android, iOS, and macOS, we must inform you that you can't pick photos using the camera on a desktop so make sure to handle that if you plan on adding support for the desktop, this may change in the future, and for more info follow this [link](https://pub.dev/packages/image_picker#windows-macos-and-linux) +4. [super_clipboard](https://pub.dev/packages/super_clipboard) which needs some setup on Android only, it's used to support copying images and pasting them into editor then you must setup it, open the page in pub.dev and read the `README.md` or click on this [link](https://pub.dev/packages/super_clipboard#android-support) to get the instructions. + +The minSdkVersion is `23` as `super_clipboard` requires it + + +> For loading the image from the internet

+> **Android**: you need to add permissions in `AndroidManifest.xml`, Follow this [Android Guide](https://developer.android.com/training/basics/network-ops/connecting) or [Flutter Networking](https://docs.flutter.dev/data-and-backend/networking#android) for more info, the internet permission is included by default only for debugging so you need to follow this link to add it in the release version too. you should allow loading images and videos only for the `https` protocol but if you want http too then you need to configure your Android application to accept `http` in the release mode, follow this [Android Cleartext / Plaintext HTTP](https://developer.android.com/privacy-and-security/risks/cleartext) page for more info.

+> **macOS**: you need to include a key in your `Info.plist`, follow this [link](https://docs.flutter.dev/data-and-backend/networking#macos) to add the required configurations > -> 1. We are using the [`gal`](https://github.com/natsuk4ze/) plugin to save images. -> For this to work, you need to add the appropriate configurations -> See to add the needed lines. -> -> 2. We also use [`image_picker`](https://pub.dev/packages/image_picker) plugin for picking images so please make sure to follow the instructions -> -> 3. We are using [youtube_player_flutter](https://pub.dev/packages/youtube_player_flutter) plugin which uses [flutter_inappwebview](https://pub.dev/packages/flutter_inappwebview) which has requirement on web, please follow this [link](https://pub.dev/packages/flutter_inappwebview#installation) in order to setup the support for web -> 4. For loading the image from the internet, we need the internet permission -> 1. For Android, you need to add some permissions in `AndroidManifest.xml`, Please follow this [link](https://developer.android.com/training/basics/network-ops/connecting) for more info, the internet permission is included by default only for debugging so you need to follow this link to add it in the release version too. you should allow loading images and videos only for the `https` protocol but if you want http too then you need to configure your Android application to accept `http` in the release mode, follow this [link](https://stackoverflow.com/questions/45940861/android-8-cleartext-http-traffic-not-permitted) for more info. -> 2. For macOS, you also need to include a key in your `Info.plist`, please follow this [link](https://stackoverflow.com/a/61201081/18519412) to add the required configurations -> -> The extension package also uses [image_picker](https://pub.dev/packages/image_picker) which also -> requires some configurations, follow this [link](https://pub.dev/packages/image_picker#installation). It's needed for Android, iOS, and macOS, we must inform you that you can't pick photos using the camera on a desktop so make sure to handle that if you plan on adding support for the desktop, this may change in the future, and for more info follow this [link](https://pub.dev/packages/image_picker#windows-macos-and-linux)
-> ## Usage -Before starting to use this package you must follow the [setup](#installation) +Start using the package in 3 steps: -Set the `embedBuilders` and `embedToolbar` params in configurations of `QuillEditor` and `QuillToolbar` with the -values provided by this repository. +1. Besure to to follow the [Installation](#installation) section. +2. This package already include `super_clipboard` and will be used internally in this package, to use it in `flutter_quill`, call this function before using any of the widgets or functionalities -**Quill Toolbar**: -```dart -QuillToolbar( - configurations: QuillToolbarConfigurations( - embedButtons: FlutterQuillEmbeds.toolbarButtons(), - ), -), -``` + ```dart + FlutterQuillExtensions.useSuperClipboardPlugin(); + ``` -**Quill Editor** -```dart -Expanded( - child: QuillEditor.basic( - configurations: QuillEditorConfigurations( - embedBuilders: kIsWeb ? FlutterQuillEmbeds.editorWebBuilders() : FlutterQuillEmbeds.editorBuilders(), + `super_clipboard` is comprehensive plugin that provide many clipboard features for reading and writing of rich text, images and other formats. + + Executing this function will allow `flutter_quill` to use modern rich text features to paste HTML and Markdown, support for Gif files, and other formats. + +3. Set the `embedBuilders` and `embedToolbar` params in configurations of `QuillEditor` and `QuillToolbar` with the + values provided by this repository. + + **Quill Toolbar**: + ```dart + QuillToolbar( + configurations: QuillToolbarConfigurations( + embedButtons: FlutterQuillEmbeds.toolbarButtons(), + ), ), - ), -) -``` + ``` + + **Quill Editor** + ```dart + Expanded( + child: QuillEditor.basic( + configurations: QuillEditorConfigurations( + embedBuilders: kIsWeb ? FlutterQuillEmbeds.editorWebBuilders() : FlutterQuillEmbeds.editorBuilders(), + ), + ), + ) + ``` + +## Configurations -## Embed Blocks +### Embed Blocks -As of version [flutter_quill](https://pub.dev/packages/flutter_quill) 6.0, embed blocks are not provided by default as part of Flutter quill. Instead, it provides an interface for all the users to provide their implementations for embed blocks. Implementations for image, video, and formula embed blocks are proved in this package +As of version [flutter_quill](https://pub.dev/packages/flutter_quill) `6.0.x`, embed blocks are not provided by default as part of Flutter quill. Instead, it provides an interface for all the users to provide their implementations for embed blocks. Implementations for image, video, and formula embed blocks are proved in this package The instructions for using the embed blocks are in the [Usage](#usage) section @@ -236,9 +253,8 @@ OnDragDoneCallback get _onDragDone { ## Features — Easy to use and customizable -- Has the option to use a custom image provider for the images +- Rich text, images and other formats - Useful utilities and widgets -- Handle different errors ``` ## Contributing diff --git a/flutter_quill_extensions/lib/flutter_quill_extensions.dart b/flutter_quill_extensions/lib/flutter_quill_extensions.dart index 772e5b15..a437ccfb 100644 --- a/flutter_quill_extensions/lib/flutter_quill_extensions.dart +++ b/flutter_quill_extensions/lib/flutter_quill_extensions.dart @@ -1,5 +1,11 @@ library flutter_quill_extensions; +// ignore: implementation_imports +import 'package:flutter_quill/src/services/clipboard/clipboard_service_provider.dart'; +import 'package:meta/meta.dart' show immutable; + +import 'services/clipboard/super_clipboard_service.dart'; + export 'embeds/embed_types.dart'; export 'embeds/formula/toolbar/formula_button.dart'; export 'embeds/image/editor/image_embed.dart'; @@ -26,3 +32,15 @@ export 'models/config/video/editor/video_configurations.dart'; export 'models/config/video/editor/video_web_configurations.dart'; export 'models/config/video/toolbar/video_configurations.dart'; export 'utils/utils.dart'; + +@immutable +class FlutterQuillExtensions { + const FlutterQuillExtensions._(); + + /// Override default implementation of [ClipboardServiceProvider.instacne] + /// to allow `flutter_quill` package to use `super_clipboard` plugin + /// to support rich text features, gif and images. + static void useSuperClipboardPlugin() { + ClipboardServiceProvider.setInstance(SuperClipboardService()); + } +} diff --git a/flutter_quill_extensions/lib/services/clipboard/super_clipboard_service.dart b/flutter_quill_extensions/lib/services/clipboard/super_clipboard_service.dart new file mode 100644 index 00000000..a808c9b5 --- /dev/null +++ b/flutter_quill_extensions/lib/services/clipboard/super_clipboard_service.dart @@ -0,0 +1,127 @@ +import 'dart:async' show Completer; + +import 'package:flutter/foundation.dart'; +// ignore: implementation_imports +import 'package:flutter_quill/src/services/clipboard/clipboard_service.dart'; +import 'package:super_clipboard/super_clipboard.dart'; + +/// Implementation based on https://pub.dev/packages/super_clipboard +class SuperClipboardService implements ClipboardService { + /// Null if the Clipboard API is not supported on this platform + /// https://pub.dev/packages/super_clipboard#usage + SystemClipboard? _getSuperClipboard() { + return SystemClipboard.instance; + } + + Future _canProvide({required DataFormat format}) async { + final clipboard = _getSuperClipboard(); + if (clipboard == null) { + return false; + } + final reader = await clipboard.read(); + return reader.canProvide(format); + } + + Future _provideFileAsBytes({required FileFormat format}) async { + final clipboard = _getSuperClipboard(); + if (clipboard == null) { + // To avoid getting this exception, use _canProvide() + throw UnsupportedError( + 'Clipboard API is not supported on this platform.', + ); + } + final reader = await clipboard.read(); + final completer = Completer(); + + reader.getFile(format, (file) async { + final bytes = await file.readAll(); + completer.complete(bytes); + }); + final bytes = await completer.future; + return bytes; + } + + /// According to super_clipboard docs, will return `null` if the value + /// is not available or the data is virtual (macOS and Windows) + Future _provideSimpleValueFormatAsString({ + required SimpleValueFormat format, + }) async { + final clipboard = _getSuperClipboard(); + if (clipboard == null) { + // To avoid getting this exception, use _canProvide() + throw UnsupportedError( + 'Clipboard API is not supported on this platform.', + ); + } + final reader = await clipboard.read(); + final value = await reader.readValue(format); + return value; + } + + /// This will need to be updated if [getImageFileAsBytes] updated. + /// Notice that even if the copied image is JPEG, it still can be provided + /// as PNG, will handle JPEG check in case this info is incorrect. + @override + Future canProvideImageFile() async { + final canProvidePngFile = await _canProvide(format: Formats.png); + if (canProvidePngFile) { + return true; + } + final canProvideJpegFile = await _canProvide(format: Formats.jpeg); + if (canProvideJpegFile) { + return true; + } + return false; + } + + /// This will need to be updated if [canProvideImageFile] updated. + @override + Future getImageFileAsBytes() async { + final canProvidePngFile = await _canProvide(format: Formats.png); + if (canProvidePngFile) { + return _provideFileAsBytes(format: Formats.png); + } + return _provideFileAsBytes(format: Formats.jpeg); + } + + @override + Future canProvidePlainText() { + return _canProvide(format: Formats.plainText); + } + + @override + Future getPlainText() { + return _provideSimpleValueFormatAsString(format: Formats.plainText); + } + + @override + Future canProvideHtmlText() { + return _canProvide(format: Formats.htmlText); + } + + @override + Future getHtmlText() { + return _provideSimpleValueFormatAsString(format: Formats.htmlText); + } + + @override + Future canProvideGifFile() { + return _canProvide(format: Formats.gif); + } + + @override + Future getGifFileAsBytes() { + return _provideFileAsBytes(format: Formats.gif); + } + + @override + Future canPaste() async { + final clipboard = _getSuperClipboard(); + if (clipboard == null) { + return false; + } + final reader = await clipboard.read(); + final availablePlatformFormats = reader.platformFormats; + return availablePlatformFormats.isNotEmpty; + } +} diff --git a/lib/src/services/clipboard/clipboard_service.dart b/lib/src/services/clipboard/clipboard_service.dart new file mode 100644 index 00000000..5043127f --- /dev/null +++ b/lib/src/services/clipboard/clipboard_service.dart @@ -0,0 +1,19 @@ +import 'package:flutter/foundation.dart'; + +/// An abstraction to make it easy to provide different implementations +@immutable +abstract class ClipboardService { + Future canProvideHtmlText(); + Future getHtmlText(); + + Future canProvidePlainText(); + Future getPlainText(); + + Future canProvideImageFile(); + Future getImageFileAsBytes(); + + Future canProvideGifFile(); + Future getGifFileAsBytes(); + + Future canPaste(); +} diff --git a/lib/src/services/clipboard/clipboard_service_provider.dart b/lib/src/services/clipboard/clipboard_service_provider.dart new file mode 100644 index 00000000..2766ada1 --- /dev/null +++ b/lib/src/services/clipboard/clipboard_service_provider.dart @@ -0,0 +1,19 @@ +import 'package:meta/meta.dart'; + +import 'clipboard_service.dart'; +import 'default_clipboard_service.dart'; + +@immutable +class ClipboardServiceProvider { + const ClipboardServiceProvider._(); + static ClipboardService _instance = DefaultClipboardService(); + static ClipboardService get instacne => _instance; + + static void setInstance(ClipboardService service) { + _instance = service; + } + + static void setInstanceToDefault() { + _instance = DefaultClipboardService(); + } +} diff --git a/lib/src/services/clipboard/default_clipboard_service.dart b/lib/src/services/clipboard/default_clipboard_service.dart new file mode 100644 index 00000000..a65df0d8 --- /dev/null +++ b/lib/src/services/clipboard/default_clipboard_service.dart @@ -0,0 +1,60 @@ +import 'package:flutter/services.dart' show Clipboard, Uint8List; + +import 'clipboard_service.dart'; + +/// Default implementation using only internal flutter plugins +class DefaultClipboardService implements ClipboardService { + @override + Future canProvideGifFile() async { + return false; + } + + @override + Future canProvideHtmlText() async { + return false; + } + + @override + Future canProvideImageFile() async { + return false; + } + + @override + Future canProvidePlainText() async { + final plainText = (await Clipboard.getData(Clipboard.kTextPlain))?.text; + return plainText == null; + } + + @override + Future getGifFileAsBytes() { + throw UnsupportedError( + 'DefaultClipboardService does not support retrieving GIF files.', + ); + } + + @override + Future getHtmlText() { + throw UnsupportedError( + 'DefaultClipboardService does not support retrieving HTML text.', + ); + } + + @override + Future getImageFileAsBytes() { + throw UnsupportedError( + 'DefaultClipboardService does not support retrieving image files.', + ); + } + + @override + Future getPlainText() async { + final plainText = (await Clipboard.getData(Clipboard.kTextPlain))?.text; + return plainText; + } + + @override + Future canPaste() async { + final plainText = await getPlainText(); + return plainText != null; + } +} diff --git a/lib/src/widgets/quill/quill_controller.dart b/lib/src/widgets/quill/quill_controller.dart index b6369e2e..f9c18fdb 100644 --- a/lib/src/widgets/quill/quill_controller.dart +++ b/lib/src/widgets/quill/quill_controller.dart @@ -4,11 +4,11 @@ import 'package:flutter/services.dart' show ClipboardData, Clipboard; import 'package:flutter/widgets.dart'; import 'package:html/parser.dart' as html_parser; import 'package:meta/meta.dart'; -import 'package:super_clipboard/super_clipboard.dart'; import '../../../flutter_quill.dart'; import '../../../quill_delta.dart'; import '../../models/documents/delta_x.dart'; +import '../../services/clipboard/clipboard_service_provider.dart'; import '../../utils/delta.dart'; typedef ReplaceTextCallback = bool Function(int index, int len, Object? data); @@ -514,9 +514,10 @@ class QuillController extends ChangeNotifier { // Snapshot the input before using `await`. // See https://github.com/flutter/flutter/issues/11427 - final plainText = await Clipboard.getData(Clipboard.kTextPlain); - if (plainText != null) { - final lines = plainText.text!.split('\n'); + final plainTextClipboardData = + await Clipboard.getData(Clipboard.kTextPlain); + if (plainTextClipboardData != null) { + final lines = plainTextClipboardData.text!.split('\n'); for (var i = 0; i < lines.length; ++i) { final line = lines[i]; if (line.isNotEmpty) { @@ -549,27 +550,26 @@ class QuillController extends ChangeNotifier { return false; } + /// True if can paste using HTML Future _pasteHTML() async { - final clipboard = SystemClipboard.instance; - if (clipboard != null) { - final reader = await clipboard.read(); - if (reader.canProvide(Formats.htmlText)) { - final html = await reader.readValue(Formats.htmlText); - if (html == null) { - return false; - } - final htmlBody = html_parser.parse(html).body?.outerHtml; - final deltaFromClipboard = DeltaX.fromHtml(htmlBody ?? html); - - replaceText( - selection.start, - selection.end - selection.start, - deltaFromClipboard, - TextSelection.collapsed(offset: selection.end), - ); + final clipboardService = ClipboardServiceProvider.instacne; + if (await clipboardService.canProvideHtmlText()) { + final html = await clipboardService.getHtmlText(); - return true; + if (html == null) { + return false; } + final htmlBody = html_parser.parse(html).body?.outerHtml; + final deltaFromClipboard = DeltaX.fromHtml(htmlBody ?? html); + + replaceText( + selection.start, + selection.end - selection.start, + deltaFromClipboard, + TextSelection.collapsed(offset: selection.end), + ); + + return true; } return false; } diff --git a/lib/src/widgets/quill/text_line.dart b/lib/src/widgets/quill/text_line.dart index e03e5ae8..88ae7020 100644 --- a/lib/src/widgets/quill/text_line.dart +++ b/lib/src/widgets/quill/text_line.dart @@ -500,7 +500,6 @@ class _TextLineState extends State { _tapLink(link); break; case LinkMenuAction.copy: - // ignore: unawaited_futures Clipboard.setData(ClipboardData(text: link)); break; case LinkMenuAction.remove: diff --git a/lib/src/widgets/raw_editor/raw_editor_state.dart b/lib/src/widgets/raw_editor/raw_editor_state.dart index 07e5f013..6f146b8f 100644 --- a/lib/src/widgets/raw_editor/raw_editor_state.dart +++ b/lib/src/widgets/raw_editor/raw_editor_state.dart @@ -19,7 +19,6 @@ import 'package:flutter/services.dart' TextInputControl; import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart' show KeyboardVisibilityController; -import 'package:super_clipboard/super_clipboard.dart'; import '../../models/documents/attribute.dart'; import '../../models/documents/document.dart'; @@ -30,6 +29,7 @@ import '../../models/documents/nodes/line.dart'; import '../../models/documents/nodes/node.dart'; import '../../models/structs/offset_value.dart'; import '../../models/structs/vertical_spacing.dart'; +import '../../services/clipboard/clipboard_service_provider.dart'; import '../../utils/cast.dart'; import '../../utils/delta.dart'; import '../../utils/embeds.dart'; @@ -184,53 +184,41 @@ class QuillRawEditorState extends EditorState return; } - final clipboard = SystemClipboard.instance; + final clipboardService = ClipboardServiceProvider.instacne; final onImagePaste = widget.configurations.onImagePaste; if (onImagePaste != null) { - if (clipboard != null) { - final reader = await clipboard.read(); - if (reader.canProvide(Formats.png)) { - reader.getFile(Formats.png, (value) async { - final image = value; - - final imageUrl = await onImagePaste(await image.readAll()); - if (imageUrl == null) { - return; - } - - controller.replaceText( - textEditingValue.selection.end, - 0, - BlockEmbed.image(imageUrl), - null, - ); - }); + if (await clipboardService.canProvideImageFile()) { + final imageBytes = await clipboardService.getImageFileAsBytes(); + final imageUrl = await onImagePaste(imageBytes); + if (imageUrl == null) { + return; } + + controller.replaceText( + textEditingValue.selection.end, + 0, + BlockEmbed.image(imageUrl), + null, + ); } } final onGifPaste = widget.configurations.onGifPaste; if (onGifPaste != null) { - if (clipboard != null) { - final reader = await clipboard.read(); - if (reader.canProvide(Formats.gif)) { - reader.getFile(Formats.gif, (value) async { - final gif = value; - - final gifUrl = await onGifPaste(await gif.readAll()); - if (gifUrl == null) { - return; - } - - controller.replaceText( - textEditingValue.selection.end, - 0, - BlockEmbed.image(gifUrl), - null, - ); - }); + if (await clipboardService.canProvideGifFile()) { + final gifBytes = await clipboardService.getGifFileAsBytes(); + final gifUrl = await onGifPaste(gifBytes); + if (gifUrl == null) { + return; } + + controller.replaceText( + textEditingValue.selection.end, + 0, + BlockEmbed.image(gifUrl), + null, + ); } } } diff --git a/lib/src/widgets/toolbar/buttons/clipboard_button.dart b/lib/src/widgets/toolbar/buttons/clipboard_button.dart index ab873b7d..29b5162a 100644 --- a/lib/src/widgets/toolbar/buttons/clipboard_button.dart +++ b/lib/src/widgets/toolbar/buttons/clipboard_button.dart @@ -2,11 +2,11 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:super_clipboard/super_clipboard.dart'; import '../../../../extensions.dart'; import '../../../../flutter_quill.dart'; import '../../../l10n/extensions/localizations.dart'; +import '../../../services/clipboard/clipboard_service_provider.dart'; import '../base_button/base_value_button.dart'; enum ClipboardAction { cut, copy, paste } @@ -27,13 +27,10 @@ class ClipboardMonitor { } Future _update(void Function() listener) async { - final reader = await SystemClipboard.instance?.read(); - if (reader != null) { - final available = reader.platformFormats; - if (_canPaste != available.isNotEmpty) { - _canPaste = available.isNotEmpty; - listener(); - } + final clipboardService = ClipboardServiceProvider.instacne; + if (await clipboardService.canPaste()) { + _canPaste = true; + listener(); } } } diff --git a/pubspec.yaml b/pubspec.yaml index f25d46b1..d4bfc5c9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -62,7 +62,6 @@ dependencies: url_launcher: ^6.2.4 flutter_keyboard_visibility: ^6.0.0 device_info_plus: ^10.0.1 - super_clipboard: ^0.8.15 dev_dependencies: flutter_lints: ^4.0.0 diff --git a/scripts/pub_get.sh b/scripts/pub_get.sh old mode 100644 new mode 100755