New improvemenets and documentation (#1443)

pull/1448/head
Ahmed Hnewa 1 year ago committed by GitHub
parent 81e3046a82
commit 7c5a12b140
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      .github/ISSUE_TEMPLATE/issue-template.md
  2. 44
      .github/PULL_REQUEST_TEMPLATE.md
  3. 11
      CHANGELOG.md
  4. 29
      README.md
  5. 34
      flutter_quill_extensions/CHANGELOG.md
  6. 11
      flutter_quill_extensions/lib/embeds/toolbar/image_button.dart
  7. 6
      flutter_quill_extensions/lib/embeds/toolbar/media_button.dart
  8. 11
      flutter_quill_extensions/lib/embeds/toolbar/video_button.dart
  9. 2
      flutter_quill_extensions/pubspec.yaml
  10. 12
      lib/src/utils/platform.dart
  11. 32
      lib/src/widgets/editor.dart
  12. 101
      lib/src/widgets/raw_editor.dart
  13. 11
      lib/src/widgets/toolbar.dart
  14. 3
      lib/src/widgets/toolbar/color_button.dart
  15. 3
      lib/src/widgets/toolbar/link_style_button.dart
  16. 4
      lib/src/widgets/toolbar/link_style_button2.dart
  17. 17
      lib/src/widgets/toolbar/search_button.dart
  18. 13
      lib/src/widgets/toolbar/toggle_check_list_button.dart
  19. 2
      pubspec.yaml

@ -11,7 +11,15 @@ My issue is about [Web]
My issue is about [Mobile] My issue is about [Mobile]
My issue is about [Desktop] My issue is about [Desktop]
I have tried running `example` directory successfully before creating an issue here. I have tried running `example` directory successfully before creating an issue here.
Please note that we are using <b>latest</b> flutter version in stable channel on branch master. If you are using beta or master channel, or you are not using <b>latest</b> flutter version in stable channel, you may experience error. Please note that we are using <b>latest</b> flutter version in stable channel on branch master. If you are using beta or master channel, or you are not using <b>latest</b> flutter version in stable channel, you may experience error.
<!-- Please explain how to encounter the issue in details if possible -->
<!-- Don't forgot to mention the platform you are testing in -->
<!-- Insert your images here if possible -->
<!-- Images: -->
<!-- Add short video that showcase the problem will help -->

@ -0,0 +1,44 @@
# Pull Request
## Description
Provide a brief description of your changes.
## Issues
<!-- Remove this if your pull request address changes other than existing issues -->
Closes #IssueNumber
(Replace "IssueNumber" with the actual issue number you are addressing.)
## Improvements
<!-- Please tell us the improvemenets you made in a list -->
<!-- Example: -->
- Improve code readability
- Improve peformance
## Features
<!-- Please tell us the features you added in a list if you add any -->
<!-- Example: -->
- Add a new feature
- Allow to custmize the widgets
<!-- Remove this if your pull request about other changes -->
## Additional notes
<!-- Optional -->
## Suggestions
<!-- Optional -->
## Checklist
<!-- Mark all that applies with `[x]` -->
- [ ] I have added/updated relevant documentation <!-- REQUIRED -->
- [ ] I have tested these changes locally. <!-- REQUIRED -->
- [ ] I have followed the code style and guidelines. <!-- REQUIRED -->
- [ ] I have updated `CHANGELOG.md` with my changes in the next section <!-- REQUIRED -->
- [ ] I have run "dart format ." on the project <!-- REQUIRED -->
- [ ] I have run `flutter test` and `flutter analyze` and it passed successfully <!-- REQUIRED -->

@ -1,8 +1,15 @@
# [7.4.14] # [7.4.16]
- Update documentation and README.md
# [7.4.15]
- Custom style attrbuites for platforms other than mobile (alignment, margin, width, height) - Custom style attrbuites for platforms other than mobile (alignment, margin, width, height)
- Improve performance by reducing the number of widgets rebuilt by listening to media query for only the needed things, for example instead of using `MediaQuery.of(context).size`, now we are using `MediaQuery.sizeOf(context)`
- Bug fixes and other improvemenets - Bug fixes and other improvemenets
# [7.4.14]
- Improve performance by reducing the number of widgets rebuilt by listening to media query for only the needed things, for example instead of using `MediaQuery.of(context).size`, now we are using `MediaQuery.sizeOf(context)`
- Add MediaButton for picking the images only since the video one is not ready - Add MediaButton for picking the images only since the video one is not ready
- A new feature which allows customizing the text selection in quill editor which is useful for custom theme design system for custom app widget - A new feature which allows customizing the text selection in quill editor which is useful for custom theme design system for custom app widget

@ -232,11 +232,21 @@ QuillToolbar.basic(
> [!WARNING] > [!WARNING]
> >
> This package uses [`gal`](https://github.com/natsuk4ze/) to save images. > If you are using [flutter_quill_extensions](https://pub.dev/packages/flutter_quill_extensions) package to add support for images, videos and more
> The extensions package require additional configurations:
>
> 1. We are using [`gal`](https://github.com/natsuk4ze/) plugin to save images.
> For this to work, you need to add the appropriate permissions > For this to work, you need to add the appropriate permissions
> to your `Info.plist` and `AndroidManifest.xml` files. > to your `Info.plist` and `AndroidManifest.xml` files.
>
> See <https://github.com/natsuk4ze/gal#-get-started> to add the needed lines. > See <https://github.com/natsuk4ze/gal#-get-started> to add the needed lines.
>
> 2. We also use [`image_picker`](https://pub.dev/packages/image_picker) plugin for picking images so please make sure follow the instructions
>
> 3. For loading the image from the internet we need internet permission
> 1. For Android, you need to add some permissions in `AndroidManifest.xml`, Please follow this [link](https://developer.android.com/training/basics/network-ops/connecting) for more info, the internet permission included by default only for debugging so you need to follow this link to add it in the release version too. you should allow loading images and videos only for the `https` protocol but if you want http too then you need to configure your android application to accept `http` in the release mode, follow this [link](https://stackoverflow.com/questions/45940861/android-8-cleartext-http-traffic-not-permitted) for more info.
> 2. for macOS you also need to include a key in your `Info.plist`, please follow this [link](https://stackoverflow.com/a/61201081/18519412) to add the required configurations
>
> The extensions package also use [image_picker](https://pub.dev/packages/image_picker) which also require some configurations, follow this [link](https://pub.dev/packages/image_picker#installation). It's needed for Android, iOS, macOS, we must inform you that you can't pick photo using camera in desktop so make sure to handle that if you plan on add support for desktop, this might changed in the future and for more info follow this [link](https://pub.dev/packages/image_picker#windows-macos-and-linux)
### Custom Size Image for Mobile ### Custom Size Image for Mobile
@ -253,6 +263,21 @@ Define `mobileWidth`, `mobileHeight`, `mobileMargin`, `mobileAlignment` as follo
} }
``` ```
### Custom Size Image for other platforms (excluding web)
Define `width`, `height`, `margin`, `alignment` as follows:
```dart
{
"insert": {
"image": "https://user-images.githubusercontent.com/122956/72955931-ccc07900-3d52-11ea-89b1-d468a6e2aa2b.png"
},
"attributes":{
"style":"width: 50; height: 50; margin: 10; alignment: topLeft"
}
}
```
### Custom Embed Blocks ### Custom Embed Blocks
Sometimes you want to add some custom content inside your text, custom widgets inside of them. An example is adding notes to the text, or anything custom that you want to add in your text editor. Sometimes you want to add some custom content inside your text, custom widgets inside of them. An example is adding notes to the text, or anything custom that you want to add in your text editor.

@ -1,4 +1,5 @@
## 0.5.1 ## 0.5.1
- Provide a way to use custom image provider for the image widgets - Provide a way to use custom image provider for the image widgets
- Provide a way to handle different errors in image widgets - Provide a way to handle different errors in image widgets
- Two bug fixes related to pick the image and capture it using the camera - Two bug fixes related to pick the image and capture it using the camera
@ -12,42 +13,49 @@
- Add new custom style attrbuite for desktop and other platforms - Add new custom style attrbuite for desktop and other platforms
## 0.5.0 ## 0.5.0
- Migrated from `gallery_saver` to `gal` for saving images - Migrated from `gallery_saver` to `gal` for saving images
- Added callbacks for greater control of editing images - Added callbacks for greater control of editing images
## 0.4.1 ## 0.4.1
- Updated dependencies to support image_picker 1.0 - Updated dependencies to support image_picker 1.0
## 0.4.0 ## 0.4.0
- Fix backspace around images [PR #1309](https://github.com/singerdmx/flutter-quill/pull/1309) - Fix backspace around images [PR #1309](https://github.com/singerdmx/flutter-quill/pull/1309)
- Feat/link regexp [PR #1329](https://github.com/singerdmx/flutter-quill/pull/1329) - Feat/link regexp [PR #1329](https://github.com/singerdmx/flutter-quill/pull/1329)
## 0.3.4 ## 0.3.4
* Resolve deprecated method use in the `video_player` package
- Resolve deprecated method use in the `video_player` package
## 0.3.3 ## 0.3.3
* Fix a prototype bug which was bring by [PR #1230](https://github.com/singerdmx/flutter-quill/pull/1230#issuecomment-1560597099)
- Fix a prototype bug which was bring by [PR #1230](https://github.com/singerdmx/flutter-quill/pull/1230#issuecomment-1560597099)
## 0.3.2 ## 0.3.2
* Updated dependencies to support intl 0.18
- Updated dependencies to support intl 0.18
## 0.3.1 ## 0.3.1
* Image embedding tweaks
* Add MediaButton which is intened to superseed the ImageButton and VideoButton. Only image selection is working. - Image embedding tweaks
* Implement image insert for web (image as base64) - Add MediaButton which is intened to superseed the ImageButton and VideoButton. Only image selection is working.
- Implement image insert for web (image as base64)
## 0.3.0 ## 0.3.0
* Added support for adding custom tooltips to toolbar buttons - Added support for adding custom tooltips to toolbar buttons
## 0.2.0 ## 0.2.0
* Allow widgets to override widget span properties [b7951b0](https://github.com/singerdmx/flutter-quill/commit/b7951b02c9086ea42e7aad6d78e6c9b0297562e5) - Allow widgets to override widget span properties [b7951b0](https://github.com/singerdmx/flutter-quill/commit/b7951b02c9086ea42e7aad6d78e6c9b0297562e5)
* Remove tuples [3e9452e](https://github.com/singerdmx/flutter-quill/commit/3e9452e675e8734ff50364c5f7b5d34088d5ff05) - Remove tuples [3e9452e](https://github.com/singerdmx/flutter-quill/commit/3e9452e675e8734ff50364c5f7b5d34088d5ff05)
* Remove transparent color of ImageVideoUtils dialog [74544bd](https://github.com/singerdmx/flutter-quill/commit/74544bd945a9d212ca1e8d6b3053dbecee22b720) - Remove transparent color of ImageVideoUtils dialog [74544bd](https://github.com/singerdmx/flutter-quill/commit/74544bd945a9d212ca1e8d6b3053dbecee22b720)
* Migrate to `youtube_player_flutter` from `youtube_player_flutter_quill` - Migrate to `youtube_player_flutter` from `youtube_player_flutter_quill`
* Updates to forumla button [5228f38](https://github.com/singerdmx/flutter-quill/commit/5228f389ba6f37d61d445cfe138c19fcf8766d71) - Updates to forumla button [5228f38](https://github.com/singerdmx/flutter-quill/commit/5228f389ba6f37d61d445cfe138c19fcf8766d71)
## 0.1.0 ## 0.1.0
* Initial release - Initial release

@ -66,7 +66,7 @@ class ImageButton extends StatelessWidget {
Future<void> _onPressedHandler(BuildContext context) async { Future<void> _onPressedHandler(BuildContext context) async {
final onImagePickCallbackRef = onImagePickCallback; final onImagePickCallbackRef = onImagePickCallback;
if (onImagePickCallbackRef == null) { if (onImagePickCallbackRef == null) {
_typeLink(context); await _typeLink(context);
return; return;
} }
final selector = final selector =
@ -80,7 +80,7 @@ class ImageButton extends StatelessWidget {
_pickImage(context); _pickImage(context);
break; break;
case MediaPickSetting.Link: case MediaPickSetting.Link:
_typeLink(context); await _typeLink(context);
break; break;
case MediaPickSetting.Camera: case MediaPickSetting.Camera:
await ImageVideoUtils.handleImageButtonTap( await ImageVideoUtils.handleImageButtonTap(
@ -116,14 +116,15 @@ class ImageButton extends StatelessWidget {
webImagePickImpl: webImagePickImpl, webImagePickImpl: webImagePickImpl,
); );
void _typeLink(BuildContext context) { Future<void> _typeLink(BuildContext context) async {
showDialog<String>( final value = await showDialog<String>(
context: context, context: context,
builder: (_) => LinkDialog( builder: (_) => LinkDialog(
dialogTheme: dialogTheme, dialogTheme: dialogTheme,
linkRegExp: linkRegExp, linkRegExp: linkRegExp,
), ),
).then(_linkSubmitted); );
_linkSubmitted(value);
} }
void _linkSubmitted(String? value) { void _linkSubmitted(String? value) {

@ -39,6 +39,7 @@ class MediaButton extends StatelessWidget {
this.galleryButtonText, this.galleryButtonText,
this.linkButtonText, this.linkButtonText,
this.autovalidateMode = AutovalidateMode.disabled, this.autovalidateMode = AutovalidateMode.disabled,
this.dialogBarrierColor = Colors.black54,
Key? key, Key? key,
this.validationMessage, this.validationMessage,
}) : assert(type == QuillMediaType.image, }) : assert(type == QuillMediaType.image,
@ -55,6 +56,7 @@ class MediaButton extends StatelessWidget {
final String? tooltip; final String? tooltip;
final MediaFilePicker mediaFilePicker; final MediaFilePicker mediaFilePicker;
final MediaPickedCallback? onMediaPickedCallback; final MediaPickedCallback? onMediaPickedCallback;
final Color dialogBarrierColor;
/// The margin between child widgets in the dialog. /// The margin between child widgets in the dialog.
final double childrenSpacing; final double childrenSpacing;
@ -344,10 +346,8 @@ class _MediaLinkDialogState extends State<MediaLinkDialog> {
void _submitLink() => Navigator.pop(context, _linkController.text); void _submitLink() => Navigator.pop(context, _linkController.text);
String? _validateLink(String? value) { String? _validateLink(String? value) {
// TODO: Use [AutoFormatMultipleLinksRule.oneLineRegExp]
// in the next update
if ((value?.isEmpty ?? false) || if ((value?.isEmpty ?? false) ||
!AutoFormatMultipleLinksRule.linkRegExp.hasMatch(value!)) { !AutoFormatMultipleLinksRule.oneLineRegExp.hasMatch(value!)) {
return widget.validationMessage ?? 'That is not a valid URL'; return widget.validationMessage ?? 'That is not a valid URL';
} }

@ -74,11 +74,11 @@ class VideoButton extends StatelessWidget {
if (source == MediaPickSetting.Gallery) { if (source == MediaPickSetting.Gallery) {
_pickVideo(context); _pickVideo(context);
} else { } else {
_typeLink(context); await _typeLink(context);
} }
} }
} else { } else {
_typeLink(context); await _typeLink(context);
} }
} }
@ -91,11 +91,12 @@ class VideoButton extends StatelessWidget {
webVideoPickImpl: webVideoPickImpl, webVideoPickImpl: webVideoPickImpl,
); );
void _typeLink(BuildContext context) { Future<void> _typeLink(BuildContext context) async {
showDialog<String>( final value = await showDialog<String>(
context: context, context: context,
builder: (_) => LinkDialog(dialogTheme: dialogTheme), builder: (_) => LinkDialog(dialogTheme: dialogTheme),
).then(_linkSubmitted); );
_linkSubmitted(value);
} }
void _linkSubmitted(String? value) { void _linkSubmitted(String? value) {

@ -12,7 +12,7 @@ dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
flutter_quill: ^7.4.14 flutter_quill: ^7.4.15
# In case you are working on changes for both libraries, # In case you are working on changes for both libraries,
# flutter_quill: # flutter_quill:
# path: ~/development/playground/framework_based/flutter/flutter-quill # path: ~/development/playground/framework_based/flutter/flutter-quill

@ -2,14 +2,18 @@ import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart' import 'package:flutter/foundation.dart'
show kIsWeb, TargetPlatform, defaultTargetPlatform; show kIsWeb, TargetPlatform, defaultTargetPlatform;
bool isWeb() {
return kIsWeb;
}
bool isMobile([TargetPlatform? targetPlatform]) { bool isMobile([TargetPlatform? targetPlatform]) {
if (kIsWeb) return false; if (isWeb()) return false;
targetPlatform ??= defaultTargetPlatform; targetPlatform ??= defaultTargetPlatform;
return {TargetPlatform.iOS, TargetPlatform.android}.contains(targetPlatform); return {TargetPlatform.iOS, TargetPlatform.android}.contains(targetPlatform);
} }
bool isDesktop([TargetPlatform? targetPlatform]) { bool isDesktop([TargetPlatform? targetPlatform]) {
if (kIsWeb) return false; if (isWeb()) return false;
targetPlatform ??= defaultTargetPlatform; targetPlatform ??= defaultTargetPlatform;
return {TargetPlatform.macOS, TargetPlatform.linux, TargetPlatform.windows} return {TargetPlatform.macOS, TargetPlatform.linux, TargetPlatform.windows}
.contains(targetPlatform); .contains(targetPlatform);
@ -21,7 +25,7 @@ bool isKeyboardOS([TargetPlatform? targetPlatform]) {
} }
bool isAppleOS([TargetPlatform? targetPlatform]) { bool isAppleOS([TargetPlatform? targetPlatform]) {
if (kIsWeb) return false; if (isWeb()) return false;
targetPlatform ??= defaultTargetPlatform; targetPlatform ??= defaultTargetPlatform;
return { return {
TargetPlatform.macOS, TargetPlatform.macOS,
@ -30,7 +34,7 @@ bool isAppleOS([TargetPlatform? targetPlatform]) {
} }
bool isMacOS([TargetPlatform? targetPlatform]) { bool isMacOS([TargetPlatform? targetPlatform]) {
if (kIsWeb) return false; if (isWeb()) return false;
targetPlatform ??= defaultTargetPlatform; targetPlatform ??= defaultTargetPlatform;
return TargetPlatform.macOS == targetPlatform; return TargetPlatform.macOS == targetPlatform;
} }

@ -583,7 +583,7 @@ class QuillEditorState extends State<QuillEditor>
: child, : child,
); );
if (kIsWeb) { if (isWeb()) {
// Intercept RawKeyEvent on Web to prevent it from propagating to parents // Intercept RawKeyEvent on Web to prevent it from propagating to parents
// that might interfere with the editor key behavior, such as // that might interfere with the editor key behavior, such as
// SingleChildScrollView. Thanks to @wliumelb for the workaround. // SingleChildScrollView. Thanks to @wliumelb for the workaround.
@ -633,7 +633,13 @@ class QuillEditorState extends State<QuillEditor>
bool get selectionEnabled => widget.enableInteractiveSelection; bool get selectionEnabled => widget.enableInteractiveSelection;
void _requestKeyboard() { void _requestKeyboard() {
_editorKey.currentState!.requestKeyboard(); final editorCurrentState = _editorKey.currentState;
if (editorCurrentState == null) {
throw ArgumentError.notNull(
'To request keyboard the editor key must not be null',
);
}
editorCurrentState.requestKeyboard();
} }
} }
@ -1068,7 +1074,9 @@ class RenderEditor extends RenderEditableContainerBox
localSelection(baseChild.container, textSelection, true); localSelection(baseChild.container, textSelection, true);
var basePoint = baseChild.getBaseEndpointForSelection(baseSelection); var basePoint = baseChild.getBaseEndpointForSelection(baseSelection);
basePoint = TextSelectionPoint( basePoint = TextSelectionPoint(
basePoint.point + baseParentData.offset, basePoint.direction); basePoint.point + baseParentData.offset,
basePoint.direction,
);
final extentNode = _container.queryChild(textSelection.end, false).node; final extentNode = _container.queryChild(textSelection.end, false).node;
RenderEditableBox? extentChild = baseChild; RenderEditableBox? extentChild = baseChild;
@ -1086,7 +1094,9 @@ class RenderEditor extends RenderEditableContainerBox
var extentPoint = var extentPoint =
extentChild.getExtentEndpointForSelection(extentSelection); extentChild.getExtentEndpointForSelection(extentSelection);
extentPoint = TextSelectionPoint( extentPoint = TextSelectionPoint(
extentPoint.point + extentParentData.offset, extentPoint.direction); extentPoint.point + extentParentData.offset,
extentPoint.direction,
);
return <TextSelectionPoint>[basePoint, extentPoint]; return <TextSelectionPoint>[basePoint, extentPoint];
} }
@ -1757,13 +1767,13 @@ class RenderEditableContainerBox extends RenderBox
EditableContainerParentData>, EditableContainerParentData>,
RenderBoxContainerDefaultsMixin<RenderEditableBox, RenderBoxContainerDefaultsMixin<RenderEditableBox,
EditableContainerParentData> { EditableContainerParentData> {
RenderEditableContainerBox( RenderEditableContainerBox({
{required container_node.Container container, required container_node.Container container,
required this.textDirection, required this.textDirection,
required this.scrollBottomInset, required this.scrollBottomInset,
required EdgeInsetsGeometry padding, required EdgeInsetsGeometry padding,
List<RenderEditableBox>? children}) List<RenderEditableBox>? children,
: assert(padding.isNonNegative), }) : assert(padding.isNonNegative),
_container = container, _container = container,
_padding = padding { _padding = padding {
addAll(children); addAll(children);

@ -1,17 +1,28 @@
import 'dart:async'; import 'dart:async' show StreamSubscription;
import 'dart:convert'; import 'dart:convert' show jsonDecode;
import 'dart:io'; import 'dart:io' show Platform;
import 'dart:math' as math; import 'dart:math' as math;
import 'dart:ui' as ui hide TextStyle; import 'dart:ui' as ui hide TextStyle;
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart' show defaultTargetPlatform;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart'
import 'package:flutter/scheduler.dart'; show RenderAbstractViewport, ViewportOffset;
import 'package:flutter/services.dart'; import 'package:flutter/scheduler.dart' show SchedulerBinding;
import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart'; import 'package:flutter/services.dart'
import 'package:pasteboard/pasteboard.dart'; show
LogicalKeyboardKey,
Uint8List,
RawKeyDownEvent,
HardwareKeyboard,
Clipboard,
ClipboardData,
TextLayoutMetrics,
TextInputControl;
import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart'
show KeyboardVisibilityController;
import 'package:pasteboard/pasteboard.dart' show Pasteboard;
import '../models/documents/attribute.dart'; import '../models/documents/attribute.dart';
import '../models/documents/document.dart'; import '../models/documents/document.dart';
@ -423,7 +434,7 @@ class RawEditorState extends EditorState
// in the web browser, but we do unfocus for all other kinds of events. // in the web browser, but we do unfocus for all other kinds of events.
switch (event.kind) { switch (event.kind) {
case ui.PointerDeviceKind.touch: case ui.PointerDeviceKind.touch:
if (kIsWeb) { if (isWeb()) {
widget.focusNode.unfocus(); widget.focusNode.unfocus();
} }
break; break;
@ -838,7 +849,9 @@ class RawEditorState extends EditorState
} }
void _handleSelectionChanged( void _handleSelectionChanged(
TextSelection selection, SelectionChangedCause cause) { TextSelection selection,
SelectionChangedCause cause,
) {
final oldSelection = controller.selection; final oldSelection = controller.selection;
controller.updateSelection(selection, ChangeSource.LOCAL); controller.updateSelection(selection, ChangeSource.LOCAL);
@ -1034,15 +1047,17 @@ class RawEditorState extends EditorState
return const VerticalSpacing(0, 0); return const VerticalSpacing(0, 0);
} }
void _didChangeTextEditingValueListener() {
_didChangeTextEditingValue(controller.ignoreFocusOnTextChange);
}
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_clipboardStatus.addListener(_onChangedClipboardStatus); _clipboardStatus.addListener(_onChangedClipboardStatus);
controller.addListener(() { controller.addListener(_didChangeTextEditingValueListener);
_didChangeTextEditingValue(controller.ignoreFocusOnTextChange);
});
_scrollController = widget.scrollController; _scrollController = widget.scrollController;
_scrollController.addListener(_updateSelectionOverlayForScroll); _scrollController.addListener(_updateSelectionOverlayForScroll);
@ -1059,7 +1074,7 @@ class RawEditorState extends EditorState
if (isKeyboardOS()) { if (isKeyboardOS()) {
_keyboardVisible = true; _keyboardVisible = true;
} else if (!kIsWeb && Platform.environment.containsKey('FLUTTER_TEST')) { } else if (!isWeb() && Platform.environment.containsKey('FLUTTER_TEST')) {
// treat tests like a keyboard OS // treat tests like a keyboard OS
_keyboardVisible = true; _keyboardVisible = true;
} else { } else {
@ -1189,7 +1204,7 @@ class RawEditorState extends EditorState
assert(!hasConnection); assert(!hasConnection);
_selectionOverlay?.dispose(); _selectionOverlay?.dispose();
_selectionOverlay = null; _selectionOverlay = null;
controller.removeListener(_didChangeTextEditingValue); controller.removeListener(_didChangeTextEditingValueListener);
widget.focusNode.removeListener(_handleFocusChanged); widget.focusNode.removeListener(_handleFocusChanged);
_cursorCont.dispose(); _cursorCont.dispose();
_clipboardStatus _clipboardStatus
@ -1208,13 +1223,17 @@ class RawEditorState extends EditorState
/// state being in sync with the controller know they may be /// state being in sync with the controller know they may be
/// operating on stale data. /// operating on stale data.
void _markNeedsBuild() { void _markNeedsBuild() {
if (_dirty) {
// No need to rebuilt if it already darty
return;
}
setState(() { setState(() {
_dirty = true; _dirty = true;
}); });
} }
void _didChangeTextEditingValue([bool ignoreFocus = false]) { void _didChangeTextEditingValue([bool ignoreFocus = false]) {
if (kIsWeb) { if (isWeb()) {
_onChangeTextEditingValue(ignoreFocus); _onChangeTextEditingValue(ignoreFocus);
if (!ignoreFocus) { if (!ignoreFocus) {
requestKeyboard(); requestKeyboard();
@ -1394,6 +1413,8 @@ class RawEditorState extends EditorState
@override @override
void requestKeyboard() { void requestKeyboard() {
if (controller.skipRequestKeyboard) { if (controller.skipRequestKeyboard) {
// TODO: There is a bug, requestKeyboard is being called 2-4 times!
// and that just by one simple change
controller.skipRequestKeyboard = false; controller.skipRequestKeyboard = false;
return; return;
} }
@ -1402,7 +1423,10 @@ class RawEditorState extends EditorState
openConnectionIfNeeded(); openConnectionIfNeeded();
if (!keyboardAlreadyShown) { if (!keyboardAlreadyShown) {
/// delay 500 milliseconds for waiting keyboard show up /// delay 500 milliseconds for waiting keyboard show up
Future.delayed(const Duration(milliseconds: 500), _showCaretOnScreen); Future.delayed(
const Duration(milliseconds: 500),
_showCaretOnScreen,
);
} else { } else {
_showCaretOnScreen(); _showCaretOnScreen();
} }
@ -1421,7 +1445,7 @@ class RawEditorState extends EditorState
// toolbar: copy, paste, select, cut. It might also provide additional // toolbar: copy, paste, select, cut. It might also provide additional
// functionality depending on the browser (such as translate). Due to this // functionality depending on the browser (such as translate). Due to this
// we should not show a Flutter toolbar for the editable text elements. // we should not show a Flutter toolbar for the editable text elements.
if (kIsWeb) { if (isWeb()) {
return false; return false;
} }
@ -1769,7 +1793,9 @@ class RawEditorState extends EditorState
@override @override
void didChangeInputControl( void didChangeInputControl(
TextInputControl? oldControl, TextInputControl? newControl) { TextInputControl? oldControl,
TextInputControl? newControl,
) {
// TODO: implement didChangeInputControl // TODO: implement didChangeInputControl
} }
@ -1830,26 +1856,29 @@ class _Editor extends MultiChildRenderObjectWidget {
@override @override
RenderEditor createRenderObject(BuildContext context) { RenderEditor createRenderObject(BuildContext context) {
return RenderEditor( return RenderEditor(
offset: offset, offset: offset,
document: document, document: document,
textDirection: textDirection, textDirection: textDirection,
hasFocus: hasFocus, hasFocus: hasFocus,
scrollable: scrollable, scrollable: scrollable,
selection: selection, selection: selection,
startHandleLayerLink: startHandleLayerLink, startHandleLayerLink: startHandleLayerLink,
endHandleLayerLink: endHandleLayerLink, endHandleLayerLink: endHandleLayerLink,
onSelectionChanged: onSelectionChanged, onSelectionChanged: onSelectionChanged,
onSelectionCompleted: onSelectionCompleted, onSelectionCompleted: onSelectionCompleted,
cursorController: cursorController, cursorController: cursorController,
padding: padding, padding: padding,
maxContentWidth: maxContentWidth, maxContentWidth: maxContentWidth,
scrollBottomInset: scrollBottomInset, scrollBottomInset: scrollBottomInset,
floatingCursorDisabled: floatingCursorDisabled); floatingCursorDisabled: floatingCursorDisabled,
);
} }
@override @override
void updateRenderObject( void updateRenderObject(
BuildContext context, covariant RenderEditor renderObject) { BuildContext context,
covariant RenderEditor renderObject,
) {
renderObject renderObject
..offset = offset ..offset = offset
..document = document ..document = document

@ -577,8 +577,11 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget {
), ),
if (customButtons.isNotEmpty) if (customButtons.isNotEmpty)
if (showDividers) if (showDividers)
QuillDivider(axis, QuillDivider(
color: sectionDividerColor, space: sectionDividerSpace), axis,
color: sectionDividerColor,
space: sectionDividerSpace,
),
for (final customButton in customButtons) for (final customButton in customButtons)
if (customButton.child != null) ...[ if (customButton.child != null) ...[
InkWell( InkWell(
@ -617,6 +620,10 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget {
/// is given. /// is given.
final Color? color; final Color? color;
// We will add this in the next major release and not now
/// The barrier color of the shown dialogs
// final Color dialogbarrierColor;
/// The locale to use for the editor toolbar, defaults to system locale /// The locale to use for the editor toolbar, defaults to system locale
/// More https://github.com/singerdmx/flutter-quill#translation /// More https://github.com/singerdmx/flutter-quill#translation
final Locale? locale; final Locale? locale;

@ -22,6 +22,7 @@ class ColorButton extends StatefulWidget {
this.iconTheme, this.iconTheme,
this.afterButtonPressed, this.afterButtonPressed,
this.tooltip, this.tooltip,
this.dialogBarrierColor = Colors.black54,
Key? key, Key? key,
}) : super(key: key); }) : super(key: key);
@ -32,6 +33,7 @@ class ColorButton extends StatefulWidget {
final QuillIconTheme? iconTheme; final QuillIconTheme? iconTheme;
final VoidCallback? afterButtonPressed; final VoidCallback? afterButtonPressed;
final String? tooltip; final String? tooltip;
final Color dialogBarrierColor;
@override @override
_ColorButtonState createState() => _ColorButtonState(); _ColorButtonState createState() => _ColorButtonState();
@ -159,6 +161,7 @@ class _ColorButtonState extends State<ColorButton> {
showDialog<String>( showDialog<String>(
context: context, context: context,
barrierColor: widget.dialogBarrierColor,
builder: (context) => StatefulBuilder(builder: (context, dlgSetState) { builder: (context) => StatefulBuilder(builder: (context, dlgSetState) {
return AlertDialog( return AlertDialog(
title: Text('Select Color'.i18n), title: Text('Select Color'.i18n),

@ -21,6 +21,7 @@ class LinkStyleButton extends StatefulWidget {
this.tooltip, this.tooltip,
this.linkRegExp, this.linkRegExp,
this.linkDialogAction, this.linkDialogAction,
this.dialogBarrierColor = Colors.black54,
Key? key, Key? key,
}) : super(key: key); }) : super(key: key);
@ -33,6 +34,7 @@ class LinkStyleButton extends StatefulWidget {
final String? tooltip; final String? tooltip;
final RegExp? linkRegExp; final RegExp? linkRegExp;
final LinkDialogAction? linkDialogAction; final LinkDialogAction? linkDialogAction;
final Color dialogBarrierColor;
@override @override
_LinkStyleButtonState createState() => _LinkStyleButtonState(); _LinkStyleButtonState createState() => _LinkStyleButtonState();
@ -95,6 +97,7 @@ class _LinkStyleButtonState extends State<LinkStyleButton> {
void _openLinkDialog(BuildContext context) { void _openLinkDialog(BuildContext context) {
showDialog<_TextLink>( showDialog<_TextLink>(
context: context, context: context,
barrierColor: widget.dialogBarrierColor,
builder: (ctx) { builder: (ctx) {
final link = _getLinkAttributeValue(); final link = _getLinkAttributeValue();
final index = widget.controller.selection.start; final index = widget.controller.selection.start;

@ -30,6 +30,7 @@ class LinkStyleButton2 extends StatefulWidget {
this.autovalidateMode = AutovalidateMode.disabled, this.autovalidateMode = AutovalidateMode.disabled,
this.validationMessage, this.validationMessage,
this.buttonSize, this.buttonSize,
this.dialogBarrierColor = Colors.black54,
Key? key, Key? key,
}) : assert(addLinkLabel == null || addLinkLabel.length > 0), }) : assert(addLinkLabel == null || addLinkLabel.length > 0),
assert(editLinkLabel == null || editLinkLabel.length > 0), assert(editLinkLabel == null || editLinkLabel.length > 0),
@ -66,6 +67,8 @@ class LinkStyleButton2 extends StatefulWidget {
/// The size of dialog buttons. /// The size of dialog buttons.
final Size? buttonSize; final Size? buttonSize;
final Color dialogBarrierColor;
@override @override
State<LinkStyleButton2> createState() => _LinkStyleButton2State(); State<LinkStyleButton2> createState() => _LinkStyleButton2State();
} }
@ -124,6 +127,7 @@ class _LinkStyleButton2State extends State<LinkStyleButton2> {
final textLink = await showDialog<QuillTextLink>( final textLink = await showDialog<QuillTextLink>(
context: context, context: context,
barrierColor: widget.dialogBarrierColor,
builder: (_) => LinkStyleDialog( builder: (_) => LinkStyleDialog(
dialogTheme: widget.dialogTheme, dialogTheme: widget.dialogTheme,
text: initialTextLink.text, text: initialTextLink.text,

@ -13,6 +13,7 @@ class SearchButton extends StatelessWidget {
this.iconSize = kDefaultIconSize, this.iconSize = kDefaultIconSize,
this.fillColor, this.fillColor,
this.iconTheme, this.iconTheme,
this.dialogBarrierColor = Colors.black54,
this.dialogTheme, this.dialogTheme,
this.afterButtonPressed, this.afterButtonPressed,
this.tooltip, this.tooltip,
@ -24,6 +25,7 @@ class SearchButton extends StatelessWidget {
final QuillController controller; final QuillController controller;
final Color? fillColor; final Color? fillColor;
final Color dialogBarrierColor;
final QuillIconTheme? iconTheme; final QuillIconTheme? iconTheme;
final QuillDialogTheme? dialogTheme; final QuillDialogTheme? dialogTheme;
@ -52,12 +54,19 @@ class SearchButton extends StatelessWidget {
} }
Future<void> _onPressedHandler(BuildContext context) async { Future<void> _onPressedHandler(BuildContext context) async {
await showDialog<String>( final value = await showDialog<String>(
barrierColor: dialogBarrierColor,
context: context, context: context,
builder: (_) => SearchDialog( builder: (_) => SearchDialog(
controller: controller, dialogTheme: dialogTheme, text: ''), controller: controller,
).then(_searchSubmitted); dialogTheme: dialogTheme,
text: '',
),
);
_searchSubmitted(value);
} }
void _searchSubmitted(String? value) {} void _searchSubmitted(String? value) {
// If we are doing nothing here then why we care about the result??
}
} }

@ -111,8 +111,15 @@ class _ToggleCheckListButtonState extends State<ToggleCheckListButton> {
} }
void _toggleAttribute() { void _toggleAttribute() {
widget.controller.formatSelection(_isToggled! // By default don't show the keybaord request as it's quite annoying
? Attribute.clone(Attribute.unchecked, null) // We will provide the option to control this in the next major update
: Attribute.unchecked); // See https://github.com/singerdmx/flutter-quill/issues/1440
widget.controller
..skipRequestKeyboard = true
..formatSelection(
_isToggled!
? Attribute.clone(Attribute.unchecked, null)
: Attribute.unchecked,
);
} }
} }

@ -1,6 +1,6 @@
name: flutter_quill 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. 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: 7.4.15 version: 7.4.16
homepage: https://1o24bbs.com/c/bulletjournal/108 homepage: https://1o24bbs.com/c/bulletjournal/108
repository: https://github.com/singerdmx/flutter-quill repository: https://github.com/singerdmx/flutter-quill

Loading…
Cancel
Save