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 pastepull/1915/head
parent
784aee35f2
commit
743c829e48
18 changed files with 364 additions and 117 deletions
@ -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<bool> _canProvide({required DataFormat format}) async { |
||||||
|
final clipboard = _getSuperClipboard(); |
||||||
|
if (clipboard == null) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
final reader = await clipboard.read(); |
||||||
|
return reader.canProvide(format); |
||||||
|
} |
||||||
|
|
||||||
|
Future<Uint8List> _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<Uint8List>(); |
||||||
|
|
||||||
|
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<String?> _provideSimpleValueFormatAsString({ |
||||||
|
required SimpleValueFormat<String> 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<String>(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<bool> 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<Uint8List> getImageFileAsBytes() async { |
||||||
|
final canProvidePngFile = await _canProvide(format: Formats.png); |
||||||
|
if (canProvidePngFile) { |
||||||
|
return _provideFileAsBytes(format: Formats.png); |
||||||
|
} |
||||||
|
return _provideFileAsBytes(format: Formats.jpeg); |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Future<bool> canProvidePlainText() { |
||||||
|
return _canProvide(format: Formats.plainText); |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Future<String?> getPlainText() { |
||||||
|
return _provideSimpleValueFormatAsString(format: Formats.plainText); |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Future<bool> canProvideHtmlText() { |
||||||
|
return _canProvide(format: Formats.htmlText); |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Future<String?> getHtmlText() { |
||||||
|
return _provideSimpleValueFormatAsString(format: Formats.htmlText); |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Future<bool> canProvideGifFile() { |
||||||
|
return _canProvide(format: Formats.gif); |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Future<Uint8List> getGifFileAsBytes() { |
||||||
|
return _provideFileAsBytes(format: Formats.gif); |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Future<bool> canPaste() async { |
||||||
|
final clipboard = _getSuperClipboard(); |
||||||
|
if (clipboard == null) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
final reader = await clipboard.read(); |
||||||
|
final availablePlatformFormats = reader.platformFormats; |
||||||
|
return availablePlatformFormats.isNotEmpty; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
import 'package:flutter/foundation.dart'; |
||||||
|
|
||||||
|
/// An abstraction to make it easy to provide different implementations |
||||||
|
@immutable |
||||||
|
abstract class ClipboardService { |
||||||
|
Future<bool> canProvideHtmlText(); |
||||||
|
Future<String?> getHtmlText(); |
||||||
|
|
||||||
|
Future<bool> canProvidePlainText(); |
||||||
|
Future<String?> getPlainText(); |
||||||
|
|
||||||
|
Future<bool> canProvideImageFile(); |
||||||
|
Future<Uint8List> getImageFileAsBytes(); |
||||||
|
|
||||||
|
Future<bool> canProvideGifFile(); |
||||||
|
Future<Uint8List> getGifFileAsBytes(); |
||||||
|
|
||||||
|
Future<bool> canPaste(); |
||||||
|
} |
@ -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(); |
||||||
|
} |
||||||
|
} |
@ -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<bool> canProvideGifFile() async { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Future<bool> canProvideHtmlText() async { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Future<bool> canProvideImageFile() async { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Future<bool> canProvidePlainText() async { |
||||||
|
final plainText = (await Clipboard.getData(Clipboard.kTextPlain))?.text; |
||||||
|
return plainText == null; |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Future<Uint8List> getGifFileAsBytes() { |
||||||
|
throw UnsupportedError( |
||||||
|
'DefaultClipboardService does not support retrieving GIF files.', |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Future<String?> getHtmlText() { |
||||||
|
throw UnsupportedError( |
||||||
|
'DefaultClipboardService does not support retrieving HTML text.', |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Future<Uint8List> getImageFileAsBytes() { |
||||||
|
throw UnsupportedError( |
||||||
|
'DefaultClipboardService does not support retrieving image files.', |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Future<String?> getPlainText() async { |
||||||
|
final plainText = (await Clipboard.getData(Clipboard.kTextPlain))?.text; |
||||||
|
return plainText; |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Future<bool> canPaste() async { |
||||||
|
final plainText = await getPlainText(); |
||||||
|
return plainText != null; |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue