Drag and drop feature in the example and builder for QuillEditor (#1508)

* Drag and drop feature in the example

* Update README.md of `flutter_quill_extensions`

* Add builder method in `QuillEditorConfigurations`
pull/1511/head
Ellet 1 year ago committed by GitHub
parent 7fdadc051f
commit b020dd6b7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      CHANGELOG.md
  2. 41
      example/lib/pages/home_page.dart
  3. 4
      example/linux/flutter/generated_plugin_registrant.cc
  4. 1
      example/linux/flutter/generated_plugins.cmake
  5. 2
      example/macos/Flutter/GeneratedPluginRegistrant.swift
  6. 1
      example/pubspec.yaml
  7. 3
      example/windows/flutter/generated_plugin_registrant.cc
  8. 1
      example/windows/flutter/generated_plugins.cmake
  9. 3
      flutter_quill_extensions/CHANGELOG.md
  10. 73
      flutter_quill_extensions/README.md
  11. 2
      flutter_quill_extensions/pubspec.yaml
  12. 11
      lib/src/models/config/editor/configurations.dart
  13. 108
      lib/src/widgets/editor/editor.dart
  14. 31
      lib/src/widgets/editor/editor_builder.dart
  15. 2
      pubspec.yaml

@ -1,3 +1,6 @@
## [8.2.5]
- Add `builder` property in the `QuillEditorConfigurations`
## [8.2.4]
- Follow flutter best practices
- Auto focus bug fix

@ -5,6 +5,7 @@ import 'dart:convert';
import 'dart:io' show File;
import 'dart:ui';
import 'package:desktop_drop/desktop_drop.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@ -13,6 +14,7 @@ import 'package:flutter_quill/extensions.dart';
import 'package:flutter_quill/flutter_quill.dart';
import 'package:flutter_quill_extensions/flutter_quill_extensions.dart';
import 'package:flutter_quill_extensions/logic/services/image_picker/image_picker.dart';
import 'package:flutter_quill_extensions/presentation/embeds/widgets/image.dart';
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';
@ -333,12 +335,45 @@ class _HomePageState extends State<HomePage> {
});
}
OnDragDoneCallback get _onDragDone {
return (details) {
final scaffoldMessenger = ScaffoldMessenger.of(context);
final file = details.files.first;
final isSupported =
imageFileExtensions.any((ext) => file.name.endsWith(ext));
if (!isSupported) {
scaffoldMessenger.showSnackBar(
SnackBar(
content: Text(
'Only images are supported right now: ${file.mimeType}, ${file.name}, ${file.path}, $imageFileExtensions',
),
),
);
return;
}
_controller.insertImageBlock(
imageSource: file.path,
);
scaffoldMessenger.showSnackBar(
const SnackBar(
content: Text('Image is inserted.'),
),
);
};
}
QuillEditor get quillEditor {
if (kIsWeb) {
return QuillEditor(
focusNode: _focusNode,
scrollController: ScrollController(),
configurations: QuillEditorConfigurations(
builder: (context, rawEditor) {
return DropTarget(
onDragDone: _onDragDone,
child: rawEditor,
);
},
placeholder: 'Add content',
readOnly: false,
scrollable: true,
@ -370,6 +405,12 @@ class _HomePageState extends State<HomePage> {
}
return QuillEditor(
configurations: QuillEditorConfigurations(
builder: (context, rawEditor) {
return DropTarget(
onDragDone: _onDragDone,
child: rawEditor,
);
},
placeholder: 'Add content',
readOnly: _isReadOnly,
autoFocus: false,

@ -6,11 +6,15 @@
#include "generated_plugin_registrant.h"
#include <desktop_drop/desktop_drop_plugin.h>
#include <file_selector_linux/file_selector_plugin.h>
#include <pasteboard/pasteboard_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
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_selector_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);

@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
desktop_drop
file_selector_linux
pasteboard
url_launcher_linux

@ -5,6 +5,7 @@
import FlutterMacOS
import Foundation
import desktop_drop
import device_info_plus
import file_selector_macos
import gal
@ -14,6 +15,7 @@ import url_launcher_macos
import video_player_avfoundation
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
DesktopDropPlugin.register(with: registry.registrar(forPlugin: "DesktopDropPlugin"))
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
GalPlugin.register(with: registry.registrar(forPlugin: "GalPlugin"))

@ -20,6 +20,7 @@ dependencies:
flutter_quill: ^8.2.3
flutter_quill_extensions: ^0.6.1
path: ^1.8.3
desktop_drop: ^0.4.4
dependency_overrides:
flutter_quill:

@ -6,12 +6,15 @@
#include "generated_plugin_registrant.h"
#include <desktop_drop/desktop_drop_plugin.h>
#include <file_selector_windows/file_selector_windows.h>
#include <gal/gal_plugin_c_api.h>
#include <pasteboard/pasteboard_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
DesktopDropPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("DesktopDropPlugin"));
FileSelectorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSelectorWindows"));
GalPluginCApiRegisterWithRegistrar(

@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
desktop_drop
file_selector_windows
gal
pasteboard

@ -1,3 +1,6 @@
## 0.6.3
- Update `README.md`
## 0.6.2
- Add more default exports

@ -18,6 +18,7 @@ Currently the support for **Web** is limitied.
- [Embed Blocks](#embed-blocks)
- [Custom Size Image for Mobile](#custom-size-image-for-mobile)
- [Custom Size Image for other platforms (excluding web)](#custom-size-image-for-other-platforms-excluding-web)
- [Drag and drop feature](#drag-and-drop-feature)
- [Features](#features)
- [Contributing](#contributing)
- [License](#license)
@ -153,6 +154,78 @@ On mobile we will use `mobileWidth`, `mobileHeight`, on desktop will use `width`
on Web we will use the `width` and the `height` but the ones in the `attributes`
This may not clear but don't worry we will update it soon.
### Drag and drop feature
Currently the drag and drop feature is not offically supported but you can achieve this very easily in the following steps:
1. Drag and drop require native code, you can use any flutter plugin you like, if you want a suggestion we recommend [desktop_drop](https://pub.dev/packages/desktop_drop), it was origanlly developed for desktop but it has support for web as well mobile platforms
2. Add the dependency in your `pubspec.yaml` using the following command:
```yaml
flutter pub add desktop_drop
```
and import it with
```dart
import 'package:desktop_drop/desktop_drop.dart';
```
3. in the configurations of `QuillEditor`, use the `builder` to wrap the editor with `DropTarget` which comes from `desktop_drop`
```dart
import 'package:flutter_quill_extensions/flutter_quill_extensions.dart';
QuillEditor.basic(
configurations: QuillEditorConfigurations(
padding: const EdgeInsets.all(16),
builder: (context, rawEditor) {
return DropTarget(
onDragDone: _onDragDone,
child: rawEditor,
);
},
embedBuilders: kIsWeb
? FlutterQuillEmbeds.editorsWebBuilders()
: FlutterQuillEmbeds.editorBuilders(),
),
)
```
4. Implement the `_onDragDone`, it depend on your use case but this just a simple example
```dart
const List<String> imageFileExtensions = [
'.jpeg',
'.png',
'.jpg',
'.gif',
'.webp',
'.tif',
'.heic'
];
OnDragDoneCallback get _onDragDone {
return (details) {
final scaffoldMessenger = ScaffoldMessenger.of(context);
final file = details.files.first;
final isSupported =
imageFileExtensions.any((ext) => file.name.endsWith(ext));
if (!isSupported) {
scaffoldMessenger.showSnackBar(
SnackBar(
content: Text(
'Only images are supported right now: ${file.mimeType}, ${file.name}, ${file.path}, $imageFileExtensions',
),
),
);
return;
}
// To get this extension function please import flutter_quill_extensions
_controller.insertImageBlock(
imageSource: file.path,
);
scaffoldMessenger.showSnackBar(
const SnackBar(
content: Text('Image is inserted.'),
),
);
};
}
```
## Features
```markdown

@ -1,6 +1,6 @@
name: flutter_quill_extensions
description: Embed extensions for flutter_quill including image, video, formula and etc.
version: 0.6.2
version: 0.6.3
homepage: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_extensions
repository: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_extensions

@ -8,6 +8,7 @@ import 'package:flutter/widgets.dart';
import '../../../widgets/default_styles.dart';
import '../../../widgets/delegate.dart';
import '../../../widgets/editor/editor.dart';
import '../../../widgets/editor/editor_builder.dart';
import '../../../widgets/embeds.dart';
import '../../../widgets/link.dart';
import '../../../widgets/raw_editor/raw_editor.dart';
@ -67,6 +68,7 @@ class QuillEditorConfigurations extends Equatable {
this.editorKey,
this.requestKeyboardFocusOnCheckListChanged = false,
this.elementOptions = const QuillEditorElementOptions(),
this.builder,
});
/// The text placeholder in the quill editor
@ -306,6 +308,8 @@ class QuillEditorConfigurations extends Equatable {
/// This is not complete yet and might changed
final QuillEditorElementOptions elementOptions;
final QuillEditorBuilder? builder;
@override
List<Object?> get props => [
placeholder,
@ -323,7 +327,7 @@ class QuillEditorConfigurations extends Equatable {
double? scrollBottomInset,
EdgeInsetsGeometry? padding,
bool? autoFocus,
bool? enableUnfocusOnTapOutside,
bool? isOnTapOutsideEnabled,
Function(PointerDownEvent event, FocusNode focusNode)? onTapOutside,
bool? showCursor,
bool? paintCursorAboveText,
@ -357,6 +361,7 @@ class QuillEditorConfigurations extends Equatable {
TextSelectionThemeData? textSelectionThemeData,
bool? requestKeyboardFocusOnCheckListChanged,
QuillEditorElementOptions? elementOptions,
QuillEditorBuilder? builder,
}) {
return QuillEditorConfigurations(
placeholder: placeholder ?? this.placeholder,
@ -365,7 +370,8 @@ class QuillEditorConfigurations extends Equatable {
scrollBottomInset: scrollBottomInset ?? this.scrollBottomInset,
padding: padding ?? this.padding,
autoFocus: autoFocus ?? this.autoFocus,
isOnTapOutsideEnabled: enableUnfocusOnTapOutside ?? isOnTapOutsideEnabled,
isOnTapOutsideEnabled:
isOnTapOutsideEnabled ?? this.isOnTapOutsideEnabled,
onTapOutside: onTapOutside ?? this.onTapOutside,
showCursor: showCursor ?? this.showCursor,
paintCursorAboveText: paintCursorAboveText ?? this.paintCursorAboveText,
@ -409,6 +415,7 @@ class QuillEditorConfigurations extends Equatable {
requestKeyboardFocusOnCheckListChanged ??
this.requestKeyboardFocusOnCheckListChanged,
elementOptions: elementOptions ?? this.elementOptions,
builder: builder ?? this.builder,
);
}
}

@ -16,6 +16,7 @@ import '../cursor.dart';
import '../delegate.dart';
import '../float_cursor.dart';
import '../text_selection.dart';
import 'editor_builder.dart';
/// Base interface for the editor state which defines contract used by
/// various mixins.
@ -238,59 +239,62 @@ class QuillEditorState extends State<QuillEditor>
final child = QuillEditorProvider(
editorConfigurations: configurations,
child: QuillRawEditor(
key: _editorKey,
controller: context.requireQuillController,
focusNode: widget.focusNode,
scrollController: widget.scrollController,
scrollable: configurations.scrollable,
scrollBottomInset: configurations.scrollBottomInset,
padding: configurations.padding,
readOnly: configurations.readOnly,
placeholder: configurations.placeholder,
onLaunchUrl: configurations.onLaunchUrl,
contextMenuBuilder: showSelectionToolbar
? (configurations.contextMenuBuilder ??
QuillRawEditor.defaultContextMenuBuilder)
: null,
showSelectionHandles: isMobile(theme.platform),
showCursor: configurations.showCursor,
cursorStyle: CursorStyle(
color: cursorColor,
backgroundColor: Colors.grey,
width: 2,
radius: cursorRadius,
offset: cursorOffset,
paintAboveText:
configurations.paintCursorAboveText ?? paintCursorAboveText,
opacityAnimates: cursorOpacityAnimates,
child: QuillEditorBuilderWidget(
builder: configurations.builder,
child: QuillRawEditor(
key: _editorKey,
controller: context.requireQuillController,
focusNode: widget.focusNode,
scrollController: widget.scrollController,
scrollable: configurations.scrollable,
scrollBottomInset: configurations.scrollBottomInset,
padding: configurations.padding,
readOnly: configurations.readOnly,
placeholder: configurations.placeholder,
onLaunchUrl: configurations.onLaunchUrl,
contextMenuBuilder: showSelectionToolbar
? (configurations.contextMenuBuilder ??
QuillRawEditor.defaultContextMenuBuilder)
: null,
showSelectionHandles: isMobile(theme.platform),
showCursor: configurations.showCursor,
cursorStyle: CursorStyle(
color: cursorColor,
backgroundColor: Colors.grey,
width: 2,
radius: cursorRadius,
offset: cursorOffset,
paintAboveText:
configurations.paintCursorAboveText ?? paintCursorAboveText,
opacityAnimates: cursorOpacityAnimates,
),
textCapitalization: configurations.textCapitalization,
minHeight: configurations.minHeight,
maxHeight: configurations.maxHeight,
maxContentWidth: configurations.maxContentWidth,
customStyles: configurations.customStyles,
expands: configurations.expands,
autoFocus: configurations.autoFocus,
selectionColor: selectionColor,
selectionCtrls:
configurations.textSelectionControls ?? textSelectionControls,
keyboardAppearance: configurations.keyboardAppearance,
enableInteractiveSelection: configurations.enableInteractiveSelection,
scrollPhysics: configurations.scrollPhysics,
embedBuilder: _getEmbedBuilder,
linkActionPickerDelegate: configurations.linkActionPickerDelegate,
customStyleBuilder: configurations.customStyleBuilder,
customRecognizerBuilder: configurations.customRecognizerBuilder,
floatingCursorDisabled: configurations.floatingCursorDisabled,
onImagePaste: configurations.onImagePaste,
customShortcuts: configurations.customShortcuts,
customActions: configurations.customActions,
customLinkPrefixes: configurations.customLinkPrefixes,
enableUnfocusOnTapOutside: configurations.isOnTapOutsideEnabled,
dialogTheme: configurations.dialogTheme,
contentInsertionConfiguration:
configurations.contentInsertionConfiguration,
),
textCapitalization: configurations.textCapitalization,
minHeight: configurations.minHeight,
maxHeight: configurations.maxHeight,
maxContentWidth: configurations.maxContentWidth,
customStyles: configurations.customStyles,
expands: configurations.expands,
autoFocus: configurations.autoFocus,
selectionColor: selectionColor,
selectionCtrls:
configurations.textSelectionControls ?? textSelectionControls,
keyboardAppearance: configurations.keyboardAppearance,
enableInteractiveSelection: configurations.enableInteractiveSelection,
scrollPhysics: configurations.scrollPhysics,
embedBuilder: _getEmbedBuilder,
linkActionPickerDelegate: configurations.linkActionPickerDelegate,
customStyleBuilder: configurations.customStyleBuilder,
customRecognizerBuilder: configurations.customRecognizerBuilder,
floatingCursorDisabled: configurations.floatingCursorDisabled,
onImagePaste: configurations.onImagePaste,
customShortcuts: configurations.customShortcuts,
customActions: configurations.customActions,
customLinkPrefixes: configurations.customLinkPrefixes,
enableUnfocusOnTapOutside: configurations.isOnTapOutsideEnabled,
dialogTheme: configurations.dialogTheme,
contentInsertionConfiguration:
configurations.contentInsertionConfiguration,
),
);

@ -0,0 +1,31 @@
import 'package:flutter/material.dart';
import '../raw_editor/raw_editor.dart';
typedef QuillEditorBuilder = Widget Function(
BuildContext context,
QuillRawEditor rawEditor,
);
class QuillEditorBuilderWidget extends StatelessWidget {
const QuillEditorBuilderWidget({
required this.child,
this.builder,
super.key,
});
final QuillRawEditor child;
final QuillEditorBuilder? builder;
@override
Widget build(BuildContext context) {
final builderCallback = builder;
if (builderCallback != null) {
return builderCallback(
context,
child,
);
}
return child;
}
}

@ -1,6 +1,6 @@
name: flutter_quill
description: A rich text editor built for the modern Android, iOS, web and desktop platforms. It is the WYSIWYG editor and a Quill component for Flutter.
version: 8.2.4
version: 8.2.5
homepage: https://1o24bbs.com/c/bulletjournal/108
repository: https://github.com/singerdmx/flutter-quill

Loading…
Cancel
Save