From 2104514f6e7b64e49b34effb1e6fd0ac90720b0b Mon Sep 17 00:00:00 2001 From: Ellet <hello@freshplatform.net> Date: Sat, 11 Nov 2023 04:34:41 +0300 Subject: [PATCH] Update flutter_quill and flutter_quill_extensions (#1520) * Update flutter_quill and flutter_quill_extensions --- CHANGELOG.md | 8 + doc/todo.md | 1 + .../android/app/src/main/AndroidManifest.xml | 6 + example/lib/pages/home_page.dart | 44 ++- example/pubspec.yaml | 5 +- example/web/index.html | 5 + flutter_quill_extensions/CHANGELOG.md | 4 + .../embeds/editor/image/image.dart | 41 ++- .../embeds/editor/image/image_menu.dart | 9 +- .../toolbar/camera_button/camera_button.dart | 15 +- .../embeds/widgets/image_resizer.dart | 4 +- .../models/config/editor/image/image.dart | 18 +- flutter_quill_extensions/pubspec.yaml | 2 +- lib/flutter_quill.dart | 2 + .../models/config/editor/configurations.dart | 8 +- .../config/raw_editor/configurations.dart | 290 ++++++++++++++++++ lib/src/utils/extensions/build_context.dart | 2 +- lib/src/utils/string.dart | 4 +- lib/src/widgets/editor/editor.dart | 117 +++---- lib/src/widgets/raw_editor/raw_editor.dart | 250 +-------------- .../raw_editor/raw_editor_actions.dart | 18 +- .../widgets/raw_editor/raw_editor_state.dart | 178 +++++------ ...editor_state_selection_delegate_mixin.dart | 32 +- ..._editor_state_text_input_client_mixin.dart | 37 ++- .../widgets/toolbar/buttons/quill_icon.dart | 8 + .../toolbar/buttons/select_header_style.dart | 2 + lib/src/widgets/utils/provider.dart | 8 +- pubspec.yaml | 2 +- 28 files changed, 642 insertions(+), 478 deletions(-) create mode 100644 lib/src/models/config/raw_editor/configurations.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a9b7268..0cba5b33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## [8.4.2] +- **Breaking change**: The `QuillRawEditor` configurations has been moved to a seperated class, also the `readOnly` has been renamed to `isReadOnly`, if you are using the `QuillEditor` you don't have to change anything +- Allow the developer to use override the `TextInputAction` in both `QuillRawEditor` and `QuillEditor` +- You can use now the `QuillRawEditor` without `QuillEditorProvider` +- Bug fixes +- Add implementation of image cropping in the `example` +- Update the `QuillToolbarIconButton` to use the material 3 buttons + ## [8.4.1] - Add `copyWith` in `OptionalSize` class diff --git a/doc/todo.md b/doc/todo.md index 7b8aa9b8..d31c9c73 100644 --- a/doc/todo.md +++ b/doc/todo.md @@ -27,6 +27,7 @@ This is a todo list page that added recently and will be updated soon. 1. Improve the Raw Quill Editor, for more [info](https://github.com/singerdmx/flutter-quill/issues/1509) 2. Provide more support to all the platforms + 3. Extract the shared properties between `QuillRawEditorConfigurations` and `QuillEditorConfigurations` ### Bugs diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 1d6e0b64..6176738c 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -42,6 +42,12 @@ <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> + + <activity + android:name="com.yalantis.ucrop.UCropActivity" + android:screenOrientation="portrait" + android:theme="@style/Theme.AppCompat.Light.NoActionBar" /> + <!-- Don't delete the meta-data below. This is used by the Flutter tool to generate GeneratedPluginRegistrant.java --> <meta-data diff --git a/example/lib/pages/home_page.dart b/example/lib/pages/home_page.dart index c36e834c..5880cf2e 100644 --- a/example/lib/pages/home_page.dart +++ b/example/lib/pages/home_page.dart @@ -15,6 +15,7 @@ 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:image_cropper/image_cropper.dart'; import 'package:path/path.dart' as path; import 'package:path_provider/path_provider.dart'; @@ -455,6 +456,42 @@ class _HomePageState extends State<HomePage> { ); } + /// When inserting an image + OnImageInsertCallback get onImageInsert { + return (image, controller) async { + final croppedFile = await ImageCropper().cropImage( + sourcePath: image, + aspectRatioPresets: [ + CropAspectRatioPreset.square, + CropAspectRatioPreset.ratio3x2, + CropAspectRatioPreset.original, + CropAspectRatioPreset.ratio4x3, + CropAspectRatioPreset.ratio16x9 + ], + uiSettings: [ + AndroidUiSettings( + toolbarTitle: 'Cropper', + toolbarColor: Colors.deepOrange, + toolbarWidgetColor: Colors.white, + initAspectRatio: CropAspectRatioPreset.original, + lockAspectRatio: false, + ), + IOSUiSettings( + title: 'Cropper', + ), + WebUiSettings( + context: context, + ), + ], + ); + final newImage = croppedFile?.path; + if (newImage == null) { + return; + } + controller.insertImageBlock(imageSource: newImage); + }; + } + QuillToolbar get quillToolbar { final customButtons = [ QuillToolbarCustomButtonOptions( @@ -481,13 +518,14 @@ class _HomePageState extends State<HomePage> { configurations: QuillToolbarConfigurations( customButtons: customButtons, embedButtons: FlutterQuillEmbeds.toolbarButtons( + cameraButtonOptions: const QuillToolbarCameraButtonOptions(), imageButtonOptions: QuillToolbarImageButtonOptions( imageButtonConfigurations: QuillToolbarImageConfigurations( onImageInsertedCallback: (image) async { _onImagePickCallback(File(image)); }, + onImageInsertCallback: onImageInsert, ), - // webImagePickImpl: _webImagePickImpl, ), ), buttonOptions: QuillToolbarButtonOptions( @@ -496,7 +534,6 @@ class _HomePageState extends State<HomePage> { ), ), ), - // afterButtonPressed: _focusNode.requestFocus, ); } if (isDesktop(supportWeb: false)) { @@ -504,6 +541,7 @@ class _HomePageState extends State<HomePage> { configurations: QuillToolbarConfigurations( customButtons: customButtons, embedButtons: FlutterQuillEmbeds.toolbarButtons( + cameraButtonOptions: const QuillToolbarCameraButtonOptions(), imageButtonOptions: QuillToolbarImageButtonOptions( imageButtonConfigurations: QuillToolbarImageConfigurations( onImageInsertedCallback: (image) async { @@ -525,6 +563,7 @@ class _HomePageState extends State<HomePage> { configurations: QuillToolbarConfigurations( customButtons: customButtons, embedButtons: FlutterQuillEmbeds.toolbarButtons( + cameraButtonOptions: const QuillToolbarCameraButtonOptions(), videoButtonOptions: QuillToolbarVideoButtonOptions( videoConfigurations: QuillToolbarVideoConfigurations( onVideoInsertedCallback: (video) => @@ -533,6 +572,7 @@ class _HomePageState extends State<HomePage> { ), imageButtonOptions: QuillToolbarImageButtonOptions( imageButtonConfigurations: QuillToolbarImageConfigurations( + onImageInsertCallback: onImageInsert, onImageInsertedCallback: (image) async { _onImagePickCallback(File(image)); }, diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 5ff55f23..ccdf1958 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -17,10 +17,11 @@ dependencies: path_provider: ^2.1.1 # filesystem_picker: ^4.0.0 file_picker: ^6.1.1 - flutter_quill: ^8.2.5 - flutter_quill_extensions: ^0.6.3 + flutter_quill: ^8.4.1 + flutter_quill_extensions: ^0.6.5 path: ^1.8.3 desktop_drop: ^0.4.4 + image_cropper: ^5.0.0 dependency_overrides: flutter_quill: diff --git a/example/web/index.html b/example/web/index.html index 45cf2ca3..1029dffd 100644 --- a/example/web/index.html +++ b/example/web/index.html @@ -32,6 +32,11 @@ <title>example</title> <link rel="manifest" href="manifest.json"> + <!-- Croppie --> + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/croppie/2.6.5/croppie.css" /> + <script defer src="https://cdnjs.cloudflare.com/ajax/libs/exif-js/2.3.0/exif.js"></script> + <script src="https://cdnjs.cloudflare.com/ajax/libs/croppie/2.6.5/croppie.min.js"></script> + <script> // The value below is injected by flutter build, do not touch. const serviceWorkerVersion = null; diff --git a/flutter_quill_extensions/CHANGELOG.md b/flutter_quill_extensions/CHANGELOG.md index ffb6cd2c..07532371 100644 --- a/flutter_quill_extensions/CHANGELOG.md +++ b/flutter_quill_extensions/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.6 +- Add `onImageClicked` in the `QuillEditorImageEmbedConfigurations` +- Fix image resizing on mobile + ## 0.6.5 - Support the new improved platform checking of `flutter_quill` - Update the Image embed builder logic diff --git a/flutter_quill_extensions/lib/presentation/embeds/editor/image/image.dart b/flutter_quill_extensions/lib/presentation/embeds/editor/image/image.dart index 3e6cac2c..8badfa3a 100644 --- a/flutter_quill_extensions/lib/presentation/embeds/editor/image/image.dart +++ b/flutter_quill_extensions/lib/presentation/embeds/editor/image/image.dart @@ -112,6 +112,20 @@ class QuillEditorImageEmbedBuilder extends EmbedBuilder { QuillSharedExtensionsConfigurations.get(context: context) .imageSaverService; return GestureDetector( + onTap: configurations.onImageClicked ?? + () => showDialog( + context: context, + builder: (context) { + return ImageOptionsMenu( + controller: controller, + configurations: configurations, + imageSource: imageSource, + imageSize: imageSize, + isReadOnly: readOnly, + imageSaverService: imageSaverService, + ); + }, + ), child: Builder( builder: (context) { if (margin != null) { @@ -123,19 +137,6 @@ class QuillEditorImageEmbedBuilder extends EmbedBuilder { return image; }, ), - onTap: () => showDialog( - context: context, - builder: (context) { - return ImageOptionsMenu( - controller: controller, - configurations: configurations, - imageSource: imageSource, - imageSize: imageSize, - isReadOnly: readOnly, - imageSaverService: imageSaverService, - ); - }, - ), ); } } @@ -191,10 +192,16 @@ class QuillEditorImageEmbedBuilder extends EmbedBuilder { // It css value as string but we will try to support it anyway // TODO: This could be improved much better - final cssHeightValue = double.tryParse( - (attrs[Attribute.height.key] ?? '').replaceFirst('px', '')); - final cssWidthValue = double.tryParse( - (attrs[Attribute.width.key] ?? '').replaceFirst('px', '')); + final cssHeightValue = double.tryParse(((base.isMobile(supportWeb: false) + ? attrs[Attribute.mobileHeight] + : attrs[Attribute.height.key]) ?? + '') + .replaceFirst('px', '')); + final cssWidthValue = double.tryParse(((!base.isMobile(supportWeb: false) + ? attrs[Attribute.width.key] + : attrs[Attribute.mobileWidth]) ?? + '') + .replaceFirst('px', '')); if (cssHeightValue != null) { imageSize = imageSize.copyWith(height: cssHeightValue); diff --git a/flutter_quill_extensions/lib/presentation/embeds/editor/image/image_menu.dart b/flutter_quill_extensions/lib/presentation/embeds/editor/image/image_menu.dart index 6fc71864..e6dc2cb0 100644 --- a/flutter_quill_extensions/lib/presentation/embeds/editor/image/image_menu.dart +++ b/flutter_quill_extensions/lib/presentation/embeds/editor/image/image_menu.dart @@ -1,6 +1,5 @@ import 'package:flutter/cupertino.dart' show showCupertinoModalPopup; import 'package:flutter/material.dart'; -// import 'package:flutter/services.dart' show Clipboard, ClipboardData; import 'package:flutter_quill/extensions.dart' show isDesktop, isMobile, replaceStyleStringWithSize; import 'package:flutter_quill/flutter_quill.dart' @@ -51,7 +50,7 @@ class ImageOptionsMenu extends StatelessWidget { builder: (context) { final screenSize = MediaQuery.sizeOf(context); return ImageResizer( - onImageResize: (w, h) { + onImageResize: (width, height) { final res = getEmbedNode( controller, controller.selection.start, @@ -59,8 +58,8 @@ class ImageOptionsMenu extends StatelessWidget { final attr = replaceStyleStringWithSize( getImageStyleString(controller), - width: w, - height: h, + width: width, + height: height, isMobile: isMobile(supportWeb: false), ); controller @@ -94,7 +93,7 @@ class ImageOptionsMenu extends StatelessWidget { ); // TODO: Implement the copy image // await Clipboard.setData( - // ClipboardData(text: '$imageUrl'), + // ClipboardData(), // ); navigator.pop(); }, diff --git a/flutter_quill_extensions/lib/presentation/embeds/toolbar/camera_button/camera_button.dart b/flutter_quill_extensions/lib/presentation/embeds/toolbar/camera_button/camera_button.dart index be7d7a14..f31171a6 100644 --- a/flutter_quill_extensions/lib/presentation/embeds/toolbar/camera_button/camera_button.dart +++ b/flutter_quill_extensions/lib/presentation/embeds/toolbar/camera_button/camera_button.dart @@ -1,5 +1,3 @@ -// ignore_for_file: use_build_context_synchronously - import 'package:flutter/material.dart'; import 'package:flutter_quill/flutter_quill.dart'; import 'package:flutter_quill/translations.dart'; @@ -104,6 +102,7 @@ class QuillToolbarCameraButton extends StatelessWidget { size: iconSize * 1.77, fillColor: iconFillColor, borderRadius: iconTheme?.borderRadius ?? 2, + // isDesktop(supportWeb: false) ? null : onPressed: () => _sharedOnPressed(context), ); } @@ -126,11 +125,9 @@ class QuillToolbarCameraButton extends StatelessWidget { BuildContext context, QuillController controller, ) async { - // if (onVideoPickCallback == null && onImagePickCallback == null) { - // throw ArgumentError( - // 'onImagePickCallback and onVideoPickCallback are both null', - // ); - // } + final imagePickerService = + QuillSharedExtensionsConfigurations.get(context: context) + .imagePickerService; final cameraAction = await _getCameraAction(context); @@ -138,10 +135,6 @@ class QuillToolbarCameraButton extends StatelessWidget { return; } - final imagePickerService = - QuillSharedExtensionsConfigurations.get(context: context) - .imagePickerService; - switch (cameraAction) { case CameraAction.video: final videoFile = await imagePickerService.pickVideo( diff --git a/flutter_quill_extensions/lib/presentation/embeds/widgets/image_resizer.dart b/flutter_quill_extensions/lib/presentation/embeds/widgets/image_resizer.dart index 92f2fb7c..8d709426 100644 --- a/flutter_quill_extensions/lib/presentation/embeds/widgets/image_resizer.dart +++ b/flutter_quill_extensions/lib/presentation/embeds/widgets/image_resizer.dart @@ -48,7 +48,9 @@ class ImageResizerState extends State<ImageResizer> { case TargetPlatform.fuchsia: return _showMaterialMenu(); default: - throw 'Not supposed to be invoked for $defaultTargetPlatform'; + throw UnsupportedError( + 'Not supposed to be invoked for $defaultTargetPlatform', + ); } } diff --git a/flutter_quill_extensions/lib/presentation/models/config/editor/image/image.dart b/flutter_quill_extensions/lib/presentation/models/config/editor/image/image.dart index 518b61ed..ed7e5abe 100644 --- a/flutter_quill_extensions/lib/presentation/models/config/editor/image/image.dart +++ b/flutter_quill_extensions/lib/presentation/models/config/editor/image/image.dart @@ -1,5 +1,6 @@ import 'dart:io' show File; +import 'package:flutter/foundation.dart' show VoidCallback; import 'package:flutter_quill/extensions.dart'; import 'package:meta/meta.dart' show immutable; @@ -12,12 +13,11 @@ import '../../../../embeds/embed_types/image.dart'; @immutable class QuillEditorImageEmbedConfigurations { const QuillEditorImageEmbedConfigurations({ - @Deprecated('This will be deleted in 0.7.0 as we will have one menu') - this.forceUseMobileOptionMenuForImageClick = false, ImageEmbedBuilderOnRemovedCallback? onImageRemovedCallback, this.shouldRemoveImageCallback, this.imageProviderBuilder, this.imageErrorWidgetBuilder, + this.onImageClicked, }) : _onImageRemovedCallback = onImageRemovedCallback; /// [onImageRemovedCallback] is called when an image is @@ -101,15 +101,7 @@ class QuillEditorImageEmbedConfigurations { /// final ImageEmbedBuilderErrorWidgetBuilder? imageErrorWidgetBuilder; - /// [forceUseMobileOptionMenuForImageClick] is a boolean - /// flag that, when set to `true`, - /// enforces the use of the mobile-specific option menu for image clicks in - /// other platforms like desktop, this option doesn't affect mobile. it will - /// not affect web - /// This option - /// can be used to override the default behavior based on the platform. - /// - final bool forceUseMobileOptionMenuForImageClick; + final VoidCallback? onImageClicked; static ImageEmbedBuilderOnRemovedCallback get defaultOnImageRemovedCallback { return (imageUrl) async { @@ -162,10 +154,6 @@ class QuillEditorImageEmbedConfigurations { imageProviderBuilder: imageProviderBuilder ?? this.imageProviderBuilder, imageErrorWidgetBuilder: imageErrorWidgetBuilder ?? this.imageErrorWidgetBuilder, - // ignore: deprecated_member_use_from_same_package - forceUseMobileOptionMenuForImageClick: - forceUseMobileOptionMenuForImageClick ?? - this.forceUseMobileOptionMenuForImageClick, ); } } diff --git a/flutter_quill_extensions/pubspec.yaml b/flutter_quill_extensions/pubspec.yaml index 3df390cd..548e27d1 100644 --- a/flutter_quill_extensions/pubspec.yaml +++ b/flutter_quill_extensions/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_quill_extensions description: Embed extensions for flutter_quill including image, video, formula and etc. -version: 0.6.5 +version: 0.6.6 homepage: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_extensions repository: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_extensions diff --git a/lib/flutter_quill.dart b/lib/flutter_quill.dart index f70653ca..07770bbe 100644 --- a/lib/flutter_quill.dart +++ b/lib/flutter_quill.dart @@ -1,6 +1,7 @@ library flutter_quill; export 'src/models/config/quill_configurations.dart'; +export 'src/models/config/raw_editor/configurations.dart'; export 'src/models/config/toolbar/base_configurations.dart'; export 'src/models/documents/attribute.dart'; export 'src/models/documents/document.dart'; @@ -22,6 +23,7 @@ export 'src/models/themes/quill_icon_theme.dart'; export 'src/utils/embeds.dart'; export 'src/utils/extensions/build_context.dart'; export 'src/widgets/controller.dart'; +export 'src/widgets/cursor.dart'; export 'src/widgets/default_styles.dart'; export 'src/widgets/editor/editor.dart'; export 'src/widgets/embeds.dart'; diff --git a/lib/src/models/config/editor/configurations.dart b/lib/src/models/config/editor/configurations.dart index 117e8dc2..c6e0f5de 100644 --- a/lib/src/models/config/editor/configurations.dart +++ b/lib/src/models/config/editor/configurations.dart @@ -2,7 +2,7 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/foundation.dart' show Brightness, Uint8List, immutable; import 'package:flutter/material.dart' - show TextCapitalization, TextSelectionThemeData; + show TextCapitalization, TextInputAction, TextSelectionThemeData; import 'package:flutter/widgets.dart'; import 'package:meta/meta.dart' show experimental; @@ -71,6 +71,7 @@ class QuillEditorConfigurations extends Equatable { this.elementOptions = const QuillEditorElementOptions(), this.builder, this.magnifierConfiguration, + this.textInputAction = TextInputAction.newline, }); /// The text placeholder in the quill editor @@ -316,6 +317,9 @@ class QuillEditorConfigurations extends Equatable { @experimental final TextMagnifierConfiguration? magnifierConfiguration; + /// Default to [TextInputAction.newline] + final TextInputAction textInputAction; + @override List<Object?> get props => [ placeholder, @@ -369,6 +373,7 @@ class QuillEditorConfigurations extends Equatable { QuillEditorElementOptions? elementOptions, QuillEditorBuilder? builder, TextMagnifierConfiguration? magnifierConfiguration, + TextInputAction? textInputAction, }) { return QuillEditorConfigurations( placeholder: placeholder ?? this.placeholder, @@ -425,6 +430,7 @@ class QuillEditorConfigurations extends Equatable { builder: builder ?? this.builder, magnifierConfiguration: magnifierConfiguration ?? this.magnifierConfiguration, + textInputAction: textInputAction ?? this.textInputAction, ); } } diff --git a/lib/src/models/config/raw_editor/configurations.dart b/lib/src/models/config/raw_editor/configurations.dart new file mode 100644 index 00000000..b7bdebe3 --- /dev/null +++ b/lib/src/models/config/raw_editor/configurations.dart @@ -0,0 +1,290 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter/foundation.dart' show Brightness, Uint8List; +import 'package:flutter/material.dart' + show + AdaptiveTextSelectionToolbar, + PointerDownEvent, + TextCapitalization, + TextInputAction; +import 'package:flutter/widgets.dart' + show + Action, + BuildContext, + Color, + ContentInsertionConfiguration, + EdgeInsets, + EdgeInsetsGeometry, + FocusNode, + Intent, + ScrollController, + ScrollPhysics, + ShortcutActivator, + TextFieldTapRegion, + TextSelectionControls, + ValueChanged, + Widget; +import 'package:meta/meta.dart' show immutable; + +import '../../../widgets/controller.dart'; +import '../../../widgets/cursor.dart'; +import '../../../widgets/default_styles.dart'; +import '../../../widgets/delegate.dart'; +import '../../../widgets/link.dart'; +import '../../../widgets/raw_editor/raw_editor.dart'; +import '../../../widgets/raw_editor/raw_editor_state.dart'; +import '../../themes/quill_dialog_theme.dart'; + +@immutable +class QuillRawEditorConfigurations extends Equatable { + const QuillRawEditorConfigurations({ + required this.controller, + required this.focusNode, + required this.scrollController, + required this.scrollBottomInset, + required this.cursorStyle, + required this.selectionColor, + required this.selectionCtrls, + required this.embedBuilder, + required this.autoFocus, + this.showCursor = true, + this.scrollable = true, + this.padding = EdgeInsets.zero, + this.isReadOnly = false, + this.placeholder, + this.onLaunchUrl, + this.contextMenuBuilder = defaultContextMenuBuilder, + this.showSelectionHandles = false, + this.textCapitalization = TextCapitalization.none, + this.maxHeight, + this.minHeight, + this.maxContentWidth, + this.customStyles, + this.customShortcuts, + this.customActions, + this.expands = false, + this.isOnTapOutsideEnabled = true, + this.onTapOutside, + this.keyboardAppearance = Brightness.light, + this.enableInteractiveSelection = true, + this.scrollPhysics, + this.linkActionPickerDelegate = defaultLinkActionPickerDelegate, + this.customStyleBuilder, + this.customRecognizerBuilder, + this.floatingCursorDisabled = false, + this.onImagePaste, + this.customLinkPrefixes = const <String>[], + this.dialogTheme, + this.contentInsertionConfiguration, + this.textInputAction = TextInputAction.newline, + this.requestKeyboardFocusOnCheckListChanged = false, + }); + + /// Controls the document being edited. + final QuillController controller; + + /// Controls whether this editor has keyboard focus. + final FocusNode focusNode; + final ScrollController scrollController; + final bool scrollable; + final double scrollBottomInset; + + /// Additional space around the editor contents. + final EdgeInsetsGeometry padding; + + /// Whether the text can be changed. + /// + /// When this is set to true, the text cannot be modified + /// by any shortcut or keyboard operation. The text is still selectable. + /// + /// Defaults to false. Must not be null. + final bool isReadOnly; + + final String? placeholder; + + /// Callback which is triggered when the user wants to open a URL from + /// a link in the document. + final ValueChanged<String>? onLaunchUrl; + + /// Builds the text selection toolbar when requested by the user. + /// + /// See also: + /// * [EditableText.contextMenuBuilder], which builds the default + /// text selection toolbar for [EditableText]. + /// + /// If not provided, no context menu will be shown. + final QuillEditorContextMenuBuilder? contextMenuBuilder; + + static Widget defaultContextMenuBuilder( + BuildContext context, + QuillRawEditorState state, + ) { + return TextFieldTapRegion( + child: AdaptiveTextSelectionToolbar.buttonItems( + buttonItems: state.contextMenuButtonItems, + anchors: state.contextMenuAnchors, + ), + ); + } + + /// Whether to show selection handles. + /// + /// When a selection is active, there will be two handles at each side of + /// boundary, or one handle if the selection is collapsed. The handles can be + /// dragged to adjust the selection. + /// + /// See also: + /// + /// * [showCursor], which controls the visibility of the cursor. + final bool showSelectionHandles; + + /// Whether to show cursor. + /// + /// The cursor refers to the blinking caret when the editor is focused. + /// + /// See also: + /// + /// * [cursorStyle], which controls the cursor visual representation. + /// * [showSelectionHandles], which controls the visibility of the selection + /// handles. + final bool showCursor; + + /// The style to be used for the editing cursor. + final CursorStyle cursorStyle; + + /// Configures how the platform keyboard will select an uppercase or + /// lowercase keyboard. + /// + /// Only supports text keyboards, other keyboard types will ignore this + /// configuration. Capitalization is locale-aware. + /// + /// Defaults to [TextCapitalization.none]. Must not be null. + /// + /// See also: + /// + /// * [TextCapitalization], for a description of each capitalization behavior + final TextCapitalization textCapitalization; + + /// The maximum height this editor can have. + /// + /// If this is null then there is no limit to the editor's height and it will + /// expand to fill its parent. + final double? maxHeight; + + /// The minimum height this editor can have. + final double? minHeight; + + /// The maximum width to be occupied by the content of this editor. + /// + /// If this is not null and and this editor's width is larger than this value + /// then the contents will be constrained to the provided maximum width and + /// horizontally centered. This is mostly useful on devices with wide screens. + final double? maxContentWidth; + + /// Allows to override [DefaultStyles]. + final DefaultStyles? customStyles; + + /// Whether this widget's height will be sized to fill its parent. + /// + /// If set to true and wrapped in a parent widget like [Expanded] or + /// + /// Defaults to false. + final bool expands; + + /// Whether this editor should focus itself if nothing else is already + /// focused. + /// + /// If true, the keyboard will open as soon as this text field obtains focus. + /// Otherwise, the keyboard is only shown after the user taps the text field. + /// + /// Defaults to false. Cannot be null. + final bool autoFocus; + + /// The color to use when painting the selection. + final Color selectionColor; + + /// Delegate for building the text selection handles and toolbar. + /// + /// The [QuillRawEditor] widget used on its own will not trigger the display + /// of the selection toolbar by itself. The toolbar is shown by calling + /// [QuillRawEditorState.showToolbar] in response to + /// an appropriate user event. + final TextSelectionControls selectionCtrls; + + /// The appearance of the keyboard. + /// + /// This setting is only honored on iOS devices. + /// + /// Defaults to [Brightness.light]. + final Brightness keyboardAppearance; + + /// If true, then long-pressing this TextField will select text and show the + /// cut/copy/paste menu, and tapping will move the text caret. + /// + /// True by default. + /// + /// If false, most of the accessibility support for selecting text, copy + /// and paste, and moving the caret will be disabled. + final bool enableInteractiveSelection; + + bool get selectionEnabled => enableInteractiveSelection; + + /// The [ScrollPhysics] to use when vertically scrolling the input. + /// + /// If not specified, it will behave according to the current platform. + /// + /// See [Scrollable.physics]. + final ScrollPhysics? scrollPhysics; + + final Future<String?> Function(Uint8List imageBytes)? onImagePaste; + + /// Contains user-defined shortcuts map. + /// + /// [https://docs.flutter.dev/development/ui/advanced/actions-and-shortcuts#shortcuts] + final Map<ShortcutActivator, Intent>? customShortcuts; + + /// Contains user-defined actions. + /// + /// [https://docs.flutter.dev/development/ui/advanced/actions-and-shortcuts#actions] + final Map<Type, Action<Intent>>? customActions; + + /// Builder function for embeddable objects. + final EmbedsBuilder embedBuilder; + final LinkActionPickerDelegate linkActionPickerDelegate; + final CustomStyleBuilder? customStyleBuilder; + final CustomRecognizerBuilder? customRecognizerBuilder; + final bool floatingCursorDisabled; + final List<String> customLinkPrefixes; + + /// Configures the dialog theme. + final QuillDialogTheme? dialogTheme; + + /// Configuration of handler for media content inserted via the system input + /// method. + /// + /// See [https://api.flutter.dev/flutter/widgets/EditableText/contentInsertionConfiguration.html] + final ContentInsertionConfiguration? contentInsertionConfiguration; + + /// Whether the [onTapOutside] should be triggered or not + /// Defaults to `true` + /// it have default implementation, check [onTapOuside] for more + final bool isOnTapOutsideEnabled; + + /// This will run only when [isOnTapOutsideEnabled] is true + /// by default on desktop and web it will unfocus + /// on mobile it will only unFocus if the kind property of + /// event [PointerDownEvent] is [PointerDeviceKind.unknown] + /// you can override this to fit your needs + final Function(PointerDownEvent event, FocusNode focusNode)? onTapOutside; + + /// When there is a change the check list values + /// should we request keyboard focus?? + final bool requestKeyboardFocusOnCheckListChanged; + + final TextInputAction textInputAction; + + @override + List<Object?> get props => [ + isReadOnly, + placeholder, + ]; +} diff --git a/lib/src/utils/extensions/build_context.dart b/lib/src/utils/extensions/build_context.dart index 74d34292..2786b2fa 100644 --- a/lib/src/utils/extensions/build_context.dart +++ b/lib/src/utils/extensions/build_context.dart @@ -2,7 +2,7 @@ import 'package:flutter/widgets.dart' show BuildContext; import '../../../flutter_quill.dart'; -// TODO: The comments of this file might needs to be updated. +// TODO: The comments of this file is outdated and needs to be updated /// Public shared extension extension BuildContextExt on BuildContext { diff --git a/lib/src/utils/string.dart b/lib/src/utils/string.dart index 2975cf60..7473eed8 100644 --- a/lib/src/utils/string.dart +++ b/lib/src/utils/string.dart @@ -20,13 +20,13 @@ Map<String, String> parseKeyValuePairs(String s, Set<String> targetKeys) { } String replaceStyleStringWithSize( - String s, { + String cssStyle, { required double width, required double height, required bool isMobile, }) { final result = <String, String>{}; - final pairs = s.split(';'); + final pairs = cssStyle.split(';'); for (final pair in pairs) { final index = pair.indexOf(':'); if (index < 0) { diff --git a/lib/src/widgets/editor/editor.dart b/lib/src/widgets/editor/editor.dart index dea30332..52d39528 100644 --- a/lib/src/widgets/editor/editor.dart +++ b/lib/src/widgets/editor/editor.dart @@ -13,7 +13,6 @@ import '../../../flutter_quill.dart'; import '../../models/documents/nodes/container.dart' as container_node; import '../../utils/platform.dart'; import '../box.dart'; -import '../cursor.dart'; import '../delegate.dart'; import '../float_cursor.dart'; import '../text_selection.dart'; @@ -247,60 +246,64 @@ class QuillEditorState extends State<QuillEditor> 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( - platform: theme.platform, - supportWeb: true, + configurations: QuillRawEditorConfigurations( + controller: context.requireQuillController, + focusNode: widget.focusNode, + scrollController: widget.scrollController, + scrollable: configurations.scrollable, + scrollBottomInset: configurations.scrollBottomInset, + padding: configurations.padding, + isReadOnly: configurations.readOnly, + placeholder: configurations.placeholder, + onLaunchUrl: configurations.onLaunchUrl, + contextMenuBuilder: showSelectionToolbar + ? (configurations.contextMenuBuilder ?? + QuillRawEditorConfigurations.defaultContextMenuBuilder) + : null, + showSelectionHandles: isMobile( + platform: theme.platform, + supportWeb: true, + ), + showCursor: configurations.showCursor ?? true, + 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, + isOnTapOutsideEnabled: configurations.isOnTapOutsideEnabled, + onTapOutside: configurations.onTapOutside, + dialogTheme: configurations.dialogTheme, + contentInsertionConfiguration: + configurations.contentInsertionConfiguration, ), - 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, ), ), ); @@ -435,15 +438,15 @@ class _QuillEditorSelectionGestureDetectorBuilder return false; } final pos = renderEditor!.getPositionForOffset(details.globalPosition); - final result = - editor!.widget.controller.document.querySegmentLeafNode(pos.offset); + final result = editor!.widget.configurations.controller.document + .querySegmentLeafNode(pos.offset); final line = result.line; if (line == null) { return false; } final segmentLeaf = result.leaf; if (segmentLeaf == null && line.length == 1) { - editor!.widget.controller.updateSelection( + editor!.widget.configurations.controller.updateSelection( TextSelection.collapsed(offset: pos.offset), ChangeSource.local, ); diff --git a/lib/src/widgets/raw_editor/raw_editor.dart b/lib/src/widgets/raw_editor/raw_editor.dart index 55b9a590..9514651b 100644 --- a/lib/src/widgets/raw_editor/raw_editor.dart +++ b/lib/src/widgets/raw_editor/raw_editor.dart @@ -1,245 +1,25 @@ import 'package:flutter/material.dart'; -import 'package:flutter/services.dart' show Uint8List; -import '../../models/themes/quill_dialog_theme.dart'; -import '../controller.dart'; -import '../cursor.dart'; -import '../default_styles.dart'; -import '../delegate.dart'; -import '../link.dart'; +import '../../models/config/raw_editor/configurations.dart'; import 'raw_editor_state.dart'; class QuillRawEditor extends StatefulWidget { - const QuillRawEditor({ - required this.controller, - required this.focusNode, - required this.scrollController, - required this.scrollBottomInset, - required this.cursorStyle, - required this.selectionColor, - required this.selectionCtrls, - required this.embedBuilder, - required this.autoFocus, + QuillRawEditor({ + required this.configurations, super.key, - this.scrollable = true, - this.padding = EdgeInsets.zero, - this.readOnly = false, - this.placeholder, - this.onLaunchUrl, - this.contextMenuBuilder = defaultContextMenuBuilder, - this.showSelectionHandles = false, - bool? showCursor, - this.textCapitalization = TextCapitalization.none, - this.maxHeight, - this.minHeight, - this.maxContentWidth, - this.customStyles, - this.customShortcuts, - this.customActions, - this.expands = false, - this.enableUnfocusOnTapOutside = true, - this.keyboardAppearance = Brightness.light, - this.enableInteractiveSelection = true, - this.scrollPhysics, - this.linkActionPickerDelegate = defaultLinkActionPickerDelegate, - this.customStyleBuilder, - this.customRecognizerBuilder, - this.floatingCursorDisabled = false, - this.onImagePaste, - this.customLinkPrefixes = const <String>[], - this.dialogTheme, - this.contentInsertionConfiguration, - }) : assert(maxHeight == null || maxHeight > 0, 'maxHeight cannot be null'), - assert(minHeight == null || minHeight >= 0, 'minHeight cannot be null'), - assert(maxHeight == null || minHeight == null || maxHeight >= minHeight, + }) : assert( + configurations.maxHeight == null || configurations.maxHeight! > 0, 'maxHeight cannot be null'), - showCursor = showCursor ?? true; - - /// Controls the document being edited. - final QuillController controller; - - /// Controls whether this editor has keyboard focus. - final FocusNode focusNode; - final ScrollController scrollController; - final bool scrollable; - final double scrollBottomInset; - final bool enableUnfocusOnTapOutside; - - /// Additional space around the editor contents. - final EdgeInsetsGeometry padding; - - /// Whether the text can be changed. - /// - /// When this is set to true, the text cannot be modified - /// by any shortcut or keyboard operation. The text is still selectable. - /// - /// Defaults to false. Must not be null. - final bool readOnly; - - final String? placeholder; - - /// Callback which is triggered when the user wants to open a URL from - /// a link in the document. - final ValueChanged<String>? onLaunchUrl; - - /// Builds the text selection toolbar when requested by the user. - /// - /// See also: - /// * [EditableText.contextMenuBuilder], which builds the default - /// text selection toolbar for [EditableText]. - /// - /// If not provided, no context menu will be shown. - final QuillEditorContextMenuBuilder? contextMenuBuilder; - - static Widget defaultContextMenuBuilder( - BuildContext context, - QuillRawEditorState state, - ) { - return TextFieldTapRegion( - child: AdaptiveTextSelectionToolbar.buttonItems( - buttonItems: state.contextMenuButtonItems, - anchors: state.contextMenuAnchors, - ), - ); - } - - /// Whether to show selection handles. - /// - /// When a selection is active, there will be two handles at each side of - /// boundary, or one handle if the selection is collapsed. The handles can be - /// dragged to adjust the selection. - /// - /// See also: - /// - /// * [showCursor], which controls the visibility of the cursor. - final bool showSelectionHandles; - - /// Whether to show cursor. - /// - /// The cursor refers to the blinking caret when the editor is focused. - /// - /// See also: - /// - /// * [cursorStyle], which controls the cursor visual representation. - /// * [showSelectionHandles], which controls the visibility of the selection - /// handles. - final bool showCursor; - - /// The style to be used for the editing cursor. - final CursorStyle cursorStyle; - - /// Configures how the platform keyboard will select an uppercase or - /// lowercase keyboard. - /// - /// Only supports text keyboards, other keyboard types will ignore this - /// configuration. Capitalization is locale-aware. - /// - /// Defaults to [TextCapitalization.none]. Must not be null. - /// - /// See also: - /// - /// * [TextCapitalization], for a description of each capitalization behavior - final TextCapitalization textCapitalization; - - /// The maximum height this editor can have. - /// - /// If this is null then there is no limit to the editor's height and it will - /// expand to fill its parent. - final double? maxHeight; - - /// The minimum height this editor can have. - final double? minHeight; - - /// The maximum width to be occupied by the content of this editor. - /// - /// If this is not null and and this editor's width is larger than this value - /// then the contents will be constrained to the provided maximum width and - /// horizontally centered. This is mostly useful on devices with wide screens. - final double? maxContentWidth; - - /// Allows to override [DefaultStyles]. - final DefaultStyles? customStyles; - - /// Whether this widget's height will be sized to fill its parent. - /// - /// If set to true and wrapped in a parent widget like [Expanded] or - /// - /// Defaults to false. - final bool expands; - - /// Whether this editor should focus itself if nothing else is already - /// focused. - /// - /// If true, the keyboard will open as soon as this text field obtains focus. - /// Otherwise, the keyboard is only shown after the user taps the text field. - /// - /// Defaults to false. Cannot be null. - final bool autoFocus; - - /// The color to use when painting the selection. - final Color selectionColor; - - /// Delegate for building the text selection handles and toolbar. - /// - /// The [QuillRawEditor] widget used on its own will not trigger the display - /// of the selection toolbar by itself. The toolbar is shown by calling - /// [QuillRawEditorState.showToolbar] in response to - /// an appropriate user event. - final TextSelectionControls selectionCtrls; - - /// The appearance of the keyboard. - /// - /// This setting is only honored on iOS devices. - /// - /// Defaults to [Brightness.light]. - final Brightness keyboardAppearance; - - /// If true, then long-pressing this TextField will select text and show the - /// cut/copy/paste menu, and tapping will move the text caret. - /// - /// True by default. - /// - /// If false, most of the accessibility support for selecting text, copy - /// and paste, and moving the caret will be disabled. - final bool enableInteractiveSelection; - - bool get selectionEnabled => enableInteractiveSelection; - - /// The [ScrollPhysics] to use when vertically scrolling the input. - /// - /// If not specified, it will behave according to the current platform. - /// - /// See [Scrollable.physics]. - final ScrollPhysics? scrollPhysics; - - final Future<String?> Function(Uint8List imageBytes)? onImagePaste; - - /// Contains user-defined shortcuts map. - /// - /// [https://docs.flutter.dev/development/ui/advanced/actions-and-shortcuts#shortcuts] - final Map<ShortcutActivator, Intent>? customShortcuts; - - /// Contains user-defined actions. - /// - /// [https://docs.flutter.dev/development/ui/advanced/actions-and-shortcuts#actions] - final Map<Type, Action<Intent>>? customActions; - - /// Builder function for embeddable objects. - final EmbedsBuilder embedBuilder; - final LinkActionPickerDelegate linkActionPickerDelegate; - final CustomStyleBuilder? customStyleBuilder; - final CustomRecognizerBuilder? customRecognizerBuilder; - final bool floatingCursorDisabled; - final List<String> customLinkPrefixes; - - /// Configures the dialog theme. - final QuillDialogTheme? dialogTheme; - - /// Configuration of handler for media content inserted via the system input - /// method. - /// - /// See [https://api.flutter.dev/flutter/widgets/EditableText/contentInsertionConfiguration.html] - final ContentInsertionConfiguration? contentInsertionConfiguration; + assert( + configurations.minHeight == null || configurations.minHeight! >= 0, + 'minHeight cannot be null'), + assert( + configurations.maxHeight == null || + configurations.minHeight == null || + configurations.maxHeight! >= configurations.minHeight!, + 'maxHeight cannot be null'); + + final QuillRawEditorConfigurations configurations; @override State<StatefulWidget> createState() => QuillRawEditorState(); diff --git a/lib/src/widgets/raw_editor/raw_editor_actions.dart b/lib/src/widgets/raw_editor/raw_editor_actions.dart index e0564d3f..54a7eba7 100644 --- a/lib/src/widgets/raw_editor/raw_editor_actions.dart +++ b/lib/src/widgets/raw_editor/raw_editor_actions.dart @@ -76,7 +76,8 @@ class QuillEditorDeleteTextAction<T extends DirectionalTextEditingIntent> @override bool get isActionEnabled => - !state.widget.readOnly && state.textEditingValue.selection.isValid; + !state.widget.configurations.isReadOnly && + state.textEditingValue.selection.isValid; } class QuillEditorUpdateTextSelectionAction< @@ -93,8 +94,8 @@ class QuillEditorUpdateTextSelectionAction< final selection = state.textEditingValue.selection; assert(selection.isValid); - final collapseSelection = - intent.collapseSelection || !state.widget.selectionEnabled; + final collapseSelection = intent.collapseSelection || + !state.widget.configurations.selectionEnabled; // Collapse to the logical start/end. TextSelection collapse(TextSelection selection) { assert(selection.isValid); @@ -217,7 +218,8 @@ class QuillEditorExtendSelectionOrCaretPositionAction extends ContextAction< @override bool get isActionEnabled => - state.widget.selectionEnabled && state.textEditingValue.selection.isValid; + state.widget.configurations.selectionEnabled && + state.textEditingValue.selection.isValid; } class QuillEditorUpdateTextSelectionToAdjacentLineAction< @@ -251,8 +253,8 @@ class QuillEditorUpdateTextSelectionToAdjacentLineAction< void invoke(T intent, [BuildContext? context]) { assert(state.textEditingValue.selection.isValid); - final collapseSelection = - intent.collapseSelection || !state.widget.selectionEnabled; + final collapseSelection = intent.collapseSelection || + !state.widget.configurations.selectionEnabled; final value = state.textEditingValue; if (!value.selection.isValid) { return; @@ -307,7 +309,7 @@ class QuillEditorSelectAllAction extends ContextAction<SelectAllTextIntent> { } @override - bool get isActionEnabled => state.widget.selectionEnabled; + bool get isActionEnabled => state.widget.configurations.selectionEnabled; } class QuillEditorCopySelectionAction @@ -559,7 +561,7 @@ class QuillEditorApplyLinkAction extends Action<QuillEditorApplyLinkIntent> { return LinkStyleDialog( text: initialTextLink.text, link: initialTextLink.link, - dialogTheme: state.widget.dialogTheme, + dialogTheme: state.widget.configurations.dialogTheme, ); }, ); diff --git a/lib/src/widgets/raw_editor/raw_editor_state.dart b/lib/src/widgets/raw_editor/raw_editor_state.dart index 0775686c..0c6d6ee6 100644 --- a/lib/src/widgets/raw_editor/raw_editor_state.dart +++ b/lib/src/widgets/raw_editor/raw_editor_state.dart @@ -32,7 +32,6 @@ import '../../models/structs/vertical_spacing.dart'; import '../../utils/cast.dart'; import '../../utils/delta.dart'; import '../../utils/embeds.dart'; -import '../../utils/extensions/build_context.dart'; import '../../utils/platform.dart'; import '../controller.dart'; import '../cursor.dart'; @@ -77,12 +76,12 @@ class QuillRawEditorState extends EditorState // Cursors late CursorCont _cursorCont; - QuillController get controller => widget.controller; + QuillController get controller => widget.configurations.controller; // Focus bool _didAutoFocus = false; - bool get _hasFocus => widget.focusNode.hasFocus; + bool get _hasFocus => widget.configurations.focusNode.hasFocus; // Theme DefaultStyles? _styles; @@ -109,10 +108,11 @@ class QuillRawEditorState extends EditorState @override void insertContent(KeyboardInsertedContent content) { - assert(widget.contentInsertionConfiguration?.allowedMimeTypes + assert(widget.configurations.contentInsertionConfiguration?.allowedMimeTypes .contains(content.mimeType) ?? false); - widget.contentInsertionConfiguration?.onContentInserted.call(content); + widget.configurations.contentInsertionConfiguration?.onContentInserted + .call(content); } /// Returns the [ContextMenuButtonItem]s representing the buttons in this @@ -200,7 +200,7 @@ class QuillRawEditorState extends EditorState case ui.PointerDeviceKind.stylus: case ui.PointerDeviceKind.invertedStylus: case ui.PointerDeviceKind.unknown: - widget.focusNode.unfocus(); + widget.configurations.focusNode.unfocus(); break; case ui.PointerDeviceKind.trackpad: throw UnimplementedError( @@ -211,7 +211,7 @@ class QuillRawEditorState extends EditorState case TargetPlatform.linux: case TargetPlatform.macOS: case TargetPlatform.windows: - widget.focusNode.unfocus(); + widget.configurations.focusNode.unfocus(); break; default: throw UnsupportedError( @@ -227,8 +227,8 @@ class QuillRawEditorState extends EditorState super.build(context); var doc = controller.document; - if (doc.isEmpty() && widget.placeholder != null) { - final raw = widget.placeholder?.replaceAll(r'"', '\\"'); + if (doc.isEmpty() && widget.configurations.placeholder != null) { + final raw = widget.configurations.placeholder?.replaceAll(r'"', '\\"'); doc = Document.fromJson( jsonDecode( '[{"attributes":{"placeholder":true},"insert":"$raw\\n"}]', @@ -246,24 +246,25 @@ class QuillRawEditorState extends EditorState document: doc, selection: controller.selection, hasFocus: _hasFocus, - scrollable: widget.scrollable, + scrollable: widget.configurations.scrollable, cursorController: _cursorCont, textDirection: _textDirection, startHandleLayerLink: _startHandleLayerLink, endHandleLayerLink: _endHandleLayerLink, onSelectionChanged: _handleSelectionChanged, onSelectionCompleted: _handleSelectionCompleted, - scrollBottomInset: widget.scrollBottomInset, - padding: widget.padding, - maxContentWidth: widget.maxContentWidth, - floatingCursorDisabled: widget.floatingCursorDisabled, + scrollBottomInset: widget.configurations.scrollBottomInset, + padding: widget.configurations.padding, + maxContentWidth: widget.configurations.maxContentWidth, + floatingCursorDisabled: + widget.configurations.floatingCursorDisabled, children: _buildChildren(doc, context), ), ), ), ); - if (widget.scrollable) { + if (widget.configurations.scrollable) { /// Since [SingleChildScrollView] does not implement /// `computeDistanceToActualBaseline` it prevents the editor from /// providing its baseline metrics. To address this issue we wrap @@ -277,7 +278,7 @@ class QuillRawEditorState extends EditorState padding: baselinePadding, child: QuillSingleChildScrollView( controller: _scrollController, - physics: widget.scrollPhysics, + physics: widget.configurations.scrollPhysics, viewportBuilder: (_, offset) => CompositedTransformTarget( link: _toolbarLayerLink, child: MouseRegion( @@ -288,17 +289,18 @@ class QuillRawEditorState extends EditorState document: doc, selection: controller.selection, hasFocus: _hasFocus, - scrollable: widget.scrollable, + scrollable: widget.configurations.scrollable, textDirection: _textDirection, startHandleLayerLink: _startHandleLayerLink, endHandleLayerLink: _endHandleLayerLink, onSelectionChanged: _handleSelectionChanged, onSelectionCompleted: _handleSelectionCompleted, - scrollBottomInset: widget.scrollBottomInset, - padding: widget.padding, - maxContentWidth: widget.maxContentWidth, + scrollBottomInset: widget.configurations.scrollBottomInset, + padding: widget.configurations.padding, + maxContentWidth: widget.configurations.maxContentWidth, cursorController: _cursorCont, - floatingCursorDisabled: widget.floatingCursorDisabled, + floatingCursorDisabled: + widget.configurations.floatingCursorDisabled, children: _buildChildren(doc, context), ), ), @@ -307,11 +309,11 @@ class QuillRawEditorState extends EditorState ); } - final constraints = widget.expands + final constraints = widget.configurations.expands ? const BoxConstraints.expand() : BoxConstraints( - minHeight: widget.minHeight ?? 0.0, - maxHeight: widget.maxHeight ?? double.infinity, + minHeight: widget.configurations.minHeight ?? 0.0, + maxHeight: widget.configurations.maxHeight ?? double.infinity, ); // Please notice that this change will make the check fixed @@ -321,13 +323,11 @@ class QuillRawEditorState extends EditorState final isDesktopMacOS = isMacOS(supportWeb: true); return TextFieldTapRegion( - enabled: widget.enableUnfocusOnTapOutside, + enabled: widget.configurations.isOnTapOutsideEnabled, onTapOutside: (event) { - final onTapOutside = - context.requireQuillEditorConfigurations.onTapOutside; + final onTapOutside = widget.configurations.onTapOutside; if (onTapOutside != null) { - context.requireQuillEditorConfigurations.onTapOutside - ?.call(event, widget.focusNode); + onTapOutside.call(event, widget.configurations.focusNode); return; } _defaultOnTapOutside(event); @@ -463,14 +463,14 @@ class QuillRawEditorState extends EditorState meta: isDesktopMacOS, ): const OpenSearchIntent(), }, { - ...?widget.customShortcuts + ...?widget.configurations.customShortcuts }), child: Actions( actions: mergeMaps<Type, Action<Intent>>(_actions, { - ...?widget.customActions, + ...?widget.configurations.customActions, }), child: Focus( - focusNode: widget.focusNode, + focusNode: widget.configurations.focusNode, onKey: _onKey, child: QuillKeyboardListener( child: Container( @@ -549,7 +549,7 @@ class QuillRawEditorState extends EditorState controller.document.queryChild(controller.selection.baseOffset); KeyEventResult insertTabCharacter() { - if (widget.readOnly) { + if (widget.configurations.isReadOnly) { return KeyEventResult.ignored; } controller.replaceText(controller.selection.baseOffset, 0, '\t', null); @@ -657,10 +657,9 @@ class QuillRawEditorState extends EditorState /// Updates the checkbox positioned at [offset] in document /// by changing its attribute according to [value]. void _handleCheckboxTap(int offset, bool value) { - final requestKeyboardFocusOnCheckListChanged = context - .requireQuillEditorConfigurations - .requestKeyboardFocusOnCheckListChanged; - if (!widget.readOnly) { + final requestKeyboardFocusOnCheckListChanged = + widget.configurations.requestKeyboardFocusOnCheckListChanged; + if (!widget.configurations.isReadOnly) { _disableScrollControllerAnimateOnce = true; final currentSelection = controller.selection.copyWith(); final attribute = value ? Attribute.checked : Attribute.unchecked; @@ -717,26 +716,27 @@ class QuillRawEditorState extends EditorState block: node, controller: controller, textDirection: getDirectionOfNode(node), - scrollBottomInset: widget.scrollBottomInset, + scrollBottomInset: widget.configurations.scrollBottomInset, verticalSpacing: _getVerticalSpacingForBlock(node, _styles), textSelection: controller.selection, - color: widget.selectionColor, + color: widget.configurations.selectionColor, styles: _styles, - enableInteractiveSelection: widget.enableInteractiveSelection, + enableInteractiveSelection: + widget.configurations.enableInteractiveSelection, hasFocus: _hasFocus, contentPadding: attrs.containsKey(Attribute.codeBlock.key) ? const EdgeInsets.all(16) : null, - embedBuilder: widget.embedBuilder, + embedBuilder: widget.configurations.embedBuilder, linkActionPicker: _linkActionPicker, - onLaunchUrl: widget.onLaunchUrl, + onLaunchUrl: widget.configurations.onLaunchUrl, cursorCont: _cursorCont, indentLevelCounts: indentLevelCounts, clearIndents: clearIndents, onCheckboxTap: _handleCheckboxTap, - readOnly: widget.readOnly, - customStyleBuilder: widget.customStyleBuilder, - customLinkPrefixes: widget.customLinkPrefixes, + readOnly: widget.configurations.isReadOnly, + customStyleBuilder: widget.configurations.customStyleBuilder, + customLinkPrefixes: widget.configurations.customLinkPrefixes, ); result.add( Directionality( @@ -760,15 +760,15 @@ class QuillRawEditorState extends EditorState final textLine = TextLine( line: node, textDirection: _textDirection, - embedBuilder: widget.embedBuilder, - customStyleBuilder: widget.customStyleBuilder, - customRecognizerBuilder: widget.customRecognizerBuilder, + embedBuilder: widget.configurations.embedBuilder, + customStyleBuilder: widget.configurations.customStyleBuilder, + customRecognizerBuilder: widget.configurations.customRecognizerBuilder, styles: _styles!, - readOnly: widget.readOnly, + readOnly: widget.configurations.isReadOnly, controller: controller, linkActionPicker: _linkActionPicker, - onLaunchUrl: widget.onLaunchUrl, - customLinkPrefixes: widget.customLinkPrefixes, + onLaunchUrl: widget.configurations.onLaunchUrl, + customLinkPrefixes: widget.configurations.customLinkPrefixes, ); final editableTextLine = EditableTextLine( node, @@ -778,8 +778,8 @@ class QuillRawEditorState extends EditorState _getVerticalSpacingForLine(node, _styles), _textDirection, controller.selection, - widget.selectionColor, - widget.enableInteractiveSelection, + widget.configurations.selectionColor, + widget.configurations.enableInteractiveSelection, _hasFocus, MediaQuery.devicePixelRatioOf(context), _cursorCont); @@ -842,12 +842,12 @@ class QuillRawEditorState extends EditorState controller.addListener(_didChangeTextEditingValueListener); - _scrollController = widget.scrollController; + _scrollController = widget.configurations.scrollController; _scrollController.addListener(_updateSelectionOverlayForScroll); _cursorCont = CursorCont( - show: ValueNotifier<bool>(widget.showCursor), - style: widget.cursorStyle, + show: ValueNotifier<bool>(widget.configurations.showCursor), + style: widget.configurations.cursorStyle, tickerProvider: this, ); @@ -882,7 +882,7 @@ class QuillRawEditorState extends EditorState } // Focus - widget.focusNode.addListener(_handleFocusChanged); + widget.configurations.focusNode.addListener(_handleFocusChanged); } // KeyboardVisibilityController only checks for keyboards that @@ -915,8 +915,8 @@ class QuillRawEditorState extends EditorState ? defaultStyles.merge(parentStyles) : defaultStyles; - if (widget.customStyles != null) { - _styles = _styles!.merge(widget.customStyles!); + if (widget.configurations.customStyles != null) { + _styles = _styles!.merge(widget.configurations.customStyles!); } _requestAutoFocusIfShould(); @@ -924,9 +924,9 @@ class QuillRawEditorState extends EditorState Future<void> _requestAutoFocusIfShould() async { final focusManager = FocusScope.of(context); - if (!_didAutoFocus && widget.autoFocus) { + if (!_didAutoFocus && widget.configurations.autoFocus) { await Future.delayed(Duration.zero); // To avoid exceptions - focusManager.autofocus(widget.focusNode); + focusManager.autofocus(widget.configurations.focusNode); _didAutoFocus = true; } } @@ -935,28 +935,29 @@ class QuillRawEditorState extends EditorState void didUpdateWidget(QuillRawEditor oldWidget) { super.didUpdateWidget(oldWidget); - _cursorCont.show.value = widget.showCursor; - _cursorCont.style = widget.cursorStyle; + _cursorCont.show.value = widget.configurations.showCursor; + _cursorCont.style = widget.configurations.cursorStyle; - if (controller != oldWidget.controller) { - oldWidget.controller.removeListener(_didChangeTextEditingValue); + if (controller != oldWidget.configurations.controller) { + oldWidget.configurations.controller + .removeListener(_didChangeTextEditingValue); controller.addListener(_didChangeTextEditingValue); updateRemoteValueIfNeeded(); } - if (widget.scrollController != _scrollController) { + if (widget.configurations.scrollController != _scrollController) { _scrollController.removeListener(_updateSelectionOverlayForScroll); - _scrollController = widget.scrollController; + _scrollController = widget.configurations.scrollController; _scrollController.addListener(_updateSelectionOverlayForScroll); } - if (widget.focusNode != oldWidget.focusNode) { - oldWidget.focusNode.removeListener(_handleFocusChanged); - widget.focusNode.addListener(_handleFocusChanged); + if (widget.configurations.focusNode != oldWidget.configurations.focusNode) { + oldWidget.configurations.focusNode.removeListener(_handleFocusChanged); + widget.configurations.focusNode.addListener(_handleFocusChanged); updateKeepAlive(); } - if (controller.selection != oldWidget.controller.selection) { + if (controller.selection != oldWidget.configurations.controller.selection) { _selectionOverlay?.update(textEditingValue); } @@ -964,19 +965,20 @@ class QuillRawEditorState extends EditorState if (!shouldCreateInputConnection) { closeConnectionIfNeeded(); } else { - if (oldWidget.readOnly && _hasFocus) { + if (oldWidget.configurations.isReadOnly && _hasFocus) { openConnectionIfNeeded(); } } // in case customStyles changed in new widget - if (widget.customStyles != null) { - _styles = _styles!.merge(widget.customStyles!); + if (widget.configurations.customStyles != null) { + _styles = _styles!.merge(widget.configurations.customStyles!); } } bool _shouldShowSelectionHandles() { - return widget.showSelectionHandles && !controller.selection.isCollapsed; + return widget.configurations.showSelectionHandles && + !controller.selection.isCollapsed; } @override @@ -988,7 +990,7 @@ class QuillRawEditorState extends EditorState _selectionOverlay?.dispose(); _selectionOverlay = null; controller.removeListener(_didChangeTextEditingValueListener); - widget.focusNode.removeListener(_handleFocusChanged); + widget.configurations.focusNode.removeListener(_handleFocusChanged); _cursorCont.dispose(); _clipboardStatus ..removeListener(_onChangedClipboardStatus) @@ -1088,12 +1090,13 @@ class QuillRawEditorState extends EditorState startHandleLayerLink: _startHandleLayerLink, endHandleLayerLink: _endHandleLayerLink, renderObject: renderEditor, - selectionCtrls: widget.selectionCtrls, + selectionCtrls: widget.configurations.selectionCtrls, selectionDelegate: this, clipboardStatus: _clipboardStatus, - contextMenuBuilder: widget.contextMenuBuilder == null + contextMenuBuilder: widget.configurations.contextMenuBuilder == null ? null - : (context) => widget.contextMenuBuilder!(context, this), + : (context) => + widget.configurations.contextMenuBuilder!(context, this), ); _selectionOverlay!.handlesVisible = _shouldShowSelectionHandles(); _selectionOverlay!.showHandles(); @@ -1127,7 +1130,8 @@ class QuillRawEditorState extends EditorState Future<LinkMenuAction> _linkActionPicker(Node linkNode) async { final link = linkNode.style.attributes[Attribute.link.key]!.value!; - return widget.linkActionPickerDelegate(context, link, linkNode); + return widget.configurations + .linkActionPickerDelegate(context, link, linkNode); } bool _showCaretOnScreenScheduled = false; @@ -1140,13 +1144,13 @@ class QuillRawEditorState extends EditorState bool _disableScrollControllerAnimateOnce = false; void _showCaretOnScreen() { - if (!widget.showCursor || _showCaretOnScreenScheduled) { + if (!widget.configurations.showCursor || _showCaretOnScreenScheduled) { return; } _showCaretOnScreenScheduled = true; SchedulerBinding.instance.addPostFrameCallback((_) { - if (widget.scrollable || _scrollController.hasClients) { + if (widget.configurations.scrollable || _scrollController.hasClients) { _showCaretOnScreenScheduled = false; if (!mounted) { @@ -1213,7 +1217,7 @@ class QuillRawEditorState extends EditorState _showCaretOnScreen(); } } else { - widget.focusNode.requestFocus(); + widget.configurations.focusNode.requestFocus(); } } @@ -1291,7 +1295,7 @@ class QuillRawEditorState extends EditorState _pastePlainText = controller.getPlainText(); _pasteStyleAndEmbed = controller.getAllIndividualSelectionStylesAndEmbed(); - if (widget.readOnly) { + if (widget.configurations.isReadOnly) { return; } final selection = textEditingValue.selection; @@ -1311,7 +1315,7 @@ class QuillRawEditorState extends EditorState /// Paste text from [Clipboard]. @override Future<void> pasteText(SelectionChangedCause cause) async { - if (widget.readOnly) { + if (widget.configurations.isReadOnly) { return; } @@ -1372,7 +1376,7 @@ class QuillRawEditorState extends EditorState return; } - final onImagePaste = widget.onImagePaste; + final onImagePaste = widget.configurations.onImagePaste; if (onImagePaste != null) { final image = await Pasteboard.image; @@ -1411,7 +1415,7 @@ class QuillRawEditorState extends EditorState } @override - bool get wantKeepAlive => widget.focusNode.hasFocus; + bool get wantKeepAlive => widget.configurations.focusNode.hasFocus; @override AnimationController get floatingCursorResetController => diff --git a/lib/src/widgets/raw_editor/raw_editor_state_selection_delegate_mixin.dart b/lib/src/widgets/raw_editor/raw_editor_state_selection_delegate_mixin.dart index 0ad2c1c7..46617573 100644 --- a/lib/src/widgets/raw_editor/raw_editor_state_selection_delegate_mixin.dart +++ b/lib/src/widgets/raw_editor/raw_editor_state_selection_delegate_mixin.dart @@ -14,17 +14,18 @@ mixin RawEditorStateSelectionDelegateMixin on EditorState implements TextSelectionDelegate { @override TextEditingValue get textEditingValue { - return widget.controller.plainTextEditingValue; + return widget.configurations.controller.plainTextEditingValue; } set textEditingValue(TextEditingValue value) { final cursorPosition = value.selection.extentOffset; - final oldText = widget.controller.document.toPlainText(); + final oldText = widget.configurations.controller.document.toPlainText(); final newText = value.text; final diff = getDiff(oldText, newText, cursorPosition); if (diff.deleted == '' && diff.inserted == '') { // Only changing selection range - widget.controller.updateSelection(value.selection, ChangeSource.local); + widget.configurations.controller + .updateSelection(value.selection, ChangeSource.local); return; } @@ -34,7 +35,7 @@ mixin RawEditorStateSelectionDelegateMixin on EditorState insertedText = containsEmbed ? _adjustInsertedText(diff.inserted) : diff.inserted; - widget.controller.replaceText( + widget.configurations.controller.replaceText( diff.start, diff.deleted.length, insertedText, value.selection); _applyPasteStyleAndEmbed(insertedText, diff.start, containsEmbed); @@ -51,18 +52,22 @@ mixin RawEditorStateSelectionDelegateMixin on EditorState final local = pos + offset; if (styleAndEmbed is Embeddable) { - widget.controller.replaceText(local, 0, styleAndEmbed, null); + widget.configurations.controller + .replaceText(local, 0, styleAndEmbed, null); } else { final style = styleAndEmbed as Style; if (style.isInline) { - widget.controller + widget.configurations.controller .formatTextStyle(local, pasteStyleAndEmbed[i].length!, style); } else if (style.isBlock) { - final node = widget.controller.document.queryChild(local).node; + final node = widget.configurations.controller.document + .queryChild(local) + .node; if (node != null && pasteStyleAndEmbed[i].length == node.length - 1) { for (final attribute in style.values) { - widget.controller.document.format(local, 0, attribute); + widget.configurations.controller.document + .format(local, 0, attribute); } } } @@ -164,15 +169,18 @@ mixin RawEditorStateSelectionDelegateMixin on EditorState } @override - bool get cutEnabled => widget.contextMenuBuilder != null && !widget.readOnly; + bool get cutEnabled => + widget.configurations.contextMenuBuilder != null && + !widget.configurations.isReadOnly; @override - bool get copyEnabled => widget.contextMenuBuilder != null; + bool get copyEnabled => widget.configurations.contextMenuBuilder != null; @override bool get pasteEnabled => - widget.contextMenuBuilder != null && !widget.readOnly; + widget.configurations.contextMenuBuilder != null && + !widget.configurations.isReadOnly; @override - bool get selectAllEnabled => widget.contextMenuBuilder != null; + bool get selectAllEnabled => widget.configurations.contextMenuBuilder != null; } diff --git a/lib/src/widgets/raw_editor/raw_editor_state_text_input_client_mixin.dart b/lib/src/widgets/raw_editor/raw_editor_state_text_input_client_mixin.dart index 01411570..cc0ac14c 100644 --- a/lib/src/widgets/raw_editor/raw_editor_state_text_input_client_mixin.dart +++ b/lib/src/widgets/raw_editor/raw_editor_state_text_input_client_mixin.dart @@ -1,8 +1,8 @@ import 'dart:ui'; -import 'package:flutter/animation.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/scheduler.dart'; +import 'package:flutter/animation.dart' show Curves; +import 'package:flutter/foundation.dart' show kIsWeb; +import 'package:flutter/scheduler.dart' show SchedulerBinding; import 'package:flutter/services.dart'; import '../../models/documents/document.dart'; @@ -27,7 +27,8 @@ mixin RawEditorStateTextInputClientMixin on EditorState /// - cmd/ctrl+c shortcut to copy. /// - cmd/ctrl+a to select all. /// - Changing the selection using a physical keyboard. - bool get shouldCreateInputConnection => kIsWeb || !widget.readOnly; + bool get shouldCreateInputConnection => + kIsWeb || !widget.configurations.isReadOnly; /// Returns `true` if there is open input connection. bool get hasConnection => @@ -36,9 +37,10 @@ mixin RawEditorStateTextInputClientMixin on EditorState /// Opens or closes input connection based on the current state of /// [focusNode] and [value]. void openOrCloseConnection() { - if (widget.focusNode.hasFocus && widget.focusNode.consumeKeyboardToken()) { + if (widget.configurations.focusNode.hasFocus && + widget.configurations.focusNode.consumeKeyboardToken()) { openConnectionIfNeeded(); - } else if (!widget.focusNode.hasFocus) { + } else if (!widget.configurations.focusNode.hasFocus) { closeConnectionIfNeeded(); } } @@ -54,14 +56,16 @@ mixin RawEditorStateTextInputClientMixin on EditorState this, TextInputConfiguration( inputType: TextInputType.multiline, - readOnly: widget.readOnly, - inputAction: TextInputAction.newline, - enableSuggestions: !widget.readOnly, - keyboardAppearance: widget.keyboardAppearance, - textCapitalization: widget.textCapitalization, - allowedMimeTypes: widget.contentInsertionConfiguration == null - ? const <String>[] - : widget.contentInsertionConfiguration!.allowedMimeTypes, + readOnly: widget.configurations.isReadOnly, + inputAction: widget.configurations.textInputAction, + enableSuggestions: !widget.configurations.isReadOnly, + keyboardAppearance: widget.configurations.keyboardAppearance, + textCapitalization: widget.configurations.textCapitalization, + allowedMimeTypes: + widget.configurations.contentInsertionConfiguration == null + ? const <String>[] + : widget.configurations.contentInsertionConfiguration! + .allowedMimeTypes, ), ); @@ -187,9 +191,10 @@ mixin RawEditorStateTextInputClientMixin on EditorState final cursorPosition = value.selection.extentOffset; final diff = getDiff(oldText, text, cursorPosition); if (diff.deleted.isEmpty && diff.inserted.isEmpty) { - widget.controller.updateSelection(value.selection, ChangeSource.local); + widget.configurations.controller + .updateSelection(value.selection, ChangeSource.local); } else { - widget.controller.replaceText( + widget.configurations.controller.replaceText( diff.start, diff.deleted.length, diff.inserted, value.selection); } } diff --git a/lib/src/widgets/toolbar/buttons/quill_icon.dart b/lib/src/widgets/toolbar/buttons/quill_icon.dart index 37e5b81a..8c2b9637 100644 --- a/lib/src/widgets/toolbar/buttons/quill_icon.dart +++ b/lib/src/widgets/toolbar/buttons/quill_icon.dart @@ -28,6 +28,14 @@ class QuillToolbarIconButton extends StatelessWidget { @override Widget build(BuildContext context) { + // return IconButton( + // onPressed: () { + // onPressed?.call(); + // afterPressed?.call(); + // }, + // icon: icon ?? const SizedBox.shrink(), + // tooltip: tooltip, + // ); return ConstrainedBox( constraints: BoxConstraints.tightFor(width: size, height: size), child: UtilityWidgets.maybeTooltip( diff --git a/lib/src/widgets/toolbar/buttons/select_header_style.dart b/lib/src/widgets/toolbar/buttons/select_header_style.dart index 865a619d..b8179cf8 100644 --- a/lib/src/widgets/toolbar/buttons/select_header_style.dart +++ b/lib/src/widgets/toolbar/buttons/select_header_style.dart @@ -145,6 +145,8 @@ class QuillToolbarSelectHeaderStyleButtonsState } final theme = Theme.of(context); final isSelected = _selectedAttribute == attribute; + // TODO: This needs to be updated to materail 3 as well just like in + // quill_icon.dart return Padding( padding: const EdgeInsets.symmetric(horizontal: !kIsWeb ? 1.0 : 5.0), child: ConstrainedBox( diff --git a/lib/src/widgets/utils/provider.dart b/lib/src/widgets/utils/provider.dart index 6a9028e0..12a49553 100644 --- a/lib/src/widgets/utils/provider.dart +++ b/lib/src/widgets/utils/provider.dart @@ -98,7 +98,7 @@ class QuillToolbarProvider extends InheritedWidget { 'because ' 'The provider is $provider. Please make sure to wrap this widget' ' with' - ' QuillProvider widget. ' + ' QuillToolbarProvider widget. ' 'You might using QuillToolbar so make sure to' ' wrap them with the quill provider widget and setup the required ' 'configurations', @@ -157,8 +157,8 @@ class QuillBaseToolbarProvider extends InheritedWidget { 'because ' 'The provider is $provider. Please make sure to wrap this widget' ' with' - ' QuillProvider widget. ' - 'You might using QuillToolbar so make sure to' + ' QuillBaseToolbarProvider widget. ' + 'You might using QuillBaseToolbar so make sure to' ' wrap them with the quill provider widget and setup the required ' 'configurations', 'QuillProvider', @@ -214,7 +214,7 @@ class QuillEditorProvider extends InheritedWidget { 'because ' 'The provider is $provider. Please make sure to wrap this widget' ' with' - ' QuillProvider widget. ' + ' QuillEditorProvider widget. ' 'You might using QuillEditor so make sure to' ' wrap them with the quill provider widget and setup the required ' 'configurations', diff --git a/pubspec.yaml b/pubspec.yaml index 437590ff..deca5dd6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -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.4.1 +version: 8.4.2 homepage: https://1o24bbs.com/c/bulletjournal/108 repository: https://github.com/singerdmx/flutter-quill