Merge branch 'master' into fix/youtube-player-desktop-extensions-package

pull/1916/head
Ellet 10 months ago committed by GitHub
commit e5d8dbd322
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 4
      .github/ISSUE_TEMPLATE/3_question.yml
  2. 1
      .github/ISSUE_TEMPLATE/config.yml
  3. 20
      .github/workflows/publish.yml
  4. 224
      CHANGELOG.md
  5. 2
      CHANGELOG_DATA.json
  6. 53
      CONTRIBUTING.md
  7. 188
      README.md
  8. 224
      dart_quill_delta/CHANGELOG.md
  9. 2
      dart_quill_delta/pubspec.yaml
  10. 13
      doc/configurations/custom_buttons.md
  11. 10
      doc/configurations/font_size.md
  12. 19
      doc/configurations/localizations_setup.md
  13. 16
      doc/configurations/using_custom_app_widget.md
  14. 24
      doc/custom_embed_blocks.md
  15. 9
      doc/custom_toolbar.md
  16. 26
      doc/development_notes.md
  17. 55
      doc/todo.md
  18. 82
      doc/translation.md
  19. 12
      example/.metadata
  20. 1
      example/lib/screens/quill/my_quill_toolbar.dart
  21. 8
      example/lib/screens/quill/samples/quill_videos_sample.dart
  22. 39
      example/web/index.html
  23. 2
      example/web/manifest.json
  24. 224
      flutter_quill_extensions/CHANGELOG.md
  25. 184
      flutter_quill_extensions/README.md
  26. 75
      flutter_quill_extensions/lib/embeds/table/editor/table_cell_embed.dart
  27. 234
      flutter_quill_extensions/lib/embeds/table/editor/table_embed.dart
  28. 85
      flutter_quill_extensions/lib/embeds/table/editor/table_models.dart
  29. 113
      flutter_quill_extensions/lib/embeds/table/toolbar/table_button.dart
  30. 27
      flutter_quill_extensions/lib/flutter_quill_embeds.dart
  31. 5
      flutter_quill_extensions/lib/flutter_quill_extensions.dart
  32. 27
      flutter_quill_extensions/lib/models/config/table/table_configurations.dart
  33. 2
      flutter_quill_extensions/lib/utils/patterns.dart
  34. 85
      flutter_quill_extensions/lib/utils/quill_table_utils.dart
  35. 6
      flutter_quill_extensions/pubspec.yaml
  36. 224
      flutter_quill_test/CHANGELOG.md
  37. 2
      flutter_quill_test/pubspec.yaml
  38. 19
      lib/src/l10n/extensions/localizations.dart
  39. 12
      lib/src/l10n/generated/quill_localizations.dart
  40. 6
      lib/src/l10n/generated/quill_localizations_ar.dart
  41. 6
      lib/src/l10n/generated/quill_localizations_bg.dart
  42. 6
      lib/src/l10n/generated/quill_localizations_bn.dart
  43. 6
      lib/src/l10n/generated/quill_localizations_cs.dart
  44. 6
      lib/src/l10n/generated/quill_localizations_da.dart
  45. 6
      lib/src/l10n/generated/quill_localizations_de.dart
  46. 6
      lib/src/l10n/generated/quill_localizations_en.dart
  47. 6
      lib/src/l10n/generated/quill_localizations_es.dart
  48. 6
      lib/src/l10n/generated/quill_localizations_fa.dart
  49. 6
      lib/src/l10n/generated/quill_localizations_fr.dart
  50. 6
      lib/src/l10n/generated/quill_localizations_he.dart
  51. 6
      lib/src/l10n/generated/quill_localizations_hi.dart
  52. 6
      lib/src/l10n/generated/quill_localizations_id.dart
  53. 6
      lib/src/l10n/generated/quill_localizations_it.dart
  54. 6
      lib/src/l10n/generated/quill_localizations_ja.dart
  55. 6
      lib/src/l10n/generated/quill_localizations_ko.dart
  56. 6
      lib/src/l10n/generated/quill_localizations_ku.dart
  57. 6
      lib/src/l10n/generated/quill_localizations_ms.dart
  58. 6
      lib/src/l10n/generated/quill_localizations_ne.dart
  59. 6
      lib/src/l10n/generated/quill_localizations_nl.dart
  60. 6
      lib/src/l10n/generated/quill_localizations_no.dart
  61. 6
      lib/src/l10n/generated/quill_localizations_pl.dart
  62. 6
      lib/src/l10n/generated/quill_localizations_pt.dart
  63. 6
      lib/src/l10n/generated/quill_localizations_ro.dart
  64. 6
      lib/src/l10n/generated/quill_localizations_ru.dart
  65. 6
      lib/src/l10n/generated/quill_localizations_sk.dart
  66. 6
      lib/src/l10n/generated/quill_localizations_sr.dart
  67. 6
      lib/src/l10n/generated/quill_localizations_sv.dart
  68. 6
      lib/src/l10n/generated/quill_localizations_sw.dart
  69. 6
      lib/src/l10n/generated/quill_localizations_tk.dart
  70. 6
      lib/src/l10n/generated/quill_localizations_tr.dart
  71. 6
      lib/src/l10n/generated/quill_localizations_uk.dart
  72. 6
      lib/src/l10n/generated/quill_localizations_ur.dart
  73. 6
      lib/src/l10n/generated/quill_localizations_vi.dart
  74. 6
      lib/src/l10n/generated/quill_localizations_zh.dart
  75. 7
      lib/src/l10n/quill_en.arb
  76. 161
      lib/src/l10n/untranslated.json
  77. 5
      lib/src/l10n/widgets/localizations.dart
  78. 8
      lib/src/models/config/quill_controller_configurations.dart
  79. 17
      lib/src/models/documents/delta_x.dart
  80. 18
      lib/src/models/rules/insert.dart
  81. 2
      lib/src/packages/quill_markdown/markdown_to_delta.dart
  82. 80
      lib/src/utils/delta_x_utils.dart
  83. 3
      lib/src/widgets/editor/editor.dart
  84. 60
      lib/src/widgets/quill/text_line.dart
  85. 33
      lib/src/widgets/raw_editor/raw_editor_actions.dart
  86. 15
      lib/src/widgets/raw_editor/raw_editor_state.dart
  87. 1
      lib/src/widgets/toolbar/base_button/base_value_button.dart
  88. 3
      lib/src/widgets/toolbar/buttons/color/color_dialog.dart
  89. 4
      lib/src/widgets/toolbar/buttons/link_style2_button.dart
  90. 3
      lib/src/widgets/toolbar/buttons/link_style_button.dart
  91. 5
      lib/src/widgets/toolbar/buttons/toggle_style_button.dart
  92. 6
      pubspec.yaml
  93. 224
      quill_html_converter/CHANGELOG.md
  94. 2
      quill_html_converter/pubspec.yaml
  95. 224
      quill_pdf_converter/CHANGELOG.md
  96. 2
      quill_pdf_converter/pubspec.yaml
  97. 68
      scripts/create_version_content_from_github_release.dart
  98. 2
      scripts/ensure_translations_correct.dart
  99. 24
      scripts/regenerate_translations.dart
  100. 29
      scripts/regenerate_translations.sh
  101. Some files were not shown because too many files have changed in this diff Show More

@ -6,7 +6,7 @@ body:
- type: markdown
attributes:
value: |
Thank you for using Flutter Quill!
If you have any suggestions or questions, feel free to share them in the [Discussions](https://github.com/singerdmx/flutter-quill/discussions) section.
- type: checkboxes
attributes:
@ -21,4 +21,4 @@ body:
placeholder: |
How do I save the images of Quill Editor?
validations:
required: true
required: true

@ -0,0 +1 @@
blank_issues_enabled: false

@ -74,25 +74,11 @@ jobs:
id: release_tag
run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT
- name: 📑 Fetch release notes from Github API
id: fetch-release-notes-request
uses: fjogeleit/http-request-action@v1
with:
url: https://api.github.com/repos/${{ github.repository }}/releases/tags/${{ steps.release_tag.outputs.tag }}
method: 'GET'
customHeaders: '{"Authorization": "${{ secrets.GITHUB_TOKEN }}"}'
preventFailureOnNoResponse: 'false'
- name: Validate release notes response
run: |
releaseNotesResponse="${{ fromJson(steps.fetch-release-notes-request.outputs.response) }}"
if [[ -z "$releaseNotesResponse" || "$releaseNotesResponse" == "null" ]]; then
echo "Error: Release notes response is empty."
exit 1
fi
- name: 📑 Fetch release notes from Github API and create a required file by the next step
run: dart ./scripts/create_version_content_from_github_release.dart "${{ github.repository }}" "${{ steps.release_tag.outputs.tag }}"
- name: 📝 Update version and CHANGELOG for all the packages
run: dart ./scripts/update_package_version.dart ${{ steps.extract_version.outputs.VERSION }} '${{ fromJson(steps.fetch-release-notes-request.outputs.response).body }}'
run: dart ./scripts/update_package_version.dart ${{ steps.extract_version.outputs.VERSION }}
- name: 💾 Commit updated version and CHANGELOG
id: auto-commit-action

@ -4,6 +4,230 @@
All notable changes to this project will be documented in this file.
## 9.5.0
* Partial support for table embed by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/1960
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.9...v9.5.0
## 9.4.9
* Upgrade photo_view to 0.15.0 for flutter_quill_extensions by @singerdmx in https://github.com/singerdmx/flutter-quill/pull/1958
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.8...v9.4.9
## 9.4.8
* Add support for html underline and videos by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/1955
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.7...v9.4.8
## 9.4.7
* fixed #1953 italic detection error by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/1954
## New Contributors
* @CatHood0 made their first contribution in https://github.com/singerdmx/flutter-quill/pull/1954
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.6...v9.4.7
## 9.4.6
* fix: search dialog throw an exception due to missing FlutterQuillLocalizations.delegate in the editor by @ellet0 in https://github.com/singerdmx/flutter-quill/pull/1938
* fix(editor): implement editor shortcut action for home and end keys to fix exception about unimplemented ScrollToDocumentBoundaryIntent by @ellet0 in https://github.com/singerdmx/flutter-quill/pull/1937
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.5...v9.4.6
## 9.4.5
* fix: color picker hex unfocus on web by @geronimol in https://github.com/singerdmx/flutter-quill/pull/1934
## New Contributors
* @geronimol made their first contribution in https://github.com/singerdmx/flutter-quill/pull/1934
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.4...v9.4.5
## 9.4.4
* fix: Enabled link regex to be overridden by @JoepHeijnen in https://github.com/singerdmx/flutter-quill/pull/1931
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.3...v9.4.4
## 9.4.3
* Fix: setState() called after dispose(): QuillToolbarClipboardButtonState #1895 by @windows7lake in https://github.com/singerdmx/flutter-quill/pull/1926
## New Contributors
* @windows7lake made their first contribution in https://github.com/singerdmx/flutter-quill/pull/1926
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.2...v9.4.3
## 9.4.2
* Respect autofocus, closes #1923 by @Guillergood in https://github.com/singerdmx/flutter-quill/pull/1924
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.1...v9.4.2
## 9.4.1
* replace base64 regex string by @salba360496 in https://github.com/singerdmx/flutter-quill/pull/1919
## New Contributors
* @salba360496 made their first contribution in https://github.com/singerdmx/flutter-quill/pull/1919
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.0...v9.4.1
## 9.4.0
This release can be used without changing anything, although it can break the behavior a little, we provided a way to use the old behavior in `9.3.x`
- Thanks to @Alspb, the search bar/dialog has been reworked for improved UI that fits **Material 3** look and feel, the search happens on the fly, and other minor changes, if you want the old search bar, you can restore it with one line if you're using `QuillSimpleToolbar`:
```dart
QuillToolbar.simple(
configurations: QuillSimpleToolbarConfigurations(
searchButtonType: SearchButtonType.legacy,
),
)
```
While the changes are mostly to the `QuillToolbarSearchDialog` and it seems this should be `searchDialogType`, we provided the old button with the old dialog in case we update the button in the future.
If you're using `QuillToolbarSearchButton` in a custom Toolbar, you don't need anything to get the new button. if you want the old button, use the `QuillToolbarLegacySearchButton` widget
Consider using the improved button with the improved dialog as the legacy button might removed in future releases (for now, it's not deprecated)
<details>
<summary>Before</summary>
![image](https://github.com/singerdmx/flutter-quill/assets/73608287/9b40ad03-717f-4518-95f1-8d9cad773b2b)
</details>
<details>
<summary>Improved</summary>
![image](https://github.com/singerdmx/flutter-quill/assets/73608287/e581733d-63fa-4984-9c41-4a325a0a0c04)
</details>
For the detailed changes, see #1904
- Korean translations by @leegh519 in https://github.com/singerdmx/flutter-quill/pull/1911
- The usage of `super_clipboard` plugin in `flutter_quill` has been moved to the `flutter_quill_extensions` package, this will restore the old behavior in `8.x.x` though it will break the `onImagePaste`, `onGifPaste` and rich text pasting from HTML or Markdown, most of those features are available in `super_clipboard` plugin except `onImagePaste` which was available as we were using [pasteboard](https://pub.dev/packages/pasteboard), Unfortunately, it's no longer supported on recent versions of Flutter, and some functionalities such as an image from Clipboard and Html paste are not supported on some platforms such as Android, your project will continue to work, calls of `onImagePaste` and `onGifPaste` will be ignored unless you include [flutter_quill_extensions](https://pub.dev/packages/flutter_quill_extensions) package in your project and call:
```dart
FlutterQuillExtensions.useSuperClipboardPlugin();
```
Before using any `flutter_quill` widgets, this will restore the old behavior in `9.x.x`
We initially wanted to publish `flutter_quill_super_clipboard` to allow:
- Using `super_clipboard` without `flutter_quill_extensions` packages and plugins
- Using `flutter_quill_extensions` with optional `super_clipboard`
To simplify the usage, we moved it to `flutter_quill_extensions`, let us know if you want any of the use cases above.
Overall `super_clipboard` is a Comprehensive clipboard plugin with a lot of features, the only thing that developers didn't want is Rust installation even though it's automated.
The main goal of `ClipboardService` is to make `super_clipboard` optional, you can use your own implementation, and create a class that implements `ClipboardService`, which you can get by:
```dart
// ignore: implementation_imports
import 'package:flutter_quill/src/services/clipboard/clipboard_service.dart';
```
Then you can call:
```dart
// ignore: implementation_imports
import 'package:flutter_quill/src/services/clipboard/clipboard_service_provider.dart';
ClipboardServiceProvider.setInstance(YourClipboardService());
```
The interface could change at any time and will be updated internally for `flutter_quill` and `flutter_quill_extensions`, we didn't export those two classes by default to avoid breaking changes in case you use them as we might change them in the future.
If you use the above imports, you might get **breaking changes** in **non-breaking change releases**.
- Subscript and Superscript should now work for all languages and characters
The previous implementation required the Apple 'SF-Pro-Display-Regular.otf' font which is only licensed/permitted for use on Apple devices.
We have removed the Apple font from the example
- Allow pasting Markdown and HTML file content from the system to the editor
Before `9.4.x` if you try to copy an HTML or Markdown file, and paste it into the editor, you will get the file name in the editor
Copying an HTML file, or HTML content from apps and websites is different than copying plain text.
This is why this change requires `super_clipboard` implementation as this is platform-dependent:
```dart
FlutterQuillExtensions.useSuperClipboardPlugin();
```
as mentioned above.
The following example for copying a Markdown file:
<details>
<summary>Markdown File Content</summary>
```md
**Note**: This package supports converting from HTML back to Quill delta but it's experimental and used internally when pasting HTML content from the clipboard to the Quill Editor
You have two options:
1. Using [quill_html_converter](./quill_html_converter/) to convert to HTML, the package can convert the Quill delta to HTML well
(it uses [vsc_quill_delta_to_html](https://pub.dev/packages/vsc_quill_delta_to_html)), it is just a handy extension to do it more quickly
1. Another option is to use
[vsc_quill_delta_to_html](https://pub.dev/packages/vsc_quill_delta_to_html) to convert your document
to HTML.
This package has full support for all Quill operations—including images, videos, formulas,
tables, and mentions.
Conversion can be performed in vanilla Dart (i.e., server-side or CLI) or in Flutter.
It is a complete Dart part of the popular and mature [quill-delta-to-html](https://www.npmjs.com/package/quill-delta-to-html)
Typescript/Javascript package.
this package doesn't convert the HTML back to Quill Delta as far as we know
```
</details>
<details>
<summary>Before</summary>
![image](https://github.com/singerdmx/flutter-quill/assets/73608287/03f5ae20-796c-4e8b-8668-09a994211c1e)
</details>
<details>
<summary>After</summary>
![image](https://github.com/singerdmx/flutter-quill/assets/73608287/7e3a1987-36e7-4665-944a-add87d24e788)
</details>
Markdown, and HTML converting from and to Delta are **currently far from perfect**, the current implementation could improved a lot
however **it will likely not work like expected**, due to differences between HTML and Delta, see this [comment](https://github.com/slab/quill/issues/1551#issuecomment-311458570) for more info.
![Copying Markdown file into Flutter Quill Editor](https://github.com/singerdmx/flutter-quill/assets/73608287/63bd6ba6-cc49-4335-84dc-91a0fa5c95a9)
For more details see #1915
Using or converting to HTML or Markdown is highly experimental and shouldn't be used for production applications.
We use it internally as it is more suitable for our specific use case., copying content from external websites and pasting it into the editor
previously breaks the styles, while the current implementation is not ready, it provides a better user experience and doesn't have many downsides.
Feel free to report any bugs or feature requests at [Issues](https://github.com/singerdmx/flutter-quill/issues) or drop any suggestions and questions at [Discussions](https://github.com/singerdmx/flutter-quill/discussions)
## New Contributors
* @leegh519 made their first contribution in https://github.com/singerdmx/flutter-quill/pull/1911
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.3.21...v9.4.0
## 9.3.21
* fix: assertion failure for swipe typing and undo on Android by @crasowas in https://github.com/singerdmx/flutter-quill/pull/1898

File diff suppressed because one or more lines are too long

@ -2,19 +2,26 @@
First of all, we would like to thank you for your time and efforts on this project, we appreciate it
You can see tutorials online on how to contribute to any open source project, it's a simple process, and you can do it even if you are not Git expert, simply start by forking the repository, clone it, creating a new branch, make your changes and commit them, then push the branch to your fork, and you will get link to send a PR to the upstream repository
You can see tutorials online on how to contribute to any open source project, it's a simple process, and you can do it
even if you are not Git expert, simply start by forking the repository, clone it, create a new branch, make your
changes and commit them, then push the branch to your fork, and you will get link to send a PR to the upstream
repository
If you don't have anything specific in mind to improve or fix, you can take a look at the issues tab or take a look at the todos of the project, they all start with `TODO:` so you can search in your IDE or use the todos tab in the IDE
If you don't have anything specific in mind to improve or fix, you can take a look at the issues tab or take a look at
the todos of the project, they all start with `TODO:` so you can search in your IDE or use the todos tab in the IDE
You can also check the [Todo](./doc/todo.md) list or the issues if you want to
You can also check the [Todo](./doc/todo.md) list or the issues if you want to
> Make sure to not edit the `CHANGELOG.md` or the version in `pubspec.yaml` for any of the packages, this process will be automated by CI.
> Make sure to not edit the `CHANGELOG.md` or the version in `pubspec.yaml` for any of the packages, CI will automate
> this process.
## Requirements
- [Flutter SDK](https://docs.flutter.dev/get-started/install) which can be installed by following the instructions the provided link, also make sure to add it to your path so `flutter --version` and `dart --version` work
- [Intellij IDEA Community Edition](https://www.jetbrains.com/idea/download/) or [Android Studio](https://developer.android.com/studio) (with Dart and Flutter plugins) or use [VS Code](https://code.visualstudio.com/) (with Dart and flutter extensions)
- [Flutter SDK](https://docs.flutter.dev/get-started/install), which can be installed by following the instructions the
provided link, also make sure to add it to your path so `flutter --version` and `dart --version` work
- [IntelliJ IDEA Community Edition](https://www.jetbrains.com/idea/download/)
or [Android Studio](https://developer.android.com/studio) (with Dart and Flutter plugins) or
use [VS Code](https://code.visualstudio.com/) (with Dart and flutter extensions)
## Test your changes 🧪
@ -22,8 +29,10 @@ Make sure you have the [Requirement](#requirements) installed and configured cor
To test your changes:
1. Go to the [Example project](./example/) in [main.dart](./example/lib/main.dart) and run the project either by using your IDE or `flutter run`
2. Make sure to read the [Development Notes](./doc/development_notes.md) if you made certain changes or [Translations Page](./doc/translation.md) if you made changes to the translations of the package
1. Go to the [Example project](./example/) in [main.dart](./example/lib/main.dart) and run the project either by using
your IDE or `flutter run`
2. Make sure to read the [Development Notes](./doc/development_notes.md) if you made certain changes
or [Translations Page](./doc/translation.md) if you made changes to the translations of the package
## Steps to contributing
@ -35,24 +44,29 @@ You will need a GitHub account as well as Git installed and configured with your
```
git remote add upstream git@github.com:singerdmx/flutter-quill.git
```
4. Open the project with your favorite IDE, usually, we prefer to use Jetbrains IDEs, but since [VS Code](https://code.visualstudio.com) is more used and has more support for Dart, then we suggest using it if you want to.
4. Open the project with your favorite IDE, usually, we prefer to use Jetbrains IDEs, but
since [VS Code](https://code.visualstudio.com) is more used and has more support for Dart, then we suggest using it
if you want to.
5. Create a new git branch and switch to it using `git checkout -b`
6. Make your changes
7. If you are working on changes that depend on different libraries in the same repo, then in that directory copy `pubspec_overrides.yaml.disabled` which exists in all the packages (`flutter_quill_test` and `flutter_quill_extensions` etc...)
to `pubspec_overrides.yaml` which will be ignored by `.gitignore` and will be used by dart pub to override the libraries
7. If you are working on changes that depend on different libraries in the same repo, then in that directory
copy `pubspec_overrides.yaml.disabled` which exists in all the packages (`flutter_quill_test`
and `flutter_quill_extensions` etc...)
to `pubspec_overrides.yaml` which will be ignored by `.gitignore` and will be used by dart pub to override the
libraries
```
cp pubspec_overrides.yaml.disabled pubspec_overrides.yaml
```
or save some time with the following script:
or save some time with the following script:
<!-- TODO: We should update this script to dart instead -->
```
./scripts/enable_local_dev.sh
```
8. Test them in the [example](./example) and add changes in there if necessary
9. Run the following script if possible
```
./scripts/before_push.sh
```
9. Run the following script if possible
```
./scripts/before_push.sh
```
10. When you are done sending your pull request, run:
```
git add .
@ -62,10 +76,11 @@ to `pubspec_overrides.yaml` which will be ignored by `.gitignore` and will be us
this will push the new branch to your forked repository
11. Now you can send your pull request either by following the link that you will get in the command line or open your
forked repository. You will find an option to send the pull request, you can also
open the [Pull Requests](https://github.com/singerdmx/flutter-quill) tab and send new pull request
forked repository. You will find an option to send the pull request, you can also
open the [Pull Requests](https://github.com/singerdmx/flutter-quill) tab and send new pull request
12. Now, wait for the review, and we might ask you to make more changes, then run:
```
git add .
git commit -m "Your new commit message"

@ -1,7 +1,7 @@
# Flutter Quill
<p align="center" style="background-color:#282C34">
<img src="https://user-images.githubusercontent.com/10923085/119221946-2de89000-baf2-11eb-8285-68168a78c658.png" width="600px">
<img src="https://user-images.githubusercontent.com/10923085/119221946-2de89000-baf2-11eb-8285-68168a78c658.png" width="600px" alt="Flutter Quill">
</p>
<h1 align="center">A rich text editor for Flutter</h1>
@ -12,14 +12,23 @@
[![Watch on GitHub][github-forks-badge]][github-forks-link]
[license-badge]: https://img.shields.io/github/license/singerdmx/flutter-quill.svg?style=for-the-badge
[license-link]: ./LICENSE
[prs-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=for-the-badge
[prs-link]: https://github.com/singerdmx/flutter-quill/issues
[github-watch-badge]: https://img.shields.io/github/watchers/singerdmx/flutter-quill.svg?style=for-the-badge&logo=github&logoColor=ffffff
[github-watch-link]: https://github.com/singerdmx/flutter-quill/watchers
[github-star-badge]: https://img.shields.io/github/stars/singerdmx/flutter-quill.svg?style=for-the-badge&logo=github&logoColor=ffffff
[github-star-link]: https://github.com/singerdmx/flutter-quill/stargazers
[github-forks-badge]: https://img.shields.io/github/forks/singerdmx/flutter-quill.svg?style=for-the-badge&logo=github&logoColor=ffffff
[github-forks-link]: https://github.com/singerdmx/flutter-quill/network/members
---
@ -27,37 +36,38 @@
FlutterQuill is a rich text editor and a [Quill] component for [Flutter].
This library is a WYSIWYG (What You See Is What You Get) editor built
for the modern Android, iOS,
for the modern Android, iOS,
web and desktop platforms.
Check out our [Youtube Playlist] or [Code Introduction](./doc/code_introduction.md)
to take a detailed walkthrough of the code base.
You can join our [Slack Group] for discussion.
> If you are viewing this page from [pub.dev](https://pub.dev/) page, then you
might have some issues with opening some links, open it in the GitHub repo instead.
> might have some issues with opening some links, open it in the GitHub repo instead.
## 📚 Table of contents
## Table of contents
- [Flutter Quill](#flutter-quill)
- [Table of contents](#table-of-contents)
- [Screenshots](#screenshots)
- [Installation](#installation)
- [Platform Specific Configurations](#platform-specific-configurations)
- [Usage](#usage)
- [Migration](#migration)
- [Input / Output](#input--output)
- [Links](#links)
- [Configurations](#configurations)
- [Links](#links-1)
- [Font Family](#font-family)
- [Embed Blocks](#embed-blocks)
- [Using the embed blocks from `flutter_quill_extensions`](#using-the-embed-blocks-from-flutter_quill_extensions)
- [Links](#links-2)
- [Conversion to HTML](#conversion-to-html)
- [Translation](#translation)
- [Testing](#testing)
- [Contributors](#contributors)
## Screenshots
- [📚 Table of contents](#-table-of-contents)
- [📸 Screenshots](#-screenshots)
- [📦 Installation](#-installation)
- [🛠 Platform Specific Configurations](#-platform-specific-configurations)
- [🚀 Usage](#-usage)
- [🔄 Migration](#-migration)
- [🔤 Input / Output](#-input--output)
- [🔗 Links](#-links)
- [ Configurations](#-configurations)
- [🔗 Links](#-links-1)
- [🖋 Font Family](#-font-family)
- [📦 Embed Blocks](#-embed-blocks)
- [🛠 Using the embed blocks from `flutter_quill_extensions`](#-using-the-embed-blocks-from-flutter_quill_extensions)
- [🔗 Links](#-links-2)
- [🔄 Conversion to HTML](#-conversion-to-html)
- [🌐 Translation](#-translation)
- [🧪 Testing](#-testing)
- [👥 Contributors](#-contributors)
## 📸 Screenshots
<details>
<summary>Tap to show/hide screenshots</summary>
@ -71,7 +81,7 @@ might have some issues with opening some links, open it in the GitHub repo inste
</details>
## Installation
## 📦 Installation
```yaml
dependencies:
@ -84,26 +94,31 @@ dependencies:
dependencies:
flutter_quill:
git: https://github.com/singerdmx/flutter-quill.git
ref: v<latest-version-here>
```
> Using the latest version and reporting any issues you encounter on GitHub will greatly contribute to the improvement of the library.
> Your input and insights are valuable in shaping a stable and reliable version for all the developers. Thank you for being part of the open-source community!
> Using the latest version and reporting any issues you encounter on GitHub will greatly contribute to the improvement
> of the library.
> Your input and insights are valuable in shaping a stable and reliable version for all the developers. Thank you for
> being part of the open-source community!
>
## Platform Specific Configurations
## 🛠 Platform Specific Configurations
Before using the package, we must inform you the package uses the following plugins:
Before using the package, we must inform you the package use the following plugins:
```
url_launcher
flutter_keyboard_visibility
device_info_plus
```
1. [`url_launcher`](https://pub.dev/packages/url_launcher) to open links.
2. [`device_info_plus`](https://pub.dev/packages/device_info_plus) to view info about the current device.
3. [`flutter_keyboard_visibility`](https://pub.dev/packages/flutter_keyboard_visibility) to listen for keyboard visibility
changes.
All of them doesn't require any platform specific setup.
All of them don't require any platform-specific setup.
> Starting from Flutter Quill `9.4.x`, [super_clipboard](https://pub.dev/packages/super_clipboard) has been moved to [FlutterQuill Extensions], to use rich text features, support pasting images, gif files, take a look at `flutter_quill_extensions` Readme.
> Starting from Flutter Quill `9.4.x`, [super_clipboard](https://pub.dev/packages/super_clipboard) has been moved
> to [FlutterQuill Extensions], to use rich text pasting, support pasting images, and gif files, take a look
> at `flutter_quill_extensions` Readme.
## Usage
## 🚀 Usage
First, you need to instantiate a controller
@ -142,11 +157,12 @@ in most cases, it's better to.
Check out [Sample Page] for more advanced usage.
## Migration
## 🔄 Migration
Starting from version `8.0.0`
We have added [Migration Guide](/doc/migration.md) for migration from different versions
## Input / Output
## 🔤 Input / Output
This library uses [Quill Delta](https://quilljs.com/docs/delta/)
to represent the document content.
@ -170,7 +186,7 @@ final json = jsonEncode(_controller.document.toDelta().toJson());
You can then write this to storage.
To open a FlutterQuill editor with an existing JSON representation that you've previously stored,
To open a FlutterQuill editor with an existing JSON representation that you've previously stored,
you can do something like this:
```dart
@ -179,7 +195,7 @@ final json = jsonDecode(r'{"insert":"hello\n"}');
_controller.document = Document.fromJson(json);
```
### Links
### 🔗 Links
- [Quill Delta](https://quilljs.com/docs/delta/)
- [Quill Delta Formats](https://quilljs.com/docs/formats)
@ -188,76 +204,95 @@ _controller.document = Document.fromJson(json);
- [Quill JS Interactive Playground](https://quilljs.com/playground/)
- [Quill JS GitHub repo](https://github.com/quilljs/quill)
## Configurations
## Configurations
The `QuillToolbar` and `QuillEditor` widgets let you customize a lot of things
[Sample Page] provides sample code for advanced usage and configuration.
### Links
### 🔗 Links
- [Using Custom App Widget](./doc/configurations/using_custom_app_widget.md)
- [Localizations Setup](./doc/configurations/localizations_setup.md)
- [Font Size](./doc/configurations/font_size.md)
- [Font Family](#font-family)
- [Custom Toolbar buttons](./doc/configurations/custom_buttons.md)
### Font Family
### 🖋 Font Family
To use your own fonts, update your [Assets](./example/assets/fonts) folder and pass in `fontFamilyValues`.
More details
on [this commit](https://github.com/singerdmx/flutter-quill/commit/71d06f6b7be1b7b6dba2ea48e09fed0d7ff8bbaa),
[this article](https://stackoverflow.com/questions/55075834/fontfamily-property-not-working-properly-in-flutter)
and [this](https://www.flutterbeads.com/change-font-family-flutter/).
To use your own fonts, update your [assets folder](./example/assets/fonts) and pass in `fontFamilyValues`.
More details on [this commit](https://github.com/singerdmx/flutter-quill/commit/71d06f6b7be1b7b6dba2ea48e09fed0d7ff8bbaa),
[this article](https://stackoverflow.com/questions/55075834/fontfamily-property-not-working-properly-in-flutter) and [this](https://www.flutterbeads.com/change-font-family-flutter/).
## 📦 Embed Blocks
## Embed Blocks
As of version 6.0, embed blocks are not provided by default as part of this package.
As of version 6.0, embed blocks are not provided by default as part of this package. Instead, this package provides an interface for all the users to provide their own implementations for embed blocks. Implementations for image, video, and formula embed blocks are proved in a separate package [`flutter_quill_extensions`](https://pub.dev/packages/flutter_quill_extensions).
Instead, this package provides an interface for all the users to provide their own implementations for embed blocks.
Implementations for image, video, and
formula embed blocks are proved in a separate
package [`flutter_quill_extensions`](https://pub.dev/packages/flutter_quill_extensions).
Provide a list of embed
### Using the embed blocks from `flutter_quill_extensions`
### 🛠 Using the embed blocks from `flutter_quill_extensions`
To see how to use the extension package, please take a look at the [README](./flutter_quill_extensions/README.md) of [FlutterQuill Extensions]
To see how to use the extension package, please take a look at the [README](./flutter_quill_extensions/README.md)
of [FlutterQuill Extensions]
### Links
### 🔗 Links
- [Custom Embed Blocks](./doc/custom_embed_blocks.md)
- [Custom Toolbar](./doc/custom_toolbar.md)
## Conversion to HTML
## 🔄 Conversion to HTML
Having your document stored in Quill Delta format is sometimes not enough. Often you'll need to convert
it to other formats such as HTML to publish it, or send an email.
**Note**: This package supports converting from HTML back to Quill delta but it's experimental and used internally when pasting HTML content from the clipboard to the Quill Editor
**Note**: This package supports converting from HTML back to Quill delta but it's experimental and used internally when
pasting HTML content from the clipboard to the Quill Editor
You have two options:
1. Using [quill_html_converter](./quill_html_converter/) to convert to HTML, the package can convert the Quill delta to HTML well
(it uses [vsc_quill_delta_to_html](https://pub.dev/packages/vsc_quill_delta_to_html)), it is just a handy extension to do it more quickly
1. Another option is to use
[vsc_quill_delta_to_html](https://pub.dev/packages/vsc_quill_delta_to_html) to convert your document
to HTML.
1. Using [quill_html_converter](./quill_html_converter/) to convert to HTML, the package can convert the Quill delta to
HTML well
(it uses [vsc_quill_delta_to_html](https://pub.dev/packages/vsc_quill_delta_to_html)), it is just a handy extension
to do it more quickly
2. Another option is to use
[vsc_quill_delta_to_html](https://pub.dev/packages/vsc_quill_delta_to_html) to convert your document
to HTML.
This package has full support for all Quill operations—including images, videos, formulas,
tables, and mentions.
tables, and mentions.
Conversion can be performed in vanilla Dart (i.e., server-side or CLI) or in Flutter.
It is a complete Dart part of the popular and mature [quill-delta-to-html](https://www.npmjs.com/package/quill-delta-to-html)
Typescript/Javascript package.
It is a complete Dart part of the popular and
mature [quill-delta-to-html](https://www.npmjs.com/package/quill-delta-to-html)
Typescript/Javascript package.
this package doesn't convert the HTML back to Quill Delta as far as we know
## Translation
The package offers translations for the quill toolbar and editor, it will follow the system locale unless you set your own locale.
> **Converting to Delta from Markdown and HTML is highly experimental and shouldn't be used for production applications**, while the current implementation is far from perfect, it could improved a lot however **it will likely not work as expected**, due to differences between HTML and Delta, see this [comment](https://github.com/slab/quill/issues/1551#issuecomment-311458570) for more info.<br>
> We use it **internally** as it is more suitable for our specific use case, copying content from external websites and pasting it into the editor
previously breaks the styles, while the current implementation is not ready, it provides a better user experience and doesn't have many downsides.
## 🌐 Translation
The package offers translations for the quill toolbar and editor, it will follow the system locale unless you set your
own locale.
Open this [page](./doc/translation.md) for more info
## Testing
## 🧪 Testing
Please use [flutter_quill_test](https://pub.dev/packages/flutter_quill_test) for testing
## Contributors
## 👥 Contributors
- Special thanks to everyone who has contributed to this project...
<a href="https://github.com/singerdmx/flutter-quill/graphs/contributors">
<img src="https://contrib.rocks/image?repo=singerdmx/flutter-quill" />
<img src="https://contrib.rocks/image?repo=singerdmx/flutter-quill" alt="Contributors"/>
</a>
<br>
@ -269,20 +304,31 @@ Made with [contrib.rocks](https://contrib.rocks).
and contributors who put time and effort into everything including making all the libraries, tools, and the
information we rely on
- We are incredibly grateful to many individuals and organizations who have played a
role in the project. This includes the welcoming community, dedicated volunteers, talented developers and
contributors, and the creators of the open-source tools we rely on.
role in the project.
This includes the welcoming community, dedicated volunteers, talented developers and
contributors, and the creators of the open-source tools we rely on.
We welcome all contributions!
Please follow these guidelines when contributing to the project. See [CONTRIBUTING.md](./CONTRIBUTING.md) for more details. <br>
Please follow these guidelines when contributing to the project.
See [CONTRIBUTING.md](./CONTRIBUTING.md) for more details.
<br>
The [CONTRIBUTING.md](./CONTRIBUTING.md) has development notes, if you're planning on contributing to the package, please consider reading it.
The [CONTRIBUTING.md](./CONTRIBUTING.md) has development notes, if you're planning on contributing to the package,
please consider reading it.
[Quill]: https://quilljs.com/docs/formats
[Flutter]: https://github.com/flutter/flutter
[FlutterQuill]: https://pub.dev/packages/flutter_quill
[FlutterQuill Extensions]: https://pub.dev/packages/flutter_quill_extensions
[ReactQuill]: https://github.com/zenoamaro/react-quill
[Youtube Playlist]: https://youtube.com/playlist?list=PLbhaS_83B97vONkOAWGJrSXWX58et9zZ2
[Slack Group]: https://join.slack.com/t/bulletjournal1024/shared_invite/zt-fys7t9hi-ITVU5PGDen1rNRyCjdcQ2g
[Sample Page]: https://github.com/singerdmx/flutter-quill/blob/master/example/lib/screens/quill/quill_screen.dart

@ -4,6 +4,230 @@
All notable changes to this project will be documented in this file.
## 9.5.0
* Partial support for table embed by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/1960
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.9...v9.5.0
## 9.4.9
* Upgrade photo_view to 0.15.0 for flutter_quill_extensions by @singerdmx in https://github.com/singerdmx/flutter-quill/pull/1958
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.8...v9.4.9
## 9.4.8
* Add support for html underline and videos by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/1955
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.7...v9.4.8
## 9.4.7
* fixed #1953 italic detection error by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/1954
## New Contributors
* @CatHood0 made their first contribution in https://github.com/singerdmx/flutter-quill/pull/1954
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.6...v9.4.7
## 9.4.6
* fix: search dialog throw an exception due to missing FlutterQuillLocalizations.delegate in the editor by @ellet0 in https://github.com/singerdmx/flutter-quill/pull/1938
* fix(editor): implement editor shortcut action for home and end keys to fix exception about unimplemented ScrollToDocumentBoundaryIntent by @ellet0 in https://github.com/singerdmx/flutter-quill/pull/1937
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.5...v9.4.6
## 9.4.5
* fix: color picker hex unfocus on web by @geronimol in https://github.com/singerdmx/flutter-quill/pull/1934
## New Contributors
* @geronimol made their first contribution in https://github.com/singerdmx/flutter-quill/pull/1934
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.4...v9.4.5
## 9.4.4
* fix: Enabled link regex to be overridden by @JoepHeijnen in https://github.com/singerdmx/flutter-quill/pull/1931
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.3...v9.4.4
## 9.4.3
* Fix: setState() called after dispose(): QuillToolbarClipboardButtonState #1895 by @windows7lake in https://github.com/singerdmx/flutter-quill/pull/1926
## New Contributors
* @windows7lake made their first contribution in https://github.com/singerdmx/flutter-quill/pull/1926
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.2...v9.4.3
## 9.4.2
* Respect autofocus, closes #1923 by @Guillergood in https://github.com/singerdmx/flutter-quill/pull/1924
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.1...v9.4.2
## 9.4.1
* replace base64 regex string by @salba360496 in https://github.com/singerdmx/flutter-quill/pull/1919
## New Contributors
* @salba360496 made their first contribution in https://github.com/singerdmx/flutter-quill/pull/1919
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.0...v9.4.1
## 9.4.0
This release can be used without changing anything, although it can break the behavior a little, we provided a way to use the old behavior in `9.3.x`
- Thanks to @Alspb, the search bar/dialog has been reworked for improved UI that fits **Material 3** look and feel, the search happens on the fly, and other minor changes, if you want the old search bar, you can restore it with one line if you're using `QuillSimpleToolbar`:
```dart
QuillToolbar.simple(
configurations: QuillSimpleToolbarConfigurations(
searchButtonType: SearchButtonType.legacy,
),
)
```
While the changes are mostly to the `QuillToolbarSearchDialog` and it seems this should be `searchDialogType`, we provided the old button with the old dialog in case we update the button in the future.
If you're using `QuillToolbarSearchButton` in a custom Toolbar, you don't need anything to get the new button. if you want the old button, use the `QuillToolbarLegacySearchButton` widget
Consider using the improved button with the improved dialog as the legacy button might removed in future releases (for now, it's not deprecated)
<details>
<summary>Before</summary>
![image](https://github.com/singerdmx/flutter-quill/assets/73608287/9b40ad03-717f-4518-95f1-8d9cad773b2b)
</details>
<details>
<summary>Improved</summary>
![image](https://github.com/singerdmx/flutter-quill/assets/73608287/e581733d-63fa-4984-9c41-4a325a0a0c04)
</details>
For the detailed changes, see #1904
- Korean translations by @leegh519 in https://github.com/singerdmx/flutter-quill/pull/1911
- The usage of `super_clipboard` plugin in `flutter_quill` has been moved to the `flutter_quill_extensions` package, this will restore the old behavior in `8.x.x` though it will break the `onImagePaste`, `onGifPaste` and rich text pasting from HTML or Markdown, most of those features are available in `super_clipboard` plugin except `onImagePaste` which was available as we were using [pasteboard](https://pub.dev/packages/pasteboard), Unfortunately, it's no longer supported on recent versions of Flutter, and some functionalities such as an image from Clipboard and Html paste are not supported on some platforms such as Android, your project will continue to work, calls of `onImagePaste` and `onGifPaste` will be ignored unless you include [flutter_quill_extensions](https://pub.dev/packages/flutter_quill_extensions) package in your project and call:
```dart
FlutterQuillExtensions.useSuperClipboardPlugin();
```
Before using any `flutter_quill` widgets, this will restore the old behavior in `9.x.x`
We initially wanted to publish `flutter_quill_super_clipboard` to allow:
- Using `super_clipboard` without `flutter_quill_extensions` packages and plugins
- Using `flutter_quill_extensions` with optional `super_clipboard`
To simplify the usage, we moved it to `flutter_quill_extensions`, let us know if you want any of the use cases above.
Overall `super_clipboard` is a Comprehensive clipboard plugin with a lot of features, the only thing that developers didn't want is Rust installation even though it's automated.
The main goal of `ClipboardService` is to make `super_clipboard` optional, you can use your own implementation, and create a class that implements `ClipboardService`, which you can get by:
```dart
// ignore: implementation_imports
import 'package:flutter_quill/src/services/clipboard/clipboard_service.dart';
```
Then you can call:
```dart
// ignore: implementation_imports
import 'package:flutter_quill/src/services/clipboard/clipboard_service_provider.dart';
ClipboardServiceProvider.setInstance(YourClipboardService());
```
The interface could change at any time and will be updated internally for `flutter_quill` and `flutter_quill_extensions`, we didn't export those two classes by default to avoid breaking changes in case you use them as we might change them in the future.
If you use the above imports, you might get **breaking changes** in **non-breaking change releases**.
- Subscript and Superscript should now work for all languages and characters
The previous implementation required the Apple 'SF-Pro-Display-Regular.otf' font which is only licensed/permitted for use on Apple devices.
We have removed the Apple font from the example
- Allow pasting Markdown and HTML file content from the system to the editor
Before `9.4.x` if you try to copy an HTML or Markdown file, and paste it into the editor, you will get the file name in the editor
Copying an HTML file, or HTML content from apps and websites is different than copying plain text.
This is why this change requires `super_clipboard` implementation as this is platform-dependent:
```dart
FlutterQuillExtensions.useSuperClipboardPlugin();
```
as mentioned above.
The following example for copying a Markdown file:
<details>
<summary>Markdown File Content</summary>
```md
**Note**: This package supports converting from HTML back to Quill delta but it's experimental and used internally when pasting HTML content from the clipboard to the Quill Editor
You have two options:
1. Using [quill_html_converter](./quill_html_converter/) to convert to HTML, the package can convert the Quill delta to HTML well
(it uses [vsc_quill_delta_to_html](https://pub.dev/packages/vsc_quill_delta_to_html)), it is just a handy extension to do it more quickly
1. Another option is to use
[vsc_quill_delta_to_html](https://pub.dev/packages/vsc_quill_delta_to_html) to convert your document
to HTML.
This package has full support for all Quill operations—including images, videos, formulas,
tables, and mentions.
Conversion can be performed in vanilla Dart (i.e., server-side or CLI) or in Flutter.
It is a complete Dart part of the popular and mature [quill-delta-to-html](https://www.npmjs.com/package/quill-delta-to-html)
Typescript/Javascript package.
this package doesn't convert the HTML back to Quill Delta as far as we know
```
</details>
<details>
<summary>Before</summary>
![image](https://github.com/singerdmx/flutter-quill/assets/73608287/03f5ae20-796c-4e8b-8668-09a994211c1e)
</details>
<details>
<summary>After</summary>
![image](https://github.com/singerdmx/flutter-quill/assets/73608287/7e3a1987-36e7-4665-944a-add87d24e788)
</details>
Markdown, and HTML converting from and to Delta are **currently far from perfect**, the current implementation could improved a lot
however **it will likely not work like expected**, due to differences between HTML and Delta, see this [comment](https://github.com/slab/quill/issues/1551#issuecomment-311458570) for more info.
![Copying Markdown file into Flutter Quill Editor](https://github.com/singerdmx/flutter-quill/assets/73608287/63bd6ba6-cc49-4335-84dc-91a0fa5c95a9)
For more details see #1915
Using or converting to HTML or Markdown is highly experimental and shouldn't be used for production applications.
We use it internally as it is more suitable for our specific use case., copying content from external websites and pasting it into the editor
previously breaks the styles, while the current implementation is not ready, it provides a better user experience and doesn't have many downsides.
Feel free to report any bugs or feature requests at [Issues](https://github.com/singerdmx/flutter-quill/issues) or drop any suggestions and questions at [Discussions](https://github.com/singerdmx/flutter-quill/discussions)
## New Contributors
* @leegh519 made their first contribution in https://github.com/singerdmx/flutter-quill/pull/1911
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.3.21...v9.4.0
## 9.3.21
* fix: assertion failure for swipe typing and undo on Android by @crasowas in https://github.com/singerdmx/flutter-quill/pull/1898

@ -1,6 +1,6 @@
name: dart_quill_delta
description: A port of quill-js-delta from typescript to dart
version: 9.3.21
version: 9.5.0
homepage: https://github.com/singerdmx/flutter-quill/tree/master/dart_quill_delta/
repository: https://github.com/singerdmx/flutter-quill/tree/master/dart_quill_delta/
issue_tracker: https://github.com/singerdmx/flutter-quill/issues/

@ -1,6 +1,9 @@
# Custom `QuillToolbar` Buttons
# Custom `QuillToolbar` Buttons
You may add custom buttons to the _end_ of the toolbar, via the `customButtons` option, which is a `List` of `QuillToolbarCustomButtonOptions`.
You may add custom buttons to the _end_ of the toolbar, via the `customButtons` option, which is a `List`
of `QuillToolbarCustomButtonOptions`.
## Adding an Icon 🖌
To add an Icon, we should use a new `QuillToolbarCustomButtonOptions` class
@ -13,11 +16,13 @@ To add an Icon, we should use a new `QuillToolbarCustomButtonOptions` class
),
```
## Example Usage 📚
Each `QuillCustomButton` is used as part of the `customButtons` option as follows:
```dart
QuillToolbar(
configurations: QuillToolbarConfigurations(
QuillToolbar.simple(
configurations: QuillSimpleToolbarConfigurations(
customButtons: [
QuillToolbarCustomButtonOptions(
icon: const Icon(Icons.ac_unit),

@ -1,8 +1,12 @@
# Font Size
# 🔠 Font Size
Within the editor toolbar, a drop-down with font-sizing capabilities is available. This can be enabled or disabled with `showFontSize`.
Within the editor toolbar, a drop-down with font-sizing capabilities is available.
This can be enabled or disabled
with `showFontSize`.
When enabled, the default font-size values can be modified via _optional_ `fontSizeValues`. `fontSizeValues` accepts a `Map<String, String>` consisting of a `String` title for the font size and a `String` value for the font size. Example:
When enabled, the default font-size values can be modified via _optional_ `fontSizeValues`.
Accepts a `Map<String, String>` consisting of a `String` title for the font size and a `String` value for the font size.
Example:
```dart
fontSizeValues: const {'Small': '8', 'Medium': '24.5', 'Large': '46'}

@ -1,7 +1,7 @@
# Localizations Setup
in addition to the required delegates mentioned above in [Using custom app widget](./using_custom_app_widget.md)
# 🌍 Localizations Setup
In addition to the required delegates mentioned above in [Using custom app widget](./using_custom_app_widget.md), which are:
which are:
```dart
localizationsDelegates: const [
DefaultCupertinoLocalizations.delegate,
@ -9,17 +9,16 @@ localizationsDelegates: const [
DefaultWidgetsLocalizations.delegate,
],
```
which are used by official Flutter widgets
The library also needs the
Which are used by Flutter widgets.
📌 Note: The library also needs the `FlutterQuillLocalizations.delegate`:
```dart
// Required localizations delegates ...
FlutterQuillLocalizations.delegate
```
To offer the default localizations.
But **you don't have to** since we have wrapped the `QuillEditor` and `QuillToolbar` with `FlutterQuillLocalizationsWidget` which will check if it sets then it will go, if not, then it will be provided only for them, so it's not really required, but if you are overriding the `localizationsDelegates` you could also add the `FlutterQuillLocalizations.delegate`
which won't change anything
**You don't have to add this explicitly** because we have wrapped the `QuillEditor` and `QuillToolbar` with `FlutterQuillLocalizationsWidget`. This widget will check if the necessary localizations are set; if not, it will provide them only for these widgets. Therefore, it's not strictly required. However, if you are overriding the `localizationsDelegates`, you can also add the `FlutterQuillLocalizations.delegate`.
There are additional notes in the [Translation](../translation.md) section
📄 For additional notes, refer to the [Translation](../translation.md) section.

@ -1,14 +1,12 @@
# Using Custom App Widget
# 🛠 Using Custom App Widget
This project uses some adaptive widgets like `AdaptiveTextSelectionToolbar` which require the following delegates:
The project uses some adaptive widgets like `AdaptiveTextSelectionToolbar` which require the following delegates:
1. Default Material Localizations delegate
2. Default Cupertino Localizations delegate
3. Default Widgets Localizations delegate
You don't need to include those since they are defined by default
but if you are using a Custom app or you are overriding the `localizationsDelegates` in the App widget
then please make sure it includes those:
You don't need to include these since they are defined by default. However, if you are using a custom app or overriding the `localizationsDelegates` in the App widget, ensure it includes the following:
```dart
localizationsDelegates: const [
@ -18,8 +16,8 @@ localizationsDelegates: const [
],
```
You might need more depending on your use case, for example, if you are using custom localizations for your app, using a custom app widget like `FluentApp` from [FluentUI]
which will also need
You might need more depending on your use case. For example, if you are using custom localizations for your app with a custom app widget like `FluentApp` from [FluentUI], you will also need:
```dart
localizationsDelegates: const [
@ -29,8 +27,8 @@ localizationsDelegates: const [
],
```
Note: In the latest versions of `FluentApp` you no longer need to add the `localizationsDelegates` but this is just an example, for more [info](https://github.com/bdlukaa/fluent_ui/pull/946)
📌 Note: In recent versions of `FluentApp`, you no longer need to add the `localizationsDelegates`. This is just an example. For more information, refer to the [#946](https://github.com/bdlukaa/fluent_ui/pull/946).
There are additional notes on the [Localizations](./localizations_setup.md) page
📄 For additional notes, see the [Localizations](./localizations_setup.md) page.
[FluentUI]: https://pub.dev/packages/fluent_ui

@ -1,12 +1,16 @@
# 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 them.
An example is adding
notes to the text, or anything custom that you want to add in your text editor.
The only thing that you need is to add a `CustomBlockEmbed` and provide a builder for it to the `embedBuilders` parameter, to transform the data inside of the Custom Block into a widget!
The only thing that you need is to add a `CustomBlockEmbed` and provide a builder for it to the `embedBuilders`
parameter, to transform the data inside the Custom Block into a widget!
Here is an example:
Starting with the `CustomBlockEmbed`, here we extend it and add the methods that are useful for the 'Note' widget, which will be the `Document`, used by the `flutter_quill` to render the rich text.
Starting with the `CustomBlockEmbed`, here we extend it and add the methods that are useful for the 'Note' widget, which
will be the `Document`, used by the `flutter_quill` to render the rich text.
```dart
class NotesBlockEmbed extends CustomBlockEmbed {
@ -21,7 +25,8 @@ class NotesBlockEmbed extends CustomBlockEmbed {
}
```
After that, we need to map this "notes" type into a widget. In that case, I used a `ListTile` with a text to show the plain text resume of the note, and the `onTap` function to edit the note.
After that, we need to map this "notes" type into a widget. In that case, I used a `ListTile` with a text to show the
plain text resume of the note, and the `onTap` function to edit the note.
Don't forget to add this method to the `QuillEditor` after that!
```dart
@ -64,7 +69,11 @@ class NotesEmbedBuilder extends EmbedBuilder {
}
```
And finally, we write the function to add/edit this note. The `showDialog` function shows the QuillEditor to edit the note after the user ends the edition, we check if the document has something, and if it has, we add or edit the `NotesBlockEmbed` inside of a `BlockEmbed.custom` (this is a little detail that will not work if you don't pass the `CustomBlockEmbed` inside of a `BlockEmbed.custom`).
And finally, we write the function to add/edit this note.
The `showDialog` function shows the QuillEditor to edit the
note after the user ends the edition, we check if the document has something, and if it has, we add or edit
the `NotesBlockEmbed` inside of a `BlockEmbed.custom` (this is a little detail that will not work if you don't pass
the `CustomBlockEmbed` inside of a `BlockEmbed.custom`).
```dart
Future<void> _addEditNote(BuildContext context, {Document? document}) async {
@ -116,11 +125,12 @@ Future<void> _addEditNote(BuildContext context, {Document? document}) async {
}
```
And voila, we have a custom widget inside of the rich text editor!
And voilà, we have a custom widget inside the rich text editor!
<p float="left">
<img width="400" alt="1" src="https://i.imgur.com/yBTPYeS.png">
</p>
> 1. For more info and a video example, see the [PR of this feature](https://github.com/singerdmx/flutter-quill/pull/877)
> 1. For more info and a video example, see
the [PR of this feature](https://github.com/singerdmx/flutter-quill/pull/877)
> 2. For more details, check out [this YouTube video](https://youtu.be/pI5p5j7cfHc)

@ -1,13 +1,13 @@
# Custom Toolbar
If you want to use a custom toolbar but still want the support of this library
If you want to use a custom toolbar but still want the support of this library,
You can use the `QuillBaseToolbar` which is the base for the `QuillToolbar`
Example:
```dart
QuillToolbar(
configurations: const QuillToolbarConfigurations(
QuillToolbar.simple(
configurations: const QuillSimpleToolbarConfigurations(
buttonOptions: QuillToolbarButtonOptions(
base: QuillToolbarBaseButtonOptions(
globalIconSize: 20,
@ -112,4 +112,5 @@ QuillToolbar(
)
```
if you want a more customized toolbar feel free to create your own and use the `controller` to interact with the editor. checkout the `QuillToolbar` and the buttons inside it to see an example of how that will work
if you want a more customized toolbar feel free to create your own and use the `controller` to interact with the editor.
checkout the `QuillToolbar` and the buttons inside it to see an example of how that will work

@ -1,10 +1,22 @@
# Development notes
- When updating the translations or localizations in the app, please take a look at the [Translation](./translation.md) page as it has important notes in order to work, if you also add a feature that adds new localizations then you need to the instructions of it in order for the translations to take effect
- We use the same package version and `CHANGELOG.md` for all the packages, for more [details](https://github.com/singerdmx/flutter-quill/pull/1878), the process is automated. We have a script that will do the followings:
1. Generate the `CHANGELOG.md` files by `CHANGELOG_JSON.json` (source of data) and then paste them into all the packages we have (overwrite), you don't need to
manually change/update any of the mentioned files above, once a new GitHub release published, the CI will take the release notes from the release, pass the info to the
script, the release notes can be auto-generated by Github using a button, a descirptive PRs title would help but you don't have to since we can change it at anytime.
2. The script require the new version as an argument, you don't need to run the script manually, when a maintainer create a new tag and publish a new GitHub release, the publish workflow will extract the new version from the tag name, run the script (pass the extracted version as an argument), commit the changes and push them into the repository, the script will update the `version` property for all the packages so the `flutter pub publish` will use the new version for each package correctly.
- When updating the translations or localizations in the app, please take a look at the [Translation](./translation.md)
page as it has important notes to work.
If you also add a feature that adds new localizations, then you need it
to the instructions of it in order for the translations to take effect
- We use the same package version and `CHANGELOG.md` for all the packages, for
more [details](https://github.com/singerdmx/flutter-quill/pull/1878), the process is automated. We have a script that
will do the following:
1. Generate the `CHANGELOG.md` files by `CHANGELOG_JSON.json` (source of data) and then paste them into all the
packages we have (overwrite), you don't need to
manually change/update any of the mentioned files above, once a new GitHub release published, the CI will take
the release notes from the release, pass the info to the
script, the release notes can be auto-generated by GitHub using a button, a descriptive PRs title would help but
you don't have to since we can change it at anytime.
2. The script require the new version as an argument, you don't need to run the script manually, when a maintainer
create a new tag and publish a new GitHub release, the publish workflow will extract the new version from the tag
name, run the script (pass the extracted version as an argument), commit the changes and push them into the
repository, the script will update the `version` property for all the packages so the `flutter pub publish` will
use the new version for each package correctly.
the script will be used the CI and no need to run it manually
the script will be used the CI and no need to run it manually

@ -1,55 +0,0 @@
# Todo
This is a todo list page that added recently and will be updated soon.
## Table of contents
- [Todo](#todo)
- [Table of contents](#table-of-contents)
- [Flutter Quill](#flutter-quill)
- [Features](#features)
- [Improvemenets](#improvemenets)
- [Bugs](#bugs)
- [Flutter Quill Extensions](#flutter-quill-extensions)
- [Features](#features-1)
- [Improvemenets](#improvemenets-1)
- [Bugs](#bugs-1)
## Flutter Quill
### Features
- Add support for Text magnification feature, for more [info](https://github.com/singerdmx/flutter-quill/issues/1504)
- Provide a way to expose quills undo redo stacks, for more [info](https://github.com/singerdmx/flutter-quill/issues/1381)
- Add callback to the `QuillToolbarColorButton` for custom color picking logic
### Improvemenets
- Improve the Raw Quill Editor, for more [info](https://github.com/singerdmx/flutter-quill/issues/1509)
- Provide more support to all the platforms
- Extract the shared properties between `QuillRawEditorConfigurations` and `QuillEditorConfigurations`
- The todo in the this [commit](https://github.com/singerdmx/flutter-quill/commit/79597ea6425357795c0663588ac079665241f23a) needs to be checked
- use `maybeOf` and of instead `ofNotNull` in the providers to follow flutter offical convenstion, completly rework the providers and update the build context extensions
- Add line through to the text when the check point checked is true
- Change the color of the numbers and dots in ol/ul to match the ones in the item list
- Fix the bugs of the font family and font size
- Try to update Quill Html Converter
- When pasting a HTML text from cliboard by not using the context menu builder, the new logic won't work
- When selecting all text and paste HTML text, it will not replace the current text, instead it will add a text
- Add strike-through in checkbox text when the checkpoint is checked
- No more using of dynamic
- There is a bug here, the first character is not being formatted when choosing font family or font size and type in the editor
- Fix the toolbar and the toolbar buttons, rework some of them, for example missing tooltips
### Bugs
Empty for now.
Please go to the [issues](https://github.com/singerdmx/flutter-quill/issues)
## Flutter Quill Extensions
### Features
### Improvemenets
### Bugs

@ -1,6 +1,8 @@
# Translation
# 🌍 Translation
The package offers translations for the quill toolbar and editor, it will follow the locale that is defined in your `WidgetsApp` for example `MaterialApp` which usually follows the system locally unless you set your own locale with:
The package offers translations for the quill toolbar and editor, it will follow the locale that is defined in
your `WidgetsApp` for example `MaterialApp` which usually follows the system locally unless you set your own locale
with:
```dart
QuillToolbar.simple(
@ -23,46 +25,76 @@ Expanded(
)
```
Currently, translations are available for these 37 locales:
## 🌐 Supported Locales
* `Locale('en')`, `Locale('ar')`, `Locale('bn')`, `Locale('bs')`
* `Locale('cs')`, `Locale('de')`, `Locale('da')`, `Locale('fr')`
* `Locale('he')`, `Locale('zh', 'CN')`, `Locale('zh', 'HK')`
* `Locale('ko')`, `Locale('ku')`, `Locale('ku', 'CKB')`
* `Locale('ro', 'RO')`, `Locale('ru')`, `Locale('es')`, `Locale('tk')`, `Locale('tr')`
* `Locale('uk')`, `Locale('ur')`, `Locale('pt')`, `Locale('pl')`
* `Locale('vi')`, `Locale('id')`, `Locale('it')`, `Locale('ms')`
* `Locale('nl')`, `Locale('no')`, `Locale('ne', 'NP')`, `Locale('fa')`, `Locale('hi')`
* `Locale('sk')`, `Locale('sr')`, `Locale('sv')`, `Locale('sw')`, `Locale('ja')`
Currently, translations are available for these 41 locales:
#### Contributing to translations
* `Locale('en')`, `Locale('hi')`, `Locale('ku', 'CKB')`, `Locale('pt')`, `Locale('sr')`, `Locale('ur')`
* `Locale('bg')`, `Locale('en', 'US')`, `Locale('id')`, `Locale('ms')`, `Locale('pt', 'br')`, `Locale('sv')`, `Locale('vi')`
* `Locale('bn')`, `Locale('es')`, `Locale('it')`, `Locale('ne')`, `Locale('ro')`, `Locale('sw')`, `Locale('zh')`
* `Locale('cs')`, `Locale('fa')`, `Locale('ja')`, `Locale('nl')`, `Locale('ro', 'RO')`, `Locale('tk')`, `Locale('zh', 'CN')`
* `Locale('da')`, `Locale('fr')`, `Locale('ko')`, `Locale('no')`, `Locale('ru')`, `Locale('tr')`, `Locale('zh', 'HK')`
* `Locale('de')`, `Locale('he')`, `Locale('ku')`, `Locale('pl')`, `Locale('ar')`, `Locale('sk')`, `Locale('uk')`
The translation files are located in the [l10n folder](../lib/src/l10n/). Feel free to contribute your own translations, just copy the [English translations](../lib/src/l10n/quill_en.arb) map and replace the values with your translations.
## 📌 Contributing to translations
Add a new file in the l10n folder with the following name
`quill_${localName}.arb` for example `quill_de.arb`
The translation files are located in the [l10n](../lib/src/l10n/) folder. Feel free to contribute your own translations.
paste the English version and replace the values
You can take a look at the [untranslated.json](../lib/src/l10n/untranslated.json) file, which is a generated file that
tells you which keys with which locales haven't translated so you can find the missing easily.
Also, you can take a look at the [untranslated.json](../lib/src/l10n/untranslated.json) JSON file, which is a generated file that tells you which keys with which locales haven't translated so you can find the missings easily
<details>
<summary>Add new local</summary>
After you are done and want to test the changes, run the following in the root folder (preferred):
1. Create a new file in [l10n](../lib/src/l10n/) folder, with the following name`quill_${localName}.arb` for
example `quill_de.arb`
```
./scripts/regenerate_translations.sh
```
2. Copy the [Arb Template](../lib/src/l10n/quill_en.arb) file and paste it into your new file, replace the values with
your translations
3. Update [Supported Locales](#supported-locales) section in this page to update the supported translations for both the
number and the list
</details>
<details>
<summary>Update existing local</summary>
1. Navigate to [l10n](../lib/src/l10n/) folder
or (if you can't run the script for some reasons):
2. Find the existing local, let's say you want to update the Korean translations, it will be `quill_ko.arb`
3. Use [untranslated.json](../lib/src/l10n/untranslated.json) as a reference to find missing, update or add what you
want
to translate.
</details>
<br>
> We usually avoid **updating the existing value of a key in the template file without updating the key or creating a new
one**.
> This will not update the [untranslated.json](../lib/src/l10n/untranslated.json) correctly and will make it harder
for contributors to find missing or incomplete.
Once you finish, run the following script:
```bash
dart ./scripts/regenerate_translations.dart
```
Or (if you can't run the script for some reason):
```bash
flutter gen-l10n
dart fix --apply ./lib/src/l10n/generated
dart format ./lib/src/l10n/generated
```
This will generate the new dart files from the arb files in order to take effect, otherwise, you won't notice a difference
The script above will generate Dart files from the Arb files to test the changes and take effect, otherwise you
won't notice a difference.
> If you added or removed translations, make sure to update `_expectedTranslationKeysLength` variable in `./scripts/ensure_translations_correct.dart` <br>
> 🔧 If you added or removed translations in the template file, make sure to update `_expectedTranslationKeysLength`
> variable in [scripts/ensure_translations_correct.dart](../scripts/ensure_translations_correct.dart) <br>
> Otherwise you don't need to update it.
Then open a pull request so everyone can benefit from your translations!

@ -4,7 +4,7 @@
# This file should be version controlled and should not be manually edited.
version:
revision: "5dcb86f68f239346676ceb1ed1ea385bd215fba1"
revision: "761747bfc538b5af34aa0d3fac380f1bc331ec49"
channel: "stable"
project_type: app
@ -13,11 +13,11 @@ project_type: app
migration:
platforms:
- platform: root
create_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1
base_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1
- platform: linux
create_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1
base_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1
create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
- platform: web
create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
# User provided section

@ -288,6 +288,7 @@ class MyQuillToolbar extends StatelessWidget {
: onImageInsert,
),
),
tableButtonOptions: const QuillToolbarTableButtonOptions(),
),
),
);

@ -1,7 +1,7 @@
const quillVideosSample = [
{'insert': '\n'},
{
'insert': {'video': 'https://youtu.be/xz6_AlJkDPA'},
'insert': {'video': 'https://youtu.be/fq4N0hgOWzU?si=SqoY_bAZYnjCkUvn'},
'attributes': {
'width': '300',
'height': '300',
@ -10,10 +10,8 @@ const quillVideosSample = [
},
{'insert': '\n'},
{'insert': '\n'},
{'insert': 'And this is just a youtube video'},
{'insert': '\n'},
{
'insert': 'This sample is not complete.',
},
{'insert': 'The video above is a Youtube video.'},
{'insert': '\n'},
{'insert': '\n'},
];

@ -23,45 +23,24 @@
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="example">
<meta name="apple-mobile-web-app-title" content="Flutter Quill Example">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<title>example</title>
<title>Flutter Quill Example</title>
<link rel="manifest" href="manifest.json">
<!-- Croppie for `image_cropper` plugin -->
<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>
<!-- Croppie for `image_cropper` plugin -->
<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>
<!-- Requirement by flutter_inappwebview, for more info: https://pub.dev/packages/flutter_inappwebview#installation -->
<script type="application/javascript" src="/assets/packages/flutter_inappwebview_web/assets/web/web_support.js" defer></script>
<script>
// The value below is injected by flutter build, do not touch.
const serviceWorkerVersion = null;
</script>
<!-- This script adds the flutter initialization JS code -->
<script src="flutter.js" defer></script>
<!-- Requirement by flutter_inappwebview, for more info: https://pub.dev/packages/flutter_inappwebview#installation -->
<script type="application/javascript" src="/assets/packages/flutter_inappwebview_web/assets/web/web_support.js" defer></script>
</head>
<body>
<script>
window.addEventListener('load', function(ev) {
// Download main.dart.js
_flutter.loader.loadEntrypoint({
serviceWorker: {
serviceWorkerVersion: serviceWorkerVersion,
},
onEntrypointLoaded: function(engineInitializer) {
engineInitializer.initializeEngine().then(function(appRunner) {
appRunner.runApp();
});
}
});
});
</script>
<script src="flutter_bootstrap.js" async></script>
</body>
</html>

@ -1,5 +1,5 @@
{
"name": "example",
"name": "Flutter Quill Example",
"short_name": "example",
"start_url": ".",
"display": "standalone",

@ -4,6 +4,230 @@
All notable changes to this project will be documented in this file.
## 9.5.0
* Partial support for table embed by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/1960
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.9...v9.5.0
## 9.4.9
* Upgrade photo_view to 0.15.0 for flutter_quill_extensions by @singerdmx in https://github.com/singerdmx/flutter-quill/pull/1958
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.8...v9.4.9
## 9.4.8
* Add support for html underline and videos by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/1955
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.7...v9.4.8
## 9.4.7
* fixed #1953 italic detection error by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/1954
## New Contributors
* @CatHood0 made their first contribution in https://github.com/singerdmx/flutter-quill/pull/1954
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.6...v9.4.7
## 9.4.6
* fix: search dialog throw an exception due to missing FlutterQuillLocalizations.delegate in the editor by @ellet0 in https://github.com/singerdmx/flutter-quill/pull/1938
* fix(editor): implement editor shortcut action for home and end keys to fix exception about unimplemented ScrollToDocumentBoundaryIntent by @ellet0 in https://github.com/singerdmx/flutter-quill/pull/1937
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.5...v9.4.6
## 9.4.5
* fix: color picker hex unfocus on web by @geronimol in https://github.com/singerdmx/flutter-quill/pull/1934
## New Contributors
* @geronimol made their first contribution in https://github.com/singerdmx/flutter-quill/pull/1934
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.4...v9.4.5
## 9.4.4
* fix: Enabled link regex to be overridden by @JoepHeijnen in https://github.com/singerdmx/flutter-quill/pull/1931
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.3...v9.4.4
## 9.4.3
* Fix: setState() called after dispose(): QuillToolbarClipboardButtonState #1895 by @windows7lake in https://github.com/singerdmx/flutter-quill/pull/1926
## New Contributors
* @windows7lake made their first contribution in https://github.com/singerdmx/flutter-quill/pull/1926
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.2...v9.4.3
## 9.4.2
* Respect autofocus, closes #1923 by @Guillergood in https://github.com/singerdmx/flutter-quill/pull/1924
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.1...v9.4.2
## 9.4.1
* replace base64 regex string by @salba360496 in https://github.com/singerdmx/flutter-quill/pull/1919
## New Contributors
* @salba360496 made their first contribution in https://github.com/singerdmx/flutter-quill/pull/1919
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.0...v9.4.1
## 9.4.0
This release can be used without changing anything, although it can break the behavior a little, we provided a way to use the old behavior in `9.3.x`
- Thanks to @Alspb, the search bar/dialog has been reworked for improved UI that fits **Material 3** look and feel, the search happens on the fly, and other minor changes, if you want the old search bar, you can restore it with one line if you're using `QuillSimpleToolbar`:
```dart
QuillToolbar.simple(
configurations: QuillSimpleToolbarConfigurations(
searchButtonType: SearchButtonType.legacy,
),
)
```
While the changes are mostly to the `QuillToolbarSearchDialog` and it seems this should be `searchDialogType`, we provided the old button with the old dialog in case we update the button in the future.
If you're using `QuillToolbarSearchButton` in a custom Toolbar, you don't need anything to get the new button. if you want the old button, use the `QuillToolbarLegacySearchButton` widget
Consider using the improved button with the improved dialog as the legacy button might removed in future releases (for now, it's not deprecated)
<details>
<summary>Before</summary>
![image](https://github.com/singerdmx/flutter-quill/assets/73608287/9b40ad03-717f-4518-95f1-8d9cad773b2b)
</details>
<details>
<summary>Improved</summary>
![image](https://github.com/singerdmx/flutter-quill/assets/73608287/e581733d-63fa-4984-9c41-4a325a0a0c04)
</details>
For the detailed changes, see #1904
- Korean translations by @leegh519 in https://github.com/singerdmx/flutter-quill/pull/1911
- The usage of `super_clipboard` plugin in `flutter_quill` has been moved to the `flutter_quill_extensions` package, this will restore the old behavior in `8.x.x` though it will break the `onImagePaste`, `onGifPaste` and rich text pasting from HTML or Markdown, most of those features are available in `super_clipboard` plugin except `onImagePaste` which was available as we were using [pasteboard](https://pub.dev/packages/pasteboard), Unfortunately, it's no longer supported on recent versions of Flutter, and some functionalities such as an image from Clipboard and Html paste are not supported on some platforms such as Android, your project will continue to work, calls of `onImagePaste` and `onGifPaste` will be ignored unless you include [flutter_quill_extensions](https://pub.dev/packages/flutter_quill_extensions) package in your project and call:
```dart
FlutterQuillExtensions.useSuperClipboardPlugin();
```
Before using any `flutter_quill` widgets, this will restore the old behavior in `9.x.x`
We initially wanted to publish `flutter_quill_super_clipboard` to allow:
- Using `super_clipboard` without `flutter_quill_extensions` packages and plugins
- Using `flutter_quill_extensions` with optional `super_clipboard`
To simplify the usage, we moved it to `flutter_quill_extensions`, let us know if you want any of the use cases above.
Overall `super_clipboard` is a Comprehensive clipboard plugin with a lot of features, the only thing that developers didn't want is Rust installation even though it's automated.
The main goal of `ClipboardService` is to make `super_clipboard` optional, you can use your own implementation, and create a class that implements `ClipboardService`, which you can get by:
```dart
// ignore: implementation_imports
import 'package:flutter_quill/src/services/clipboard/clipboard_service.dart';
```
Then you can call:
```dart
// ignore: implementation_imports
import 'package:flutter_quill/src/services/clipboard/clipboard_service_provider.dart';
ClipboardServiceProvider.setInstance(YourClipboardService());
```
The interface could change at any time and will be updated internally for `flutter_quill` and `flutter_quill_extensions`, we didn't export those two classes by default to avoid breaking changes in case you use them as we might change them in the future.
If you use the above imports, you might get **breaking changes** in **non-breaking change releases**.
- Subscript and Superscript should now work for all languages and characters
The previous implementation required the Apple 'SF-Pro-Display-Regular.otf' font which is only licensed/permitted for use on Apple devices.
We have removed the Apple font from the example
- Allow pasting Markdown and HTML file content from the system to the editor
Before `9.4.x` if you try to copy an HTML or Markdown file, and paste it into the editor, you will get the file name in the editor
Copying an HTML file, or HTML content from apps and websites is different than copying plain text.
This is why this change requires `super_clipboard` implementation as this is platform-dependent:
```dart
FlutterQuillExtensions.useSuperClipboardPlugin();
```
as mentioned above.
The following example for copying a Markdown file:
<details>
<summary>Markdown File Content</summary>
```md
**Note**: This package supports converting from HTML back to Quill delta but it's experimental and used internally when pasting HTML content from the clipboard to the Quill Editor
You have two options:
1. Using [quill_html_converter](./quill_html_converter/) to convert to HTML, the package can convert the Quill delta to HTML well
(it uses [vsc_quill_delta_to_html](https://pub.dev/packages/vsc_quill_delta_to_html)), it is just a handy extension to do it more quickly
1. Another option is to use
[vsc_quill_delta_to_html](https://pub.dev/packages/vsc_quill_delta_to_html) to convert your document
to HTML.
This package has full support for all Quill operations—including images, videos, formulas,
tables, and mentions.
Conversion can be performed in vanilla Dart (i.e., server-side or CLI) or in Flutter.
It is a complete Dart part of the popular and mature [quill-delta-to-html](https://www.npmjs.com/package/quill-delta-to-html)
Typescript/Javascript package.
this package doesn't convert the HTML back to Quill Delta as far as we know
```
</details>
<details>
<summary>Before</summary>
![image](https://github.com/singerdmx/flutter-quill/assets/73608287/03f5ae20-796c-4e8b-8668-09a994211c1e)
</details>
<details>
<summary>After</summary>
![image](https://github.com/singerdmx/flutter-quill/assets/73608287/7e3a1987-36e7-4665-944a-add87d24e788)
</details>
Markdown, and HTML converting from and to Delta are **currently far from perfect**, the current implementation could improved a lot
however **it will likely not work like expected**, due to differences between HTML and Delta, see this [comment](https://github.com/slab/quill/issues/1551#issuecomment-311458570) for more info.
![Copying Markdown file into Flutter Quill Editor](https://github.com/singerdmx/flutter-quill/assets/73608287/63bd6ba6-cc49-4335-84dc-91a0fa5c95a9)
For more details see #1915
Using or converting to HTML or Markdown is highly experimental and shouldn't be used for production applications.
We use it internally as it is more suitable for our specific use case., copying content from external websites and pasting it into the editor
previously breaks the styles, while the current implementation is not ready, it provides a better user experience and doesn't have many downsides.
Feel free to report any bugs or feature requests at [Issues](https://github.com/singerdmx/flutter-quill/issues) or drop any suggestions and questions at [Discussions](https://github.com/singerdmx/flutter-quill/discussions)
## New Contributors
* @leegh519 made their first contribution in https://github.com/singerdmx/flutter-quill/pull/1911
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.3.21...v9.4.0
## 9.3.21
* fix: assertion failure for swipe typing and undo on Android by @crasowas in https://github.com/singerdmx/flutter-quill/pull/1898

@ -1,39 +1,42 @@
# Flutter Quill Extensions
An extensions for [flutter_quill](https://pub.dev/packages/flutter_quill)
to support embedding widgets like images, formulas, videos, and more.
to support embedding widgets like images, formulas, videos, tables and more.
Check [Flutter Quill](https://github.com/singerdmx/flutter-quill) for details of use.
Check [Flutter Quill](https://github.com/singerdmx/flutter-quill) for details of use.
## Table of Contents
> The support for tables is currently limited and under development, more are changes expected to arrive. We are actively working on enhancing its functionality and usability. We appreciate your feedback as it is invaluable in helping us refine and expand this feature.
## 📚 Table of Contents
- [Flutter Quill Extensions](#flutter-quill-extensions)
- [Table of Contents](#table-of-contents)
- [About](#about)
- [Installation](#installation)
- [Platform Specific Configurations](#platform-specific-configurations)
- [Usage](#usage)
- [Configurations](#configurations)
- [Embed Blocks](#embed-blocks)
- [Element properties](#element-properties)
- [Custom Element properties](#custom-element-properties)
- [Image Assets](#image-assets)
- [Drag and drop feature](#drag-and-drop-feature)
- [Features](#features)
- [Contributing](#contributing)
- [Acknowledgments](#acknowledgments)
## About
Flutter Quill is a rich editor text. It'd allow you to customize a lot of things,
- [📚 Table of Contents](#-table-of-contents)
- [📝 About](#-about)
- [📦 Installation](#-installation)
- [🛠 Platform Specific Configurations](#-platform-specific-configurations)
- [🚀 Usage](#-usage)
- [ Configurations](#-configurations)
- [📦 Embed Blocks](#-embed-blocks)
- [🔍 Element properties](#-element-properties)
- [🔧 Custom Element properties](#-custom-element-properties)
- [🖼 Image Assets](#-image-assets)
- [🎯 Drag and drop feature](#-drag-and-drop-feature)
- [💡 Features](#-features)
- [🤝 Contributing](#-contributing)
- [🌟 Acknowledgments](#-acknowledgments)
## 📝 About
Flutter Quill is a rich editor text.
It'd allow you to customize a lot of things,
it has custom embed builders that allow you to render custom widgets in the editor <br>
this is an extension to extend its functionalities by adding more features like images, videos, and more
## Installation
This is an extension to extend its functionalities by adding more features like images, videos, and more
## 📦 Installation
Before starting using this package, please make sure to install
[flutter_quill](https://github.com/singerdmx/flutter-quill) package first and follow
[flutter_quill](https://github.com/singerdmx/flutter-quill) package first and follow
its usage instructions.
```yaml
@ -48,48 +51,71 @@ dependencies:
flutter_quill_extensions:
git: https://github.com/singerdmx/flutter-quill.git
path: flutter_quill_extensions
ref: v<latest-version-here>
```
## Platform Specific Configurations
## 🛠 Platform Specific Configurations
The package use the following plugins:
The package uses the following plugins:
1. [`gal`](https://github.com/natsuk4ze/) plugin to save images.
For this to work, you need to add the appropriate configurations
See <https://github.com/natsuk4ze/gal#-get-started> to add the needed lines.
1. [`image_picker`](https://pub.dev/packages/image_picker) plugin for picking images so please make sure to follow the instructions
2. [youtube_player_flutter](https://pub.dev/packages/youtube_player_flutter) plugin which uses [flutter_inappwebview](https://pub.dev/packages/flutter_inappwebview) which has requirement on web, please follow this [link](https://pub.dev/packages/flutter_inappwebview#installation) in order to setup the support for web
3. [image_picker](https://pub.dev/packages/image_picker) which also
requires some configurations, follow this [link](https://pub.dev/packages/image_picker#installation). It's needed for Android, iOS, and macOS, we must inform you that you can't pick photos using the camera on a desktop so make sure to handle that if you plan on adding support for the desktop, this may change in the future, and for more info follow this [link](https://pub.dev/packages/image_picker#windows-macos-and-linux)
4. [super_clipboard](https://pub.dev/packages/super_clipboard) which needs some setup on Android only, it's used to support copying images and pasting them into editor then you must setup it, open the page in pub.dev and read the `README.md` or click on this [link](https://pub.dev/packages/super_clipboard#android-support) to get the instructions.
For this to work, you need to add the appropriate configurations
See <https://github.com/natsuk4ze/gal#-get-started> to add the needed lines.
2. [`image_picker`](https://pub.dev/packages/image_picker) plugin for picking images so please make sure to follow the
instructions
3. [`youtube_player_flutter`](https://pub.dev/packages/youtube_player_flutter) plugin which
uses [`flutter_inappwebview`](https://pub.dev/packages/flutter_inappwebview) which has a requirement on web, please
follow this [link](https://pub.dev/packages/flutter_inappwebview#installation) to set up the support for web
4. [`image_picker`](https://pub.dev/packages/image_picker) which also
requires some configurations, follow this [link](https://pub.dev/packages/image_picker#installation).
It's needed for
Android, iOS, and macOS, we must inform you that you can't pick photos using the camera on a desktop so make sure to
handle that if you plan on adding support for the desktop, this may change in the future, and for more info follow
this [link](https://pub.dev/packages/image_picker#windows-macos-and-linux)
5. [`super_clipboard`](https://pub.dev/packages/super_clipboard) which needs some setup on Android only, it's used to
support copying images and pasting them into editor then you must set up it, open the page in pub.dev and read
the `README.md` or click on this [link](https://pub.dev/packages/super_clipboard#android-support) to get the
instructions.
The minSdkVersion is `23` as `super_clipboard` requires it
> For loading the image from the internet <br> <br>
> **Android**: you need to add permissions in `AndroidManifest.xml`, Follow this [Android Guide](https://developer.android.com/training/basics/network-ops/connecting) or [Flutter Networking](https://docs.flutter.dev/data-and-backend/networking#android) for more info, the internet permission is included by default only for debugging so you need to follow this link to add it in the release version too. you should allow loading images and videos only for the `https` protocol but if you want http too then you need to configure your Android application to accept `http` in the release mode, follow this [Android Cleartext / Plaintext HTTP](https://developer.android.com/privacy-and-security/risks/cleartext) page for more info. <br> <br>
> **macOS**: you need to include a key in your `Info.plist`, follow this [link](https://docs.flutter.dev/data-and-backend/networking#macos) to add the required configurations
> **Android**: you need to add permissions in `AndroidManifest.xml`, Follow
> this [Android Guide](https://developer.android.com/training/basics/network-ops/connecting)
> or [Flutter Networking](https://docs.flutter.dev/data-and-backend/networking#android) for more info, the internet
> permission is included by default only for debugging, you need to follow this link to add it in the release version
> too. you should allow loading images and videos only for the `https` protocol but if you want http too then you need
> to
> configure your Android application to accept `http` in the release mode, follow
> this [Android Cleartext / Plaintext HTTP](https://developer.android.com/privacy-and-security/risks/cleartext) page for
> more info. <br> <br>
> **macOS**: you need to include a key in your `Info.plist`, follow
> this [link](https://docs.flutter.dev/data-and-backend/networking#macos) to add the required configurations
>
## Usage
## 🚀 Usage
Start using the package in 3 steps:
1. Besure to to follow the [Installation](#installation) section.
2. This package already include `super_clipboard` and will be used internally in this package, to use it in `flutter_quill`, call this function before using any of the widgets or functionalities
1. Be sure to follow the [Installation](#installation) section.
2. This package already include `super_clipboard` and will be used internally in this package, to use it
in `flutter_quill`, call this function before using any of the widgets or functionalities
```dart
FlutterQuillExtensions.useSuperClipboardPlugin();
```
`super_clipboard` is comprehensive plugin that provide many clipboard features for reading and writing of rich text, images and other formats.
`super_clipboard` is comprehensive plugin that provides many clipboard features for reading and writing of rich text,
images and other formats.
Executing this function will allow `flutter_quill` to use modern rich text features to paste HTML and Markdown, support for Gif files, and other formats.
Executing this function will allow `flutter_quill` to use modern rich text features to paste HTML and Markdown,
support for Gif files, and other formats.
3. Set the `embedBuilders` and `embedToolbar` params in configurations of `QuillEditor` and `QuillToolbar` with the
values provided by this repository.
values provided by this repository.
**Quill Toolbar**:
**Quill Toolbar**:
```dart
QuillToolbar(
configurations: QuillToolbarConfigurations(
@ -98,7 +124,7 @@ Start using the package in 3 steps:
),
```
**Quill Editor**
**Quill Editor**
```dart
Expanded(
child: QuillEditor.basic(
@ -109,31 +135,35 @@ Start using the package in 3 steps:
)
```
## Configurations
## Configurations
### Embed Blocks
### 📦 Embed Blocks
As of version [flutter_quill](https://pub.dev/packages/flutter_quill) `6.0.x`, embed blocks are not provided by default as part of Flutter quill. Instead, it provides an interface for all the users to provide their implementations for embed blocks. Implementations for image, video, and formula embed blocks are proved in this package
As of version [flutter_quill](https://pub.dev/packages/flutter_quill) `6.0.x`, embed blocks are not provided by default
as part of Flutter quill.
Instead, it provides an interface for all the users to provide their implementations for embed
blocks.
Implementations for image, video, and formula embed blocks are proved in this package
The instructions for using the embed blocks are in the [Usage](#usage) section
### Element properties
### 🔍 Element properties
Currently the library has limitied support for the image and video properties
Currently, the library has limited support for the image and video properties,
and it supports only `width`, `height`, `margin`
```json
{
"insert": {
"image": "https://user-images.githubusercontent.com/122956/72955931-ccc07900-3d52-11ea-89b1-d468a6e2aa2b.png"
},
"attributes": {
"style":"width: 50px; height: 50px; margin: 10px;"
}
"insert": {
"image": "https://user-images.githubusercontent.com/122956/72955931-ccc07900-3d52-11ea-89b1-d468a6e2aa2b.png"
},
"attributes": {
"style": "width: 50px; height: 50px; margin: 10px;"
}
}
```
### Custom Element properties
### 🔧 Custom Element properties
Doesn't apply to official Quill JS
@ -141,18 +171,18 @@ Define flutterAlignment` as follows:
```json
{
"insert": {
"image": "https://user-images.githubusercontent.com/122956/72955931-ccc07900-3d52-11ea-89b1-d468a6e2aa2b.png"
},
"attributes":{
"style":"flutterAlignment: topLeft"
}
"insert": {
"image": "https://user-images.githubusercontent.com/122956/72955931-ccc07900-3d52-11ea-89b1-d468a6e2aa2b.png"
},
"attributes": {
"style": "flutterAlignment: topLeft"
}
}
```
This works for all platforms except Web
### Image Assets
### 🖼 Image Assets
If you want to use image assets in the Quill Editor, you need to make sure your assets folder is `assets` otherwise:
@ -172,22 +202,27 @@ QuillEditor.basic(
);
```
This info is needed by the package to check if it asset image to use the `AssetImage` provider
This info is needed by the package to check if its asset image to use the `AssetImage` provider
### 🎯 Drag and drop feature
### Drag and drop feature
Currently, the drag-and-drop feature is not officially supported, but you can achieve this very easily in the following steps:
Currently, the drag-and-drop feature is not officially supported, but you can achieve this very easily in the following
steps:
1. Drag and drop require native code, you can use any Flutter plugin you like, if you want a suggestion we recommend [desktop_drop](https://pub.dev/packages/desktop_drop), it was originally developed for desktop but it has support for the web as well as Android (that is not the case for iOS)
1. Drag and drop require native code, you can use any Flutter plugin you like, if you want a suggestion we
recommend [desktop_drop](https://pub.dev/packages/desktop_drop), it was originally developed for desktop.
It has support for the web as well as Android (that is not the case for iOS)
2. Add the dependency in your `pubspec.yaml` using the following command:
```yaml
flutter pub add desktop_drop
```
and import it with
and import it with
```dart
import 'package:desktop_drop/desktop_drop.dart';
```
3. in the configurations of `QuillEditor`, use the `builder` to wrap the editor with `DropTarget` which comes from `desktop_drop`
3. in the configurations of `QuillEditor`, use the `builder` to wrap the editor with `DropTarget` which comes
from `desktop_drop`
```dart
import 'package:flutter_quill_extensions/flutter_quill_extensions.dart';
@ -208,6 +243,7 @@ Currently, the drag-and-drop feature is not officially supported, but you can ac
)
```
4. Implement the `_onDragDone`, it depends on your use case but this is just a simple example
```dart
const List<String> imageFileExtensions = [
'.jpeg',
@ -247,23 +283,25 @@ OnDragDoneCallback get _onDragDone {
}
```
## Features
## 💡 Features
```markdown
## Features
— Easy to use and customizable
- Rich text, images and other formats
- Useful utilities and widgets
```
## Contributing
## 🤝 Contributing
We welcome contributions!
Please follow these guidelines when contributing to our project. See [CONTRIBUTING.md](../CONTRIBUTING.md) for more details.
Please follow these guidelines when contributing to our project. See [CONTRIBUTING.md](../CONTRIBUTING.md) for more
details.
## Acknowledgments
## 🌟 Acknowledgments
- Thanks to the [Flutter Team](https://flutter.dev/)
- Thanks to the welcoming community, the volunteers who helped along the journey, developers, contributors

@ -0,0 +1,75 @@
import 'dart:async';
import 'package:flutter/material.dart';
class TableCellWidget extends StatefulWidget {
const TableCellWidget({
required this.cellId,
required this.cellData,
required this.onUpdate,
required this.onTap,
super.key,
});
final String cellId;
final String cellData;
final Function(FocusNode node) onTap;
final Function(String data) onUpdate;
@override
State<TableCellWidget> createState() => _TableCellWidgetState();
}
class _TableCellWidgetState extends State<TableCellWidget> {
late final TextEditingController controller;
late final FocusNode node;
Timer? _debounce;
@override
void initState() {
controller = TextEditingController(text: widget.cellData);
node = FocusNode();
super.initState();
}
void _onTextChanged() {
if (!_debounce!.isActive) {
widget.onUpdate(controller.text);
return;
}
}
@override
void dispose() {
controller
..removeListener(_onTextChanged)
..dispose();
node.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
width: 40,
constraints: const BoxConstraints(
minHeight: 50,
),
padding: const EdgeInsets.only(left: 5, right: 5, top: 5),
child: TextFormField(
controller: controller,
focusNode: node,
keyboardType: TextInputType.multiline,
maxLines: null,
decoration: const InputDecoration.collapsed(hintText: ''),
onTap: () {
widget.onTap.call(node);
},
onTapAlwaysCalled: true,
onChanged: (value) {
_debounce = Timer(
const Duration(milliseconds: 900),
_onTextChanged,
);
},
),
);
}
}

@ -0,0 +1,234 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_quill/flutter_quill.dart';
import 'package:flutter_quill/quill_delta.dart';
import '../../../utils/quill_table_utils.dart';
import 'table_cell_embed.dart';
import 'table_models.dart';
class CustomTableEmbed extends CustomBlockEmbed {
const CustomTableEmbed(String value) : super(tableType, value);
static const String tableType = 'table';
static CustomTableEmbed fromDocument(Document document) =>
CustomTableEmbed(jsonEncode(document.toDelta().toJson()));
Document get document => Document.fromJson(jsonDecode(data));
}
//Embed builder
class QuillEditorTableEmbedBuilder extends EmbedBuilder {
@override
String get key => 'table';
@override
Widget build(
BuildContext context,
QuillController controller,
Embed node,
bool readOnly,
bool inline,
TextStyle textStyle,
) {
final tableData = node.value.data;
return TableWidget(
tableData: tableData,
controller: controller,
);
}
}
class TableWidget extends StatefulWidget {
const TableWidget({
required this.tableData,
required this.controller,
super.key,
});
final QuillController controller;
final Map<String, dynamic> tableData;
@override
State<TableWidget> createState() => _TableWidgetState();
}
class _TableWidgetState extends State<TableWidget> {
TableModel _tableModel = TableModel(columns: {}, rows: {});
String _selectedColumnId = '';
String _selectedRowId = '';
@override
void initState() {
_tableModel = TableModel.fromMap(widget.tableData);
super.initState();
}
void _addColumn() {
setState(() {
final id = '${_tableModel.columns.length + 1}';
final position = _tableModel.columns.length;
_tableModel.columns[id] = ColumnModel(id: id, position: position);
_tableModel.rows.forEach((key, row) {
row.cells[id] = '';
});
});
_updateTable();
}
void _addRow() {
setState(() {
final id = '${_tableModel.rows.length + 1}';
final cells = <String, String>{};
_tableModel.columns.forEach((key, column) {
cells[key] = '';
});
_tableModel.rows[id] = RowModel(id: id, cells: cells);
});
_updateTable();
}
void _removeColumn(String columnId) {
setState(() {
_tableModel.columns.remove(columnId);
_tableModel.rows.forEach((key, row) {
row.cells.remove(columnId);
});
if (_selectedRowId == _selectedColumnId) {
_selectedRowId = '';
}
_selectedColumnId = '';
});
_updateTable();
}
void _removeRow(String rowId) {
setState(() {
_tableModel.rows.remove(rowId);
_selectedRowId = '';
});
_updateTable();
}
void _updateCell(String columnId, String rowId, String data) {
setState(() {
_tableModel.rows[rowId]!.cells[columnId] = data;
});
_updateTable();
}
void _updateTable() {
WidgetsBinding.instance.addPostFrameCallback((_) {
final offset = getEmbedNode(
widget.controller,
widget.controller.selection.start,
).offset;
final delta = Delta()..insert({'table': _tableModel.toMap()});
widget.controller.replaceText(
offset,
1,
delta,
TextSelection.collapsed(
offset: offset,
),
);
});
}
@override
Widget build(BuildContext context) {
return Material(
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: Theme.of(context).textTheme.bodyMedium?.color ??
Colors.black)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
IconButton(
icon: const Icon(Icons.more_vert),
onPressed: () async {
final position = renderPosition(context);
await showMenu<TableOperation>(
context: context,
position: position,
items: [
const PopupMenuItem(
value: TableOperation.addColumn,
child: Text('Add column'),
),
const PopupMenuItem(
value: TableOperation.addRow,
child: Text('Add row'),
),
const PopupMenuItem(
value: TableOperation.removeColumn,
child: Text('Delete column'),
),
const PopupMenuItem(
value: TableOperation.removeRow,
child: Text('Delete row'),
),
]).then((value) {
if (value != null) {
if (value == TableOperation.addRow) {
_addRow();
}
if (value == TableOperation.addColumn) {
_addColumn();
}
if (value == TableOperation.removeColumn) {
_removeColumn(_selectedColumnId);
}
if (value == TableOperation.removeRow) {
_removeRow(_selectedRowId);
}
}
});
},
),
const Divider(
color: Colors.white,
height: 1,
),
Table(
border: const TableBorder.symmetric(
inside: BorderSide(color: Colors.white)),
children: _buildTableRows(),
),
],
),
),
);
}
List<TableRow> _buildTableRows() {
final rows = <TableRow>[];
_tableModel.rows.forEach((rowId, rowModel) {
final rowCells = <Widget>[];
final rowKey = rowId;
rowModel.cells.forEach((key, value) {
if (key != 'id') {
final columnId = key;
final data = value;
rowCells.add(TableCellWidget(
cellId: rowKey,
onTap: (node) {
setState(() {
_selectedColumnId = columnId;
_selectedRowId = rowModel.id;
});
},
cellData: data,
onUpdate: (data) => _updateCell(columnId, rowKey, data),
));
}
});
rows.add(TableRow(children: rowCells));
});
return rows;
}
}

@ -0,0 +1,85 @@
class TableModel {
TableModel({required this.columns, required this.rows});
factory TableModel.fromMap(Map<String, dynamic> json) {
return TableModel(
columns: (json['columns'] as Map<String, dynamic>).map(
(key, value) => MapEntry(
key,
ColumnModel.fromMap(
value,
),
),
),
rows: (json['rows'] as Map<String, dynamic>).map(
(key, value) => MapEntry(
key,
RowModel.fromMap(
value,
),
),
),
);
}
Map<String, ColumnModel> columns;
Map<String, RowModel> rows;
Map<String, dynamic> toMap() {
return {
'columns': columns.map(
(key, value) => MapEntry(
key,
value.toMap(),
),
),
'rows': rows.map(
(key, value) => MapEntry(
key,
value.toMap(),
),
),
};
}
}
class ColumnModel {
ColumnModel({required this.id, required this.position});
factory ColumnModel.fromMap(Map<String, dynamic> json) {
return ColumnModel(
id: json['id'],
position: json['position'],
);
}
String id;
int position;
Map<String, dynamic> toMap() {
return {
'id': id,
'position': position,
};
}
}
class RowModel {
// Key is column ID, value is cell content
RowModel({required this.id, required this.cells});
factory RowModel.fromMap(Map<String, dynamic> json) {
return RowModel(
id: json['id'],
cells: Map<String, String>.from(json['cells']),
);
}
String id;
Map<String, String> cells;
Map<String, dynamic> toMap() {
return {
'id': id,
'cells': cells,
};
}
}

@ -0,0 +1,113 @@
import 'package:flutter/material.dart';
import 'package:flutter_quill/flutter_quill.dart';
import 'package:flutter_quill/translations.dart';
import '../../../models/config/table/table_configurations.dart';
import '../../../utils/quill_table_utils.dart';
class QuillToolbarTableButton extends StatelessWidget {
const QuillToolbarTableButton({
required this.controller,
this.options = const QuillToolbarTableButtonOptions(),
super.key,
});
final QuillController controller;
final QuillToolbarTableButtonOptions options;
double _iconSize(BuildContext context) {
final baseFontSize = baseButtonExtraOptions(context)?.iconSize;
final iconSize = options.iconSize;
return iconSize ?? baseFontSize ?? kDefaultIconSize;
}
double _iconButtonFactor(BuildContext context) {
final baseIconFactor = baseButtonExtraOptions(context)?.iconButtonFactor;
final iconButtonFactor = options.iconButtonFactor;
return iconButtonFactor ?? baseIconFactor ?? kDefaultIconButtonFactor;
}
VoidCallback? _afterButtonPressed(BuildContext context) {
return options.afterButtonPressed ??
baseButtonExtraOptions(context)?.afterButtonPressed;
}
QuillIconTheme? _iconTheme(BuildContext context) {
return options.iconTheme ?? baseButtonExtraOptions(context)?.iconTheme;
}
QuillToolbarBaseButtonOptions? baseButtonExtraOptions(BuildContext context) {
return context.quillToolbarBaseButtonOptions;
}
IconData _iconData(BuildContext context) {
return options.iconData ??
baseButtonExtraOptions(context)?.iconData ??
Icons.table_chart;
}
String _tooltip(BuildContext context) {
return options.tooltip ??
baseButtonExtraOptions(context)?.tooltip ??
context.loc.insertTable;
}
void _sharedOnPressed(BuildContext context) {
_onPressedHandler(context);
_afterButtonPressed(context);
}
@override
Widget build(BuildContext context) {
final tooltip = _tooltip(context);
final iconSize = _iconSize(context);
final iconButtonFactor = _iconButtonFactor(context);
final iconData = _iconData(context);
final childBuilder =
options.childBuilder ?? baseButtonExtraOptions(context)?.childBuilder;
if (childBuilder != null) {
return childBuilder(
QuillToolbarTableButtonOptions(
afterButtonPressed: _afterButtonPressed(context),
iconData: iconData,
iconSize: iconSize,
iconButtonFactor: iconButtonFactor,
iconTheme: options.iconTheme,
tooltip: options.tooltip,
),
QuillToolbarTableButtonExtraOptions(
context: context,
controller: controller,
onPressed: () => _sharedOnPressed(context),
),
);
}
return QuillToolbarIconButton(
icon: Icon(
iconData,
size: iconButtonFactor * iconSize,
),
tooltip: tooltip,
isSelected: false,
onPressed: () => _sharedOnPressed(context),
iconTheme: _iconTheme(context),
);
}
Future<void> _onPressedHandler(BuildContext context) async {
final position = renderPosition(context);
await showMenu(context: context, position: position, items: [
const PopupMenuItem(value: 2, child: Text('2x2')),
const PopupMenuItem(value: 4, child: Text('4x4')),
const PopupMenuItem(value: 6, child: Text('6x6')),
]).then(
(value) {
if (value != null) {
insertTable(value, value, controller, ChangeSource.local);
}
},
);
}
}

@ -6,6 +6,8 @@ import 'embeds/image/editor/image_embed.dart';
import 'embeds/image/editor/image_web_embed.dart';
import 'embeds/image/toolbar/image_button.dart';
import 'embeds/others/camera_button/camera_button.dart';
import 'embeds/table/editor/table_embed.dart';
import 'embeds/table/toolbar/table_button.dart';
import 'embeds/video/editor/video_embed.dart';
import 'embeds/video/editor/video_web_embed.dart';
import 'embeds/video/toolbar/video_button.dart';
@ -13,6 +15,7 @@ import 'models/config/camera/camera_configurations.dart';
import 'models/config/image/editor/image_configurations.dart';
import 'models/config/image/toolbar/image_configurations.dart';
import 'models/config/media/media_button_configurations.dart';
import 'models/config/table/table_configurations.dart';
import 'models/config/video/editor/video_configurations.dart';
import 'models/config/video/editor/video_web_configurations.dart';
import 'models/config/video/toolbar/video_configurations.dart';
@ -61,6 +64,7 @@ class FlutterQuillEmbeds {
QuillEditorVideoEmbedBuilder(
configurations: videoEmbedConfigurations,
),
QuillEditorTableEmbedBuilder(),
];
}
@ -117,6 +121,10 @@ class FlutterQuillEmbeds {
QuillToolbarVideoButtonOptions? videoButtonOptions =
const QuillToolbarVideoButtonOptions(),
QuillToolbarCameraButtonOptions? cameraButtonOptions,
QuillToolbarTableButtonOptions? tableButtonOptions,
@Deprecated(
'Media button has been removed, the value of this parameter will be ignored',
)
QuillToolbarMediaButtonOptions? mediaButtonOptions,
}) =>
[
@ -138,18 +146,11 @@ class FlutterQuillEmbeds {
controller: controller,
options: cameraButtonOptions,
),
// if (mediaButtonOptions != null)
// (controller, toolbarIconSize, iconTheme, dialogTheme) =>
// QuillToolbarMediaButton(
// controller: mediaButtonOptions.controller ?? controller,
// options: mediaButtonOptions,
// ),
// Drop the support for formula button for now
// if (formulaButtonOptions != null)
// (controller, toolbarIconSize, iconTheme, dialogTheme) =>
// QuillToolbarFormulaButton(
// controller: formulaButtonOptions.controller ?? controller,
// options: formulaButtonOptions,
// ),
if (tableButtonOptions != null)
(controller, toolbarIconSize, iconTheme, dialogTheme) =>
QuillToolbarTableButton(
controller: controller,
options: tableButtonOptions,
),
];
}

@ -14,6 +14,10 @@ export 'embeds/image/editor/image_web_embed.dart';
export 'embeds/image/toolbar/image_button.dart';
export 'embeds/others/camera_button/camera_button.dart';
export 'embeds/others/media_button/media_button.dart';
export 'embeds/table/editor/table_cell_embed.dart';
export 'embeds/table/editor/table_embed.dart';
export 'embeds/table/editor/table_models.dart';
export 'embeds/table/toolbar/table_button.dart';
export 'embeds/unknown/editor/unknown_embed.dart';
export 'embeds/video/editor/video_embed.dart';
export 'embeds/video/editor/video_web_embed.dart';
@ -28,6 +32,7 @@ export 'models/config/image/editor/image_web_configurations.dart';
export 'models/config/image/toolbar/image_configurations.dart';
export 'models/config/media/media_button_configurations.dart';
export 'models/config/shared_configurations.dart';
export 'models/config/table/table_configurations.dart';
export 'models/config/video/editor/video_configurations.dart';
export 'models/config/video/editor/video_web_configurations.dart';
export 'models/config/video/toolbar/video_configurations.dart';

@ -0,0 +1,27 @@
import 'package:flutter_quill/flutter_quill.dart';
import 'package:meta/meta.dart' show immutable;
class QuillToolbarTableButtonExtraOptions
extends QuillToolbarBaseButtonExtraOptions {
const QuillToolbarTableButtonExtraOptions({
required super.controller,
required super.context,
required super.onPressed,
});
}
@immutable
class QuillToolbarTableButtonOptions extends QuillToolbarBaseButtonOptions<
QuillToolbarTableButtonOptions, QuillToolbarTableButtonExtraOptions> {
const QuillToolbarTableButtonOptions({
super.iconData,
super.iconSize,
super.iconButtonFactor,
/// specifies the tooltip text for the image button.
super.tooltip,
super.afterButtonPressed,
super.childBuilder,
super.iconTheme,
});
}

@ -1,5 +1,5 @@
RegExp base64RegExp = RegExp(
r'^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{4})$',
r'^(?:[A-Za-z0-9+\/][A-Za-z0-9+\/][A-Za-z0-9+\/][A-Za-z0-9+\/])*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{4})$',
);
final imageRegExp = RegExp(

@ -0,0 +1,85 @@
import 'package:flutter/widgets.dart'
show
BuildContext,
MediaQuery,
Offset,
Overlay,
Rect,
RelativeRect,
RenderBox,
Size,
TextSelection;
import 'package:flutter_quill/flutter_quill.dart';
import 'package:flutter_quill/quill_delta.dart';
enum TableOperation {
addColumn,
addRow,
removeColumn,
removeRow,
}
RelativeRect renderPosition(BuildContext context, [Size? size]) {
size ??= MediaQuery.sizeOf(context);
final overlay = Overlay.of(context).context.findRenderObject() as RenderBox;
final button = context.findRenderObject() as RenderBox;
final position = RelativeRect.fromRect(
Rect.fromPoints(
button.localToGlobal(const Offset(0, -65), ancestor: overlay),
button.localToGlobal(
button.size.bottomRight(Offset.zero) + const Offset(-50, 0),
ancestor: overlay),
),
Offset.zero & size * 0.40,
);
return position;
}
void insertTable(int rows, int columns, QuillController quillController,
ChangeSource? changeFrom) {
final tableData = _createTableData(rows, columns);
final delta = Delta()..insert({'table': tableData});
final selection = quillController.selection;
final replacedLength = selection.extentOffset - selection.baseOffset;
final newBaseOffset = selection.baseOffset;
final newExtentOffsetCandidate =
(selection.baseOffset + 1 - replacedLength).toInt();
final newExtentOffsetAdjusted =
newExtentOffsetCandidate < 0 ? 0 : newExtentOffsetCandidate;
quillController.replaceText(
newBaseOffset,
replacedLength,
delta,
TextSelection(
baseOffset: newBaseOffset, extentOffset: newExtentOffsetAdjusted),
);
}
Map<String, dynamic> _createTableData(int rows, int columns) {
// Crear el mapa para las columnas
final columnsData = <String, dynamic>{};
for (var col = 0; col < columns; col++) {
final columnId = '${col + 1}';
columnsData[columnId] = {'id': columnId, 'position': col};
}
// Crear el mapa para las filas
final rowsData = <String, dynamic>{};
for (var row = 0; row < rows; row++) {
final rowId = '${row + 1}';
rowsData[rowId] = {'id': rowId, 'cells': {}};
for (var col = 0; col < columns; col++) {
final columnId = '${col + 1}';
rowsData[rowId]['cells'][columnId] = '';
}
}
// Combinar las columnas y filas en una estructura de tabla
final tableData = <String, dynamic>{
'columns': columnsData,
'rows': rowsData,
};
return tableData;
}

@ -1,6 +1,6 @@
name: flutter_quill_extensions
description: Embed extensions for flutter_quill including image, video, formula and etc.
version: 9.3.21
version: 9.5.0
homepage: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_extensions/
repository: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_extensions/
issue_tracker: https://github.com/singerdmx/flutter-quill/issues/
@ -36,7 +36,7 @@ dependencies:
cross_file: ^0.3.3+6
flutter_quill: ^9.3.12
photo_view: ^0.14.0
photo_view: ^0.15.0
youtube_explode_dart: ^2.2.1
# Plugins
@ -54,4 +54,4 @@ dev_dependencies:
flutter_lints: ^4.0.0
flutter:
uses-material-design: true
uses-material-design: true

@ -4,6 +4,230 @@
All notable changes to this project will be documented in this file.
## 9.5.0
* Partial support for table embed by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/1960
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.9...v9.5.0
## 9.4.9
* Upgrade photo_view to 0.15.0 for flutter_quill_extensions by @singerdmx in https://github.com/singerdmx/flutter-quill/pull/1958
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.8...v9.4.9
## 9.4.8
* Add support for html underline and videos by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/1955
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.7...v9.4.8
## 9.4.7
* fixed #1953 italic detection error by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/1954
## New Contributors
* @CatHood0 made their first contribution in https://github.com/singerdmx/flutter-quill/pull/1954
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.6...v9.4.7
## 9.4.6
* fix: search dialog throw an exception due to missing FlutterQuillLocalizations.delegate in the editor by @ellet0 in https://github.com/singerdmx/flutter-quill/pull/1938
* fix(editor): implement editor shortcut action for home and end keys to fix exception about unimplemented ScrollToDocumentBoundaryIntent by @ellet0 in https://github.com/singerdmx/flutter-quill/pull/1937
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.5...v9.4.6
## 9.4.5
* fix: color picker hex unfocus on web by @geronimol in https://github.com/singerdmx/flutter-quill/pull/1934
## New Contributors
* @geronimol made their first contribution in https://github.com/singerdmx/flutter-quill/pull/1934
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.4...v9.4.5
## 9.4.4
* fix: Enabled link regex to be overridden by @JoepHeijnen in https://github.com/singerdmx/flutter-quill/pull/1931
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.3...v9.4.4
## 9.4.3
* Fix: setState() called after dispose(): QuillToolbarClipboardButtonState #1895 by @windows7lake in https://github.com/singerdmx/flutter-quill/pull/1926
## New Contributors
* @windows7lake made their first contribution in https://github.com/singerdmx/flutter-quill/pull/1926
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.2...v9.4.3
## 9.4.2
* Respect autofocus, closes #1923 by @Guillergood in https://github.com/singerdmx/flutter-quill/pull/1924
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.1...v9.4.2
## 9.4.1
* replace base64 regex string by @salba360496 in https://github.com/singerdmx/flutter-quill/pull/1919
## New Contributors
* @salba360496 made their first contribution in https://github.com/singerdmx/flutter-quill/pull/1919
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.0...v9.4.1
## 9.4.0
This release can be used without changing anything, although it can break the behavior a little, we provided a way to use the old behavior in `9.3.x`
- Thanks to @Alspb, the search bar/dialog has been reworked for improved UI that fits **Material 3** look and feel, the search happens on the fly, and other minor changes, if you want the old search bar, you can restore it with one line if you're using `QuillSimpleToolbar`:
```dart
QuillToolbar.simple(
configurations: QuillSimpleToolbarConfigurations(
searchButtonType: SearchButtonType.legacy,
),
)
```
While the changes are mostly to the `QuillToolbarSearchDialog` and it seems this should be `searchDialogType`, we provided the old button with the old dialog in case we update the button in the future.
If you're using `QuillToolbarSearchButton` in a custom Toolbar, you don't need anything to get the new button. if you want the old button, use the `QuillToolbarLegacySearchButton` widget
Consider using the improved button with the improved dialog as the legacy button might removed in future releases (for now, it's not deprecated)
<details>
<summary>Before</summary>
![image](https://github.com/singerdmx/flutter-quill/assets/73608287/9b40ad03-717f-4518-95f1-8d9cad773b2b)
</details>
<details>
<summary>Improved</summary>
![image](https://github.com/singerdmx/flutter-quill/assets/73608287/e581733d-63fa-4984-9c41-4a325a0a0c04)
</details>
For the detailed changes, see #1904
- Korean translations by @leegh519 in https://github.com/singerdmx/flutter-quill/pull/1911
- The usage of `super_clipboard` plugin in `flutter_quill` has been moved to the `flutter_quill_extensions` package, this will restore the old behavior in `8.x.x` though it will break the `onImagePaste`, `onGifPaste` and rich text pasting from HTML or Markdown, most of those features are available in `super_clipboard` plugin except `onImagePaste` which was available as we were using [pasteboard](https://pub.dev/packages/pasteboard), Unfortunately, it's no longer supported on recent versions of Flutter, and some functionalities such as an image from Clipboard and Html paste are not supported on some platforms such as Android, your project will continue to work, calls of `onImagePaste` and `onGifPaste` will be ignored unless you include [flutter_quill_extensions](https://pub.dev/packages/flutter_quill_extensions) package in your project and call:
```dart
FlutterQuillExtensions.useSuperClipboardPlugin();
```
Before using any `flutter_quill` widgets, this will restore the old behavior in `9.x.x`
We initially wanted to publish `flutter_quill_super_clipboard` to allow:
- Using `super_clipboard` without `flutter_quill_extensions` packages and plugins
- Using `flutter_quill_extensions` with optional `super_clipboard`
To simplify the usage, we moved it to `flutter_quill_extensions`, let us know if you want any of the use cases above.
Overall `super_clipboard` is a Comprehensive clipboard plugin with a lot of features, the only thing that developers didn't want is Rust installation even though it's automated.
The main goal of `ClipboardService` is to make `super_clipboard` optional, you can use your own implementation, and create a class that implements `ClipboardService`, which you can get by:
```dart
// ignore: implementation_imports
import 'package:flutter_quill/src/services/clipboard/clipboard_service.dart';
```
Then you can call:
```dart
// ignore: implementation_imports
import 'package:flutter_quill/src/services/clipboard/clipboard_service_provider.dart';
ClipboardServiceProvider.setInstance(YourClipboardService());
```
The interface could change at any time and will be updated internally for `flutter_quill` and `flutter_quill_extensions`, we didn't export those two classes by default to avoid breaking changes in case you use them as we might change them in the future.
If you use the above imports, you might get **breaking changes** in **non-breaking change releases**.
- Subscript and Superscript should now work for all languages and characters
The previous implementation required the Apple 'SF-Pro-Display-Regular.otf' font which is only licensed/permitted for use on Apple devices.
We have removed the Apple font from the example
- Allow pasting Markdown and HTML file content from the system to the editor
Before `9.4.x` if you try to copy an HTML or Markdown file, and paste it into the editor, you will get the file name in the editor
Copying an HTML file, or HTML content from apps and websites is different than copying plain text.
This is why this change requires `super_clipboard` implementation as this is platform-dependent:
```dart
FlutterQuillExtensions.useSuperClipboardPlugin();
```
as mentioned above.
The following example for copying a Markdown file:
<details>
<summary>Markdown File Content</summary>
```md
**Note**: This package supports converting from HTML back to Quill delta but it's experimental and used internally when pasting HTML content from the clipboard to the Quill Editor
You have two options:
1. Using [quill_html_converter](./quill_html_converter/) to convert to HTML, the package can convert the Quill delta to HTML well
(it uses [vsc_quill_delta_to_html](https://pub.dev/packages/vsc_quill_delta_to_html)), it is just a handy extension to do it more quickly
1. Another option is to use
[vsc_quill_delta_to_html](https://pub.dev/packages/vsc_quill_delta_to_html) to convert your document
to HTML.
This package has full support for all Quill operations—including images, videos, formulas,
tables, and mentions.
Conversion can be performed in vanilla Dart (i.e., server-side or CLI) or in Flutter.
It is a complete Dart part of the popular and mature [quill-delta-to-html](https://www.npmjs.com/package/quill-delta-to-html)
Typescript/Javascript package.
this package doesn't convert the HTML back to Quill Delta as far as we know
```
</details>
<details>
<summary>Before</summary>
![image](https://github.com/singerdmx/flutter-quill/assets/73608287/03f5ae20-796c-4e8b-8668-09a994211c1e)
</details>
<details>
<summary>After</summary>
![image](https://github.com/singerdmx/flutter-quill/assets/73608287/7e3a1987-36e7-4665-944a-add87d24e788)
</details>
Markdown, and HTML converting from and to Delta are **currently far from perfect**, the current implementation could improved a lot
however **it will likely not work like expected**, due to differences between HTML and Delta, see this [comment](https://github.com/slab/quill/issues/1551#issuecomment-311458570) for more info.
![Copying Markdown file into Flutter Quill Editor](https://github.com/singerdmx/flutter-quill/assets/73608287/63bd6ba6-cc49-4335-84dc-91a0fa5c95a9)
For more details see #1915
Using or converting to HTML or Markdown is highly experimental and shouldn't be used for production applications.
We use it internally as it is more suitable for our specific use case., copying content from external websites and pasting it into the editor
previously breaks the styles, while the current implementation is not ready, it provides a better user experience and doesn't have many downsides.
Feel free to report any bugs or feature requests at [Issues](https://github.com/singerdmx/flutter-quill/issues) or drop any suggestions and questions at [Discussions](https://github.com/singerdmx/flutter-quill/discussions)
## New Contributors
* @leegh519 made their first contribution in https://github.com/singerdmx/flutter-quill/pull/1911
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.3.21...v9.4.0
## 9.3.21
* fix: assertion failure for swipe typing and undo on Android by @crasowas in https://github.com/singerdmx/flutter-quill/pull/1898

@ -1,6 +1,6 @@
name: flutter_quill_test
description: Test utilities for flutter_quill which includes methods to simplify interacting with the editor in test cases.
version: 9.3.21
version: 9.5.0
homepage: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_test/
repository: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_test/
issue_tracker: https://github.com/singerdmx/flutter-quill/issues/

@ -4,18 +4,23 @@ import '../generated/quill_localizations.dart' as generated;
typedef FlutterQuillLocalizations = generated.FlutterQuillLocalizations;
class MissingFlutterQuillLocalizationException extends UnimplementedError {
MissingFlutterQuillLocalizationException();
@override
String? get message =>
'FlutterQuillLocalizations instance is required and could not found. '
'Ensure that you are wrapping the current widget with '
'FlutterQuillLocalizationsWidget or add '
'FlutterQuillLocalizations.delegate to the localizationsDelegates '
'in your App widget (e.,g WidgetsApp, MaterialApp). If you believe this is a bug, consider reporting it.';
}
extension LocalizationsExt on BuildContext {
/// Require the [FlutterQuillLocalizations] instance
///
/// `loc` is short for `localizations`
FlutterQuillLocalizations get loc {
return FlutterQuillLocalizations.of(this) ??
(throw UnimplementedError(
"The instance of FlutterQuillLocalizations.of(context) is null and it's"
' required, please make sure you wrapping the current widget with '
'FlutterQuillLocalizationsWidget or add '
'FlutterQuillLocalizations.delegate to the localizationsDelegates '
'in your App widget, please consider report this in GitHub as a bug',
));
(throw MissingFlutterQuillLocalizationException());
}
}

@ -415,6 +415,12 @@ abstract class FlutterQuillLocalizations {
/// **'Align right'**
String get alignRight;
/// Justify the text over the full window width
///
/// In en, this message translates to:
/// **'Align justify'**
String get alignJustify;
/// No description provided for @justifyWinWidth.
///
/// In en, this message translates to:
@ -732,6 +738,12 @@ abstract class FlutterQuillLocalizations {
/// In en, this message translates to:
/// **'Paste'**
String get paste;
/// No description provided for @insertTable.
///
/// In en, this message translates to:
/// **'Insert table'**
String get insertTable;
}
class _FlutterQuillLocalizationsDelegate

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsAr extends FlutterQuillLocalizations {
@override
String get alignRight => 'محاذاة اليمين';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => 'تبرير مع العرض';
@ -288,4 +291,7 @@ class FlutterQuillLocalizationsAr extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsBg extends FlutterQuillLocalizations {
@override
String get alignRight => 'Подравни вдясно';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => 'Подравни във всяка колонка';
@ -290,4 +293,7 @@ class FlutterQuillLocalizationsBg extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsBn extends FlutterQuillLocalizations {
@override
String get alignRight => 'ন সিবদ';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => 'রসর সযত';
@ -290,4 +293,7 @@ class FlutterQuillLocalizationsBn extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsCs extends FlutterQuillLocalizations {
@override
String get alignRight => 'Zarovnat vpravo';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => 'Zarovnat do bloku';
@ -290,4 +293,7 @@ class FlutterQuillLocalizationsCs extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsDa extends FlutterQuillLocalizations {
@override
String get alignRight => 'Align right';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => 'Justify win width';
@ -288,4 +291,7 @@ class FlutterQuillLocalizationsDa extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsDe extends FlutterQuillLocalizations {
@override
String get alignRight => 'Rechtsbündig ausrichten';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => 'Blocksatz';
@ -294,4 +297,7 @@ class FlutterQuillLocalizationsDe extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsEn extends FlutterQuillLocalizations {
@override
String get alignRight => 'Align right';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => 'Justify win width';
@ -290,6 +293,9 @@ class FlutterQuillLocalizationsEn extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}
/// The translations for English, as used in the United States (`en_US`).

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsEs extends FlutterQuillLocalizations {
@override
String get alignRight => 'Alinear a la derecha';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => 'Justificar';
@ -290,4 +293,7 @@ class FlutterQuillLocalizationsEs extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsFa extends FlutterQuillLocalizations {
@override
String get alignRight => 'چیدمان راست';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => 'تضمین عرض پنجره';
@ -291,4 +294,7 @@ class FlutterQuillLocalizationsFa extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsFr extends FlutterQuillLocalizations {
@override
String get alignRight => 'Aligner à droite';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => 'Justifier';
@ -296,4 +299,7 @@ class FlutterQuillLocalizationsFr extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsHe extends FlutterQuillLocalizations {
@override
String get alignRight => 'יישור לימין';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => 'יישור לרוחב החלון';
@ -290,4 +293,7 @@ class FlutterQuillLocalizationsHe extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsHi extends FlutterQuillLocalizations {
@override
String get alignRight => 'ित कर';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => 'जसिन च';
@ -291,4 +294,7 @@ class FlutterQuillLocalizationsHi extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsId extends FlutterQuillLocalizations {
@override
String get alignRight => 'Rata Kanan';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => 'Rata Kanan dan Kiri';
@ -292,4 +295,7 @@ class FlutterQuillLocalizationsId extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsIt extends FlutterQuillLocalizations {
@override
String get alignRight => 'Allinea a destra';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => 'Giustifica per larghezza finestra';
@ -292,4 +295,7 @@ class FlutterQuillLocalizationsIt extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsJa extends FlutterQuillLocalizations {
@override
String get alignRight => '右揃え';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => '両端揃え';
@ -287,4 +290,7 @@ class FlutterQuillLocalizationsJa extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsKo extends FlutterQuillLocalizations {
@override
String get alignRight => '오른쪽 정렬';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => '좌우로 정렬';
@ -287,4 +290,7 @@ class FlutterQuillLocalizationsKo extends FlutterQuillLocalizations {
@override
String get paste => '붙여넣기';
@override
String get insertTable => 'Insert table';
}

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsKu extends FlutterQuillLocalizations {
@override
String get alignRight => 'بۆ ڕاست';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => 'پانی ڕێکبخە';
@ -291,6 +294,9 @@ class FlutterQuillLocalizationsKu extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}
/// The translations for Kurdish (`ku_CKB`).

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsMs extends FlutterQuillLocalizations {
@override
String get alignRight => 'Align right';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => 'Justify win width';
@ -290,4 +293,7 @@ class FlutterQuillLocalizationsMs extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsNe extends FlutterQuillLocalizations {
@override
String get alignRight => ' पङिबद';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => 'जसिन च';
@ -294,4 +297,7 @@ class FlutterQuillLocalizationsNe extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsNl extends FlutterQuillLocalizations {
@override
String get alignRight => 'Align right';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => 'Justify win width';
@ -292,4 +295,7 @@ class FlutterQuillLocalizationsNl extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsNo extends FlutterQuillLocalizations {
@override
String get alignRight => 'Høyrejuster';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => 'Rettferdiggjør bredden';
@ -292,4 +295,7 @@ class FlutterQuillLocalizationsNo extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsPl extends FlutterQuillLocalizations {
@override
String get alignRight => 'Align right';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => 'Justify win width';
@ -289,4 +292,7 @@ class FlutterQuillLocalizationsPl extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsPt extends FlutterQuillLocalizations {
@override
String get alignRight => 'Align right';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => 'Justify win width';
@ -290,6 +293,9 @@ class FlutterQuillLocalizationsPt extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}
/// The translations for Portuguese, as used in Brazil (`pt_BR`).

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsRo extends FlutterQuillLocalizations {
@override
String get alignRight => 'Aliniază la dreapta';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => 'Justifică lățimea ferestrei';
@ -293,6 +296,9 @@ class FlutterQuillLocalizationsRo extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}
/// The translations for Romanian Moldavian Moldovan, as used in Romania (`ro_RO`).

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsRu extends FlutterQuillLocalizations {
@override
String get alignRight => 'Выровнять по правому краю';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => 'Выровнять по ширине окна';
@ -293,4 +296,7 @@ class FlutterQuillLocalizationsRu extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsSk extends FlutterQuillLocalizations {
@override
String get alignRight => 'Zarovnať vpravo';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => 'Zarovnať na šírku okna';
@ -294,4 +297,7 @@ class FlutterQuillLocalizationsSk extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsSr extends FlutterQuillLocalizations {
@override
String get alignRight => 'Poravnanje desno';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => 'Centriraj širinu prozora';
@ -291,4 +294,7 @@ class FlutterQuillLocalizationsSr extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsSv extends FlutterQuillLocalizations {
@override
String get alignRight => 'Högerjustera';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => 'Justera till fönsterbredd';
@ -290,4 +293,7 @@ class FlutterQuillLocalizationsSv extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsSw extends FlutterQuillLocalizations {
@override
String get alignRight => 'Pangilia Kulia';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => 'Kuhalalisha Upana wa Ushindi';
@ -289,4 +292,7 @@ class FlutterQuillLocalizationsSw extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsTk extends FlutterQuillLocalizations {
@override
String get alignRight => 'Saga deňleşdir';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => 'Justify win width';
@ -288,4 +291,7 @@ class FlutterQuillLocalizationsTk extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsTr extends FlutterQuillLocalizations {
@override
String get alignRight => 'Sağa Hizala';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => 'Kenarlara Hizala';
@ -289,4 +292,7 @@ class FlutterQuillLocalizationsTr extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsUk extends FlutterQuillLocalizations {
@override
String get alignRight => 'Вирівняти праворуч';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => 'Вирівняти за шириною вікна';
@ -294,4 +297,7 @@ class FlutterQuillLocalizationsUk extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsUr extends FlutterQuillLocalizations {
@override
String get alignRight => 'دائیں ہم آہنگ ہوں';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => 'جسٹیفائی ون چوڑائی';
@ -293,4 +296,7 @@ class FlutterQuillLocalizationsUr extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsVi extends FlutterQuillLocalizations {
@override
String get alignRight => 'Căn phải';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => 'Căn đều chiều rộng';
@ -290,4 +293,7 @@ class FlutterQuillLocalizationsVi extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}

@ -127,6 +127,9 @@ class FlutterQuillLocalizationsZh extends FlutterQuillLocalizations {
@override
String get alignRight => '右对齐';
@override
String get alignJustify => 'Align justify';
@override
String get justifyWinWidth => '两端对齐';
@ -287,6 +290,9 @@ class FlutterQuillLocalizationsZh extends FlutterQuillLocalizations {
@override
String get paste => 'Paste';
@override
String get insertTable => 'Insert table';
}
/// The translations for Chinese, as used in China (`zh_CN`).

@ -41,6 +41,10 @@
"alignLeft": "Align left",
"alignCenter": "Align center",
"alignRight": "Align right",
"alignJustify": "Align justify",
"@alignJustify": {
"description": "Justify the text over the full window width"
},
"justifyWinWidth": "Justify win width",
"textDirection": "Text direction",
"headerStyle": "Header style",
@ -102,5 +106,6 @@
"close": "Close",
"searchSettings": "Search settings",
"cut": "Cut",
"paste": "Paste"
"paste": "Paste",
"insertTable": "Insert table"
}

@ -1,15 +1,18 @@
{
"ar": [
"alignJustify",
"theImageHasBeenSavedAt",
"caseSensitive",
"wholeWord",
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"bg": [
"alignJustify",
"normal",
"heading1",
"heading2",
@ -29,10 +32,12 @@
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"bn": [
"alignJustify",
"normal",
"heading1",
"heading2",
@ -52,19 +57,23 @@
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"cs": [
"alignJustify",
"caseSensitive",
"wholeWord",
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"da": [
"alignJustify",
"normal",
"heading1",
"heading2",
@ -84,10 +93,12 @@
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"de": [
"alignJustify",
"normal",
"heading1",
"heading2",
@ -102,10 +113,12 @@
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"en_US": [
"alignJustify",
"normal",
"heading1",
"heading2",
@ -125,10 +138,12 @@
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"es": [
"alignJustify",
"normal",
"heading1",
"heading2",
@ -143,10 +158,12 @@
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"fa": [
"alignJustify",
"normal",
"heading1",
"heading2",
@ -166,20 +183,24 @@
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"fr": [
"alignJustify",
"theImageHasBeenSavedAt",
"caseSensitive",
"wholeWord",
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"he": [
"alignJustify",
"normal",
"heading1",
"heading2",
@ -199,10 +220,12 @@
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"hi": [
"alignJustify",
"normal",
"heading1",
"heading2",
@ -222,10 +245,12 @@
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"id": [
"alignJustify",
"normal",
"heading1",
"heading2",
@ -245,10 +270,12 @@
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"it": [
"alignJustify",
"normal",
"heading1",
"heading2",
@ -268,10 +295,12 @@
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"ja": [
"alignJustify",
"normal",
"heading1",
"heading2",
@ -291,30 +320,41 @@
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"ko": [
"alignJustify",
"insertTable"
],
"ku": [
"alignJustify",
"theImageHasBeenSavedAt",
"caseSensitive",
"wholeWord",
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"ku_CKB": [
"alignJustify",
"theImageHasBeenSavedAt",
"caseSensitive",
"wholeWord",
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"ms": [
"alignJustify",
"normal",
"heading1",
"heading2",
@ -334,19 +374,23 @@
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"ne": [
"alignJustify",
"caseSensitive",
"wholeWord",
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"nl": [
"alignJustify",
"normal",
"heading1",
"heading2",
@ -366,10 +410,12 @@
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"no": [
"alignJustify",
"normal",
"heading1",
"heading2",
@ -389,10 +435,12 @@
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"pl": [
"alignJustify",
"normal",
"heading1",
"heading2",
@ -412,10 +460,12 @@
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"pt": [
"alignJustify",
"normal",
"heading1",
"heading2",
@ -435,10 +485,12 @@
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"pt_BR": [
"alignJustify",
"normal",
"heading1",
"heading2",
@ -458,30 +510,36 @@
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"ro": [
"alignJustify",
"theImageHasBeenSavedAt",
"caseSensitive",
"wholeWord",
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"ro_RO": [
"alignJustify",
"theImageHasBeenSavedAt",
"caseSensitive",
"wholeWord",
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"ru": [
"alignJustify",
"normal",
"heading1",
"heading2",
@ -496,19 +554,23 @@
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"sk": [
"alignJustify",
"caseSensitive",
"wholeWord",
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"sr": [
"alignJustify",
"normal",
"heading1",
"heading2",
@ -528,20 +590,24 @@
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"sv": [
"alignJustify",
"theImageHasBeenSavedAt",
"caseSensitive",
"wholeWord",
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"sw": [
"alignJustify",
"normal",
"heading1",
"heading2",
@ -561,10 +627,12 @@
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"tk": [
"alignJustify",
"normal",
"heading1",
"heading2",
@ -584,10 +652,12 @@
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"tr": [
"alignJustify",
"normal",
"heading1",
"heading2",
@ -607,20 +677,24 @@
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"uk": [
"alignJustify",
"theImageHasBeenSavedAt",
"caseSensitive",
"wholeWord",
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"ur": [
"alignJustify",
"normal",
"heading1",
"heading2",
@ -640,10 +714,12 @@
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"vi": [
"alignJustify",
"normal",
"heading1",
"heading2",
@ -663,30 +739,36 @@
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"zh": [
"alignJustify",
"theImageHasBeenSavedAt",
"caseSensitive",
"wholeWord",
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"zh_CN": [
"alignJustify",
"theImageHasBeenSavedAt",
"caseSensitive",
"wholeWord",
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
],
"zh_HK": [
"alignJustify",
"normal",
"heading1",
"heading2",
@ -706,6 +788,7 @@
"close",
"searchSettings",
"cut",
"paste"
"paste",
"insertTable"
]
}

@ -1,9 +1,12 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import '../../extensions/quill_configurations_ext.dart';
import '../extensions/localizations.dart';
/// A widget that check if [FlutterQuillLocalizations.delegate] is provided
/// in the widgets app (e.g, [MaterialApp] or [WidgetsApp]).
///
/// If not, will provide in the [child] to access it in the widget tree.
class FlutterQuillLocalizationsWidget extends StatelessWidget {
const FlutterQuillLocalizationsWidget({
required this.child,

@ -1,8 +1,14 @@
class QuillControllerConfigurations {
const QuillControllerConfigurations({this.onClipboardPaste});
const QuillControllerConfigurations(
{this.onClipboardPaste, this.requireScriptFontFeatures = false});
/// Callback when the user pastes and data has not already been processed
///
/// Return true if the paste operation was handled
final Future<bool> Function()? onClipboardPaste;
/// Render subscript and superscript text using Open Type FontFeatures
///
/// Default is false to use built-in script rendering that is independent of font capabilities
final bool requireScriptFontFeatures;
}

@ -1,9 +1,9 @@
import 'package:html2md/html2md.dart' as html2md;
import 'package:markdown/markdown.dart' as md;
import 'package:meta/meta.dart';
import '../../../markdown_quill.dart';
import '../../../quill_delta.dart';
import '../../utils/delta_x_utils.dart';
@immutable
@experimental
@ -16,7 +16,10 @@ class DeltaX {
/// used for **production applications**.
@experimental
static Delta fromMarkdown(String markdownText) {
final mdDocument = md.Document(encodeHtml: false);
final mdDocument = md.Document(
encodeHtml: false,
inlineSyntaxes: [UnderlineSyntax(), VideoSyntax()],
);
final mdToDelta = MarkdownToDelta(markdownDocument: mdDocument);
return mdToDelta.convert(markdownText);
}
@ -33,8 +36,14 @@ class DeltaX {
///
@experimental
static Delta fromHtml(String htmlText) {
final markdownText = html2md.convert(htmlText).replaceAll('unsafe:', '');
final markdownText = html2md.convert(
htmlText,
rules: [underlineRule, videoRule],
styleOptions: {'emDelimiter': '*'},
).replaceAll(
'unsafe:',
'',
);
return fromMarkdown(markdownText);
}
}

@ -371,18 +371,18 @@ class AutoFormatMultipleLinksRule extends InsertRule {
r'https?:\/\/[\w\-]+(\.[\w\-]+)*(:\d+)?(\/[^\s]*)?';
/// It requires a valid link in one link
static final oneLineLinkRegExp = RegExp(
_oneLineLinkPattern,
caseSensitive: false,
);
RegExp get oneLineLinkRegExp => RegExp(
_oneLineLinkPattern,
caseSensitive: false,
);
/// It detect if there is a link in the text whatever if it in the middle etc
// Used to solve bug https://github.com/singerdmx/flutter-quill/issues/1432
static final detectLinkRegExp = RegExp(
_detectLinkPattern,
caseSensitive: false,
);
static final linkRegExp = oneLineLinkRegExp;
RegExp get detectLinkRegExp => RegExp(
_detectLinkPattern,
caseSensitive: false,
);
RegExp get linkRegExp => oneLineLinkRegExp;
@override
Delta? applyRule(

@ -82,6 +82,7 @@ class MarkdownToDelta extends Converter<String, Delta>
final _elementToInlineAttr = <String, ElementToAttributeConvertor>{
'em': (_) => [Attribute.italic],
'u': (_) => [Attribute.underline],
'strong': (_) => [Attribute.bold],
'del': (_) => [Attribute.strikeThrough],
'a': (element) => [LinkAttribute(element.attributes['href'])],
@ -91,6 +92,7 @@ class MarkdownToDelta extends Converter<String, Delta>
final _elementToEmbed = <String, ElementToEmbeddableConvertor>{
'hr': (_) => horizontalRule,
'img': (elAttrs) => BlockEmbed.image(elAttrs['src'] ?? ''),
'video': (elAttrs) => BlockEmbed.video(elAttrs['src'] ?? '')
};
var _delta = Delta();

@ -0,0 +1,80 @@
import 'package:html2md/html2md.dart' as hmd;
import 'package:markdown/markdown.dart' as md;
// [ character
const int _$lbracket = 0x5B;
final RegExp _youtubeVideoUrlValidator = RegExp(
r'^((?:https?:)?\/\/)?((?:www|m)\.)?((?:youtube\.com|youtu.be))(\/(?:[\w\-]+\?v=|embed\/|v\/)?)([\w\-]+)(\S+)?$');
///Local syntax implementation for underline
class UnderlineSyntax extends md.DelimiterSyntax {
UnderlineSyntax()
: super(
'<und>',
requiresDelimiterRun: true,
allowIntraWord: true,
tags: [md.DelimiterTag('u', 5)],
);
}
class VideoSyntax extends md.LinkSyntax {
VideoSyntax({super.linkResolver})
: super(
pattern: r'\[',
startCharacter: _$lbracket,
);
@override
md.Element createNode(
String destination,
String? title, {
required List<md.Node> Function() getChildren,
}) {
final element = md.Element.empty('video');
element.attributes['src'] = destination;
if (title != null && title.isNotEmpty) {
element.attributes['title'] = title;
}
return element;
}
}
///This rule avoid the default converter from html2md ignore underline tag for <u> or <ins>
final underlineRule =
hmd.Rule('underline', filters: ['u', 'ins'], replacement: (content, node) {
//Is used a local underline implemenation since markdown just use underline with html tags
return '<und>$content<und>';
});
final videoRule = hmd.Rule('video', filters: ['iframe', 'video'],
replacement: (content, node) {
//This need to be verified by a different way of iframes, since video tag can have <source> children
if (node.nodeName == 'video') {
//if has children then just will be taked as different part of code
if (node.childNum > 0) {
var child = node.firstChild!;
var src = child.getAttribute('src');
if (src == null) {
child = node.childNodes().last;
src = child.getAttribute('src');
}
if (!_youtubeVideoUrlValidator.hasMatch(src ?? '')) {
return '<video>${child.outerHTML}</video>';
}
return '[$content]($src)';
}
final src = node.getAttribute('src');
if (src == null || !_youtubeVideoUrlValidator.hasMatch(src)) {
return node.outerHTML;
}
return '[$content]($src)';
}
//by now, we can only access to src
final src = node.getAttribute('src');
//if the source is null or is not valid youtube url, then just return the html instead remove it
//by now is only available validation for youtube videos
if (src == null || !_youtubeVideoUrlValidator.hasMatch(src)) {
return node.outerHTML;
}
final title = node.getAttribute('title');
return '[$title]($src)';
});

@ -173,6 +173,9 @@ class QuillEditorState extends State<QuillEditor>
void initState() {
super.initState();
widget.configurations.controller.editorFocusNode ??= widget.focusNode;
if (configurations.autoFocus) {
widget.configurations.controller.editorFocusNode?.requestFocus();
}
_editorKey = configurations.editorKey ?? GlobalKey<EditorState>();
_selectionGestureDetectorBuilder =
_QuillEditorSelectionGestureDetectorBuilder(

@ -322,19 +322,71 @@ class _TextLineState extends State<TextLine> {
return textStyle;
}
TextSpan _getTextSpanFromNode(
/// Processes subscript and superscript attributed text.
///
/// Reduces text fontSize and shifts down or up. Increases fontWeight to maintain balance with normal text.
/// Outputs characters individually to allow correct caret positioning and text selection.
InlineSpan _scriptSpan(String text, bool superScript, TextStyle style,
DefaultStyles defaultStyles) {
assert(text.isNotEmpty);
//
final lineStyle = style.fontSize == null || style.fontWeight == null
? _getLineStyle(defaultStyles)
: null;
final fontWeight = FontWeight.lerp(
style.fontWeight ?? lineStyle?.fontWeight ?? FontWeight.normal,
FontWeight.w900,
0.25);
final fontSize = style.fontSize ?? lineStyle?.fontSize ?? 16;
final y = (superScript ? -0.4 : 0.14) * fontSize;
final charStyle = style.copyWith(
fontFeatures: <FontFeature>[],
fontWeight: fontWeight,
fontSize: fontSize * 0.7);
//
final offset = Offset(0, y);
final children = <WidgetSpan>[];
for (final c in text.characters) {
children.add(WidgetSpan(
child: Transform.translate(
offset: offset,
child: Text(
c,
style: charStyle,
))));
}
//
if (children.length > 1) {
return TextSpan(children: children);
}
return children[0];
}
InlineSpan _getTextSpanFromNode(
DefaultStyles defaultStyles, Node node, Style lineStyle) {
final textNode = node as leaf.QuillText;
final nodeStyle = textNode.style;
final isLink = nodeStyle.containsKey(Attribute.link.key) &&
nodeStyle.attributes[Attribute.link.key]!.value != null;
final recognizer = _getRecognizer(node, isLink);
final style = _getInlineTextStyle(
textNode, defaultStyles, nodeStyle, lineStyle, isLink);
if (widget.controller.configurations.requireScriptFontFeatures == false &&
textNode.value.isNotEmpty) {
if (nodeStyle.containsKey(Attribute.script.key)) {
final attr = nodeStyle.attributes[Attribute.script.key];
if (attr == Attribute.superscript || attr == Attribute.subscript) {
return _scriptSpan(textNode.value, attr == Attribute.superscript,
style, defaultStyles);
}
}
}
final recognizer = _getRecognizer(node, isLink);
return TextSpan(
text: textNode.value,
style: _getInlineTextStyle(
textNode, defaultStyles, nodeStyle, lineStyle, isLink),
style: style,
recognizer: recognizer,
mouseCursor: (recognizer != null) ? SystemMouseCursors.click : null,
);

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import '../../../translations.dart';
import '../../models/documents/attribute.dart';
import '../editor/editor.dart';
import '../toolbar/buttons/link_style2_button.dart';
@ -460,8 +461,10 @@ class QuillEditorOpenSearchAction extends ContextAction<OpenSearchIntent> {
}
await showDialog<String>(
context: context,
builder: (_) => QuillToolbarSearchDialog(
controller: state.controller,
builder: (_) => FlutterQuillLocalizationsWidget(
child: QuillToolbarSearchDialog(
controller: state.controller,
),
),
);
}
@ -577,3 +580,29 @@ class QuillEditorInsertEmbedIntent extends Intent {
final Attribute type;
}
class NavigateToDocumentBoundaryAction
extends ContextAction<ScrollToDocumentBoundaryIntent> {
NavigateToDocumentBoundaryAction(this.state);
final QuillRawEditorState state;
@override
Object? invoke(
ScrollToDocumentBoundaryIntent intent, [
BuildContext? context,
]) {
return Actions.invoke(
context!,
UpdateSelectionIntent(
state.textEditingValue,
intent.forward
? TextSelection.collapsed(
offset: state.controller.plainTextEditingValue.text.length,
)
: const TextSelection.collapsed(offset: 0),
SelectionChangedCause.keyboard,
),
);
}
}

@ -701,6 +701,18 @@ class QuillRawEditorState extends EditorState
control: !isDesktopMacOS,
meta: isDesktopMacOS,
): const OpenSearchIntent(),
// Navigate to the start or end of the document
SingleActivator(
LogicalKeyboardKey.home,
control: !isDesktopMacOS,
meta: isDesktopMacOS,
): const ScrollToDocumentBoundaryIntent(forward: false),
SingleActivator(
LogicalKeyboardKey.end,
control: !isDesktopMacOS,
meta: isDesktopMacOS,
): const ScrollToDocumentBoundaryIntent(forward: true),
}, {
...?widget.configurations.customShortcuts
}),
@ -1674,7 +1686,8 @@ class QuillRawEditorState extends EditorState
IndentSelectionIntent: _indentSelectionAction,
QuillEditorApplyHeaderIntent: _applyHeaderAction,
QuillEditorApplyCheckListIntent: _applyCheckListAction,
QuillEditorApplyLinkIntent: QuillEditorApplyLinkAction(this)
QuillEditorApplyLinkIntent: QuillEditorApplyLinkAction(this),
ScrollToDocumentBoundaryIntent: NavigateToDocumentBoundaryAction(this)
};
@override

@ -84,6 +84,7 @@ abstract class QuillToolbarBaseButtonState<
}
void didChangeEditingValue() {
if (!mounted) return;
setState(() => currentValue = currentStateValue);
}

@ -54,6 +54,7 @@ class ColorPickerDialogState extends State<ColorPickerDialog> {
actions: [
TextButton(
onPressed: () {
widget.onRequestChangeColor(context, selectedColor);
Navigator.of(context).pop();
},
child: Text(context.loc.ok)),
@ -123,8 +124,6 @@ class ColorPickerDialogState extends State<ColorPickerDialog> {
controller: hexController,
onChanged: (value) {
selectedColor = hexToColor(value);
widget.onRequestChangeColor(context, selectedColor);
colorBoxSetState(() {});
},
decoration: InputDecoration(

@ -413,7 +413,9 @@ class _LinkStyleDialogState extends State<LinkStyleDialog> {
String? _validateLink(String? value) {
if ((value?.isEmpty ?? false) ||
!AutoFormatMultipleLinksRule.oneLineLinkRegExp.hasMatch(value!)) {
!const AutoFormatMultipleLinksRule()
.oneLineLinkRegExp
.hasMatch(value!)) {
return widget.validationMessage ?? 'That is not a valid URL';
}

@ -196,7 +196,8 @@ class _LinkDialogState extends State<_LinkDialog> {
late String _text;
RegExp get linkRegExp {
return widget.linkRegExp ?? AutoFormatMultipleLinksRule.oneLineLinkRegExp;
return widget.linkRegExp ??
const AutoFormatMultipleLinksRule().oneLineLinkRegExp;
}
late TextEditingController _linkController;

@ -78,10 +78,7 @@ class QuillToolbarToggleStyleButtonState
'left' => (context.loc.alignLeft, Icons.format_align_left),
'right' => (context.loc.alignRight, Icons.format_align_right),
'center' => (context.loc.alignCenter, Icons.format_align_center),
'justify' => (
context.loc.justifyWinWidth,
Icons.format_align_justify
),
'justify' => (context.loc.alignJustify, Icons.format_align_justify),
Object() => throw ArgumentError(widget.attribute.value),
null => (context.loc.alignCenter, Icons.format_align_center),
};

@ -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: 9.3.21
version: 9.5.0
homepage: https://1o24bbs.com/c/bulletjournal/108/
repository: https://github.com/singerdmx/flutter-quill/
issue_tracker: https://github.com/singerdmx/flutter-quill/issues/
@ -70,10 +70,12 @@ dev_dependencies:
flutter_quill_test: ^9.3.4
test: ^1.24.3
# Both yaml_edit and yaml are only used for the scripts
# For scripts only
yaml_edit: ^2.2.1
yaml: ^3.1.2
http: ^1.2.1
path: any
flutter:
uses-material-design: true
generate: true

@ -4,6 +4,230 @@
All notable changes to this project will be documented in this file.
## 9.5.0
* Partial support for table embed by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/1960
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.9...v9.5.0
## 9.4.9
* Upgrade photo_view to 0.15.0 for flutter_quill_extensions by @singerdmx in https://github.com/singerdmx/flutter-quill/pull/1958
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.8...v9.4.9
## 9.4.8
* Add support for html underline and videos by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/1955
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.7...v9.4.8
## 9.4.7
* fixed #1953 italic detection error by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/1954
## New Contributors
* @CatHood0 made their first contribution in https://github.com/singerdmx/flutter-quill/pull/1954
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.6...v9.4.7
## 9.4.6
* fix: search dialog throw an exception due to missing FlutterQuillLocalizations.delegate in the editor by @ellet0 in https://github.com/singerdmx/flutter-quill/pull/1938
* fix(editor): implement editor shortcut action for home and end keys to fix exception about unimplemented ScrollToDocumentBoundaryIntent by @ellet0 in https://github.com/singerdmx/flutter-quill/pull/1937
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.5...v9.4.6
## 9.4.5
* fix: color picker hex unfocus on web by @geronimol in https://github.com/singerdmx/flutter-quill/pull/1934
## New Contributors
* @geronimol made their first contribution in https://github.com/singerdmx/flutter-quill/pull/1934
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.4...v9.4.5
## 9.4.4
* fix: Enabled link regex to be overridden by @JoepHeijnen in https://github.com/singerdmx/flutter-quill/pull/1931
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.3...v9.4.4
## 9.4.3
* Fix: setState() called after dispose(): QuillToolbarClipboardButtonState #1895 by @windows7lake in https://github.com/singerdmx/flutter-quill/pull/1926
## New Contributors
* @windows7lake made their first contribution in https://github.com/singerdmx/flutter-quill/pull/1926
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.2...v9.4.3
## 9.4.2
* Respect autofocus, closes #1923 by @Guillergood in https://github.com/singerdmx/flutter-quill/pull/1924
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.1...v9.4.2
## 9.4.1
* replace base64 regex string by @salba360496 in https://github.com/singerdmx/flutter-quill/pull/1919
## New Contributors
* @salba360496 made their first contribution in https://github.com/singerdmx/flutter-quill/pull/1919
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.0...v9.4.1
## 9.4.0
This release can be used without changing anything, although it can break the behavior a little, we provided a way to use the old behavior in `9.3.x`
- Thanks to @Alspb, the search bar/dialog has been reworked for improved UI that fits **Material 3** look and feel, the search happens on the fly, and other minor changes, if you want the old search bar, you can restore it with one line if you're using `QuillSimpleToolbar`:
```dart
QuillToolbar.simple(
configurations: QuillSimpleToolbarConfigurations(
searchButtonType: SearchButtonType.legacy,
),
)
```
While the changes are mostly to the `QuillToolbarSearchDialog` and it seems this should be `searchDialogType`, we provided the old button with the old dialog in case we update the button in the future.
If you're using `QuillToolbarSearchButton` in a custom Toolbar, you don't need anything to get the new button. if you want the old button, use the `QuillToolbarLegacySearchButton` widget
Consider using the improved button with the improved dialog as the legacy button might removed in future releases (for now, it's not deprecated)
<details>
<summary>Before</summary>
![image](https://github.com/singerdmx/flutter-quill/assets/73608287/9b40ad03-717f-4518-95f1-8d9cad773b2b)
</details>
<details>
<summary>Improved</summary>
![image](https://github.com/singerdmx/flutter-quill/assets/73608287/e581733d-63fa-4984-9c41-4a325a0a0c04)
</details>
For the detailed changes, see #1904
- Korean translations by @leegh519 in https://github.com/singerdmx/flutter-quill/pull/1911
- The usage of `super_clipboard` plugin in `flutter_quill` has been moved to the `flutter_quill_extensions` package, this will restore the old behavior in `8.x.x` though it will break the `onImagePaste`, `onGifPaste` and rich text pasting from HTML or Markdown, most of those features are available in `super_clipboard` plugin except `onImagePaste` which was available as we were using [pasteboard](https://pub.dev/packages/pasteboard), Unfortunately, it's no longer supported on recent versions of Flutter, and some functionalities such as an image from Clipboard and Html paste are not supported on some platforms such as Android, your project will continue to work, calls of `onImagePaste` and `onGifPaste` will be ignored unless you include [flutter_quill_extensions](https://pub.dev/packages/flutter_quill_extensions) package in your project and call:
```dart
FlutterQuillExtensions.useSuperClipboardPlugin();
```
Before using any `flutter_quill` widgets, this will restore the old behavior in `9.x.x`
We initially wanted to publish `flutter_quill_super_clipboard` to allow:
- Using `super_clipboard` without `flutter_quill_extensions` packages and plugins
- Using `flutter_quill_extensions` with optional `super_clipboard`
To simplify the usage, we moved it to `flutter_quill_extensions`, let us know if you want any of the use cases above.
Overall `super_clipboard` is a Comprehensive clipboard plugin with a lot of features, the only thing that developers didn't want is Rust installation even though it's automated.
The main goal of `ClipboardService` is to make `super_clipboard` optional, you can use your own implementation, and create a class that implements `ClipboardService`, which you can get by:
```dart
// ignore: implementation_imports
import 'package:flutter_quill/src/services/clipboard/clipboard_service.dart';
```
Then you can call:
```dart
// ignore: implementation_imports
import 'package:flutter_quill/src/services/clipboard/clipboard_service_provider.dart';
ClipboardServiceProvider.setInstance(YourClipboardService());
```
The interface could change at any time and will be updated internally for `flutter_quill` and `flutter_quill_extensions`, we didn't export those two classes by default to avoid breaking changes in case you use them as we might change them in the future.
If you use the above imports, you might get **breaking changes** in **non-breaking change releases**.
- Subscript and Superscript should now work for all languages and characters
The previous implementation required the Apple 'SF-Pro-Display-Regular.otf' font which is only licensed/permitted for use on Apple devices.
We have removed the Apple font from the example
- Allow pasting Markdown and HTML file content from the system to the editor
Before `9.4.x` if you try to copy an HTML or Markdown file, and paste it into the editor, you will get the file name in the editor
Copying an HTML file, or HTML content from apps and websites is different than copying plain text.
This is why this change requires `super_clipboard` implementation as this is platform-dependent:
```dart
FlutterQuillExtensions.useSuperClipboardPlugin();
```
as mentioned above.
The following example for copying a Markdown file:
<details>
<summary>Markdown File Content</summary>
```md
**Note**: This package supports converting from HTML back to Quill delta but it's experimental and used internally when pasting HTML content from the clipboard to the Quill Editor
You have two options:
1. Using [quill_html_converter](./quill_html_converter/) to convert to HTML, the package can convert the Quill delta to HTML well
(it uses [vsc_quill_delta_to_html](https://pub.dev/packages/vsc_quill_delta_to_html)), it is just a handy extension to do it more quickly
1. Another option is to use
[vsc_quill_delta_to_html](https://pub.dev/packages/vsc_quill_delta_to_html) to convert your document
to HTML.
This package has full support for all Quill operations—including images, videos, formulas,
tables, and mentions.
Conversion can be performed in vanilla Dart (i.e., server-side or CLI) or in Flutter.
It is a complete Dart part of the popular and mature [quill-delta-to-html](https://www.npmjs.com/package/quill-delta-to-html)
Typescript/Javascript package.
this package doesn't convert the HTML back to Quill Delta as far as we know
```
</details>
<details>
<summary>Before</summary>
![image](https://github.com/singerdmx/flutter-quill/assets/73608287/03f5ae20-796c-4e8b-8668-09a994211c1e)
</details>
<details>
<summary>After</summary>
![image](https://github.com/singerdmx/flutter-quill/assets/73608287/7e3a1987-36e7-4665-944a-add87d24e788)
</details>
Markdown, and HTML converting from and to Delta are **currently far from perfect**, the current implementation could improved a lot
however **it will likely not work like expected**, due to differences between HTML and Delta, see this [comment](https://github.com/slab/quill/issues/1551#issuecomment-311458570) for more info.
![Copying Markdown file into Flutter Quill Editor](https://github.com/singerdmx/flutter-quill/assets/73608287/63bd6ba6-cc49-4335-84dc-91a0fa5c95a9)
For more details see #1915
Using or converting to HTML or Markdown is highly experimental and shouldn't be used for production applications.
We use it internally as it is more suitable for our specific use case., copying content from external websites and pasting it into the editor
previously breaks the styles, while the current implementation is not ready, it provides a better user experience and doesn't have many downsides.
Feel free to report any bugs or feature requests at [Issues](https://github.com/singerdmx/flutter-quill/issues) or drop any suggestions and questions at [Discussions](https://github.com/singerdmx/flutter-quill/discussions)
## New Contributors
* @leegh519 made their first contribution in https://github.com/singerdmx/flutter-quill/pull/1911
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.3.21...v9.4.0
## 9.3.21
* fix: assertion failure for swipe typing and undo on Android by @crasowas in https://github.com/singerdmx/flutter-quill/pull/1898

@ -1,6 +1,6 @@
name: quill_html_converter
description: A extension for flutter_quill package to add support for dealing with conversion to/from html
version: 9.3.21
version: 9.5.0
homepage: https://github.com/singerdmx/flutter-quill/tree/master/quill_html_converter/
repository: https://github.com/singerdmx/flutter-quill/tree/master/quill_html_converter/
issue_tracker: https://github.com/singerdmx/flutter-quill/issues/

@ -4,6 +4,230 @@
All notable changes to this project will be documented in this file.
## 9.5.0
* Partial support for table embed by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/1960
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.9...v9.5.0
## 9.4.9
* Upgrade photo_view to 0.15.0 for flutter_quill_extensions by @singerdmx in https://github.com/singerdmx/flutter-quill/pull/1958
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.8...v9.4.9
## 9.4.8
* Add support for html underline and videos by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/1955
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.7...v9.4.8
## 9.4.7
* fixed #1953 italic detection error by @CatHood0 in https://github.com/singerdmx/flutter-quill/pull/1954
## New Contributors
* @CatHood0 made their first contribution in https://github.com/singerdmx/flutter-quill/pull/1954
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.6...v9.4.7
## 9.4.6
* fix: search dialog throw an exception due to missing FlutterQuillLocalizations.delegate in the editor by @ellet0 in https://github.com/singerdmx/flutter-quill/pull/1938
* fix(editor): implement editor shortcut action for home and end keys to fix exception about unimplemented ScrollToDocumentBoundaryIntent by @ellet0 in https://github.com/singerdmx/flutter-quill/pull/1937
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.5...v9.4.6
## 9.4.5
* fix: color picker hex unfocus on web by @geronimol in https://github.com/singerdmx/flutter-quill/pull/1934
## New Contributors
* @geronimol made their first contribution in https://github.com/singerdmx/flutter-quill/pull/1934
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.4...v9.4.5
## 9.4.4
* fix: Enabled link regex to be overridden by @JoepHeijnen in https://github.com/singerdmx/flutter-quill/pull/1931
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.3...v9.4.4
## 9.4.3
* Fix: setState() called after dispose(): QuillToolbarClipboardButtonState #1895 by @windows7lake in https://github.com/singerdmx/flutter-quill/pull/1926
## New Contributors
* @windows7lake made their first contribution in https://github.com/singerdmx/flutter-quill/pull/1926
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.2...v9.4.3
## 9.4.2
* Respect autofocus, closes #1923 by @Guillergood in https://github.com/singerdmx/flutter-quill/pull/1924
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.1...v9.4.2
## 9.4.1
* replace base64 regex string by @salba360496 in https://github.com/singerdmx/flutter-quill/pull/1919
## New Contributors
* @salba360496 made their first contribution in https://github.com/singerdmx/flutter-quill/pull/1919
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.4.0...v9.4.1
## 9.4.0
This release can be used without changing anything, although it can break the behavior a little, we provided a way to use the old behavior in `9.3.x`
- Thanks to @Alspb, the search bar/dialog has been reworked for improved UI that fits **Material 3** look and feel, the search happens on the fly, and other minor changes, if you want the old search bar, you can restore it with one line if you're using `QuillSimpleToolbar`:
```dart
QuillToolbar.simple(
configurations: QuillSimpleToolbarConfigurations(
searchButtonType: SearchButtonType.legacy,
),
)
```
While the changes are mostly to the `QuillToolbarSearchDialog` and it seems this should be `searchDialogType`, we provided the old button with the old dialog in case we update the button in the future.
If you're using `QuillToolbarSearchButton` in a custom Toolbar, you don't need anything to get the new button. if you want the old button, use the `QuillToolbarLegacySearchButton` widget
Consider using the improved button with the improved dialog as the legacy button might removed in future releases (for now, it's not deprecated)
<details>
<summary>Before</summary>
![image](https://github.com/singerdmx/flutter-quill/assets/73608287/9b40ad03-717f-4518-95f1-8d9cad773b2b)
</details>
<details>
<summary>Improved</summary>
![image](https://github.com/singerdmx/flutter-quill/assets/73608287/e581733d-63fa-4984-9c41-4a325a0a0c04)
</details>
For the detailed changes, see #1904
- Korean translations by @leegh519 in https://github.com/singerdmx/flutter-quill/pull/1911
- The usage of `super_clipboard` plugin in `flutter_quill` has been moved to the `flutter_quill_extensions` package, this will restore the old behavior in `8.x.x` though it will break the `onImagePaste`, `onGifPaste` and rich text pasting from HTML or Markdown, most of those features are available in `super_clipboard` plugin except `onImagePaste` which was available as we were using [pasteboard](https://pub.dev/packages/pasteboard), Unfortunately, it's no longer supported on recent versions of Flutter, and some functionalities such as an image from Clipboard and Html paste are not supported on some platforms such as Android, your project will continue to work, calls of `onImagePaste` and `onGifPaste` will be ignored unless you include [flutter_quill_extensions](https://pub.dev/packages/flutter_quill_extensions) package in your project and call:
```dart
FlutterQuillExtensions.useSuperClipboardPlugin();
```
Before using any `flutter_quill` widgets, this will restore the old behavior in `9.x.x`
We initially wanted to publish `flutter_quill_super_clipboard` to allow:
- Using `super_clipboard` without `flutter_quill_extensions` packages and plugins
- Using `flutter_quill_extensions` with optional `super_clipboard`
To simplify the usage, we moved it to `flutter_quill_extensions`, let us know if you want any of the use cases above.
Overall `super_clipboard` is a Comprehensive clipboard plugin with a lot of features, the only thing that developers didn't want is Rust installation even though it's automated.
The main goal of `ClipboardService` is to make `super_clipboard` optional, you can use your own implementation, and create a class that implements `ClipboardService`, which you can get by:
```dart
// ignore: implementation_imports
import 'package:flutter_quill/src/services/clipboard/clipboard_service.dart';
```
Then you can call:
```dart
// ignore: implementation_imports
import 'package:flutter_quill/src/services/clipboard/clipboard_service_provider.dart';
ClipboardServiceProvider.setInstance(YourClipboardService());
```
The interface could change at any time and will be updated internally for `flutter_quill` and `flutter_quill_extensions`, we didn't export those two classes by default to avoid breaking changes in case you use them as we might change them in the future.
If you use the above imports, you might get **breaking changes** in **non-breaking change releases**.
- Subscript and Superscript should now work for all languages and characters
The previous implementation required the Apple 'SF-Pro-Display-Regular.otf' font which is only licensed/permitted for use on Apple devices.
We have removed the Apple font from the example
- Allow pasting Markdown and HTML file content from the system to the editor
Before `9.4.x` if you try to copy an HTML or Markdown file, and paste it into the editor, you will get the file name in the editor
Copying an HTML file, or HTML content from apps and websites is different than copying plain text.
This is why this change requires `super_clipboard` implementation as this is platform-dependent:
```dart
FlutterQuillExtensions.useSuperClipboardPlugin();
```
as mentioned above.
The following example for copying a Markdown file:
<details>
<summary>Markdown File Content</summary>
```md
**Note**: This package supports converting from HTML back to Quill delta but it's experimental and used internally when pasting HTML content from the clipboard to the Quill Editor
You have two options:
1. Using [quill_html_converter](./quill_html_converter/) to convert to HTML, the package can convert the Quill delta to HTML well
(it uses [vsc_quill_delta_to_html](https://pub.dev/packages/vsc_quill_delta_to_html)), it is just a handy extension to do it more quickly
1. Another option is to use
[vsc_quill_delta_to_html](https://pub.dev/packages/vsc_quill_delta_to_html) to convert your document
to HTML.
This package has full support for all Quill operations—including images, videos, formulas,
tables, and mentions.
Conversion can be performed in vanilla Dart (i.e., server-side or CLI) or in Flutter.
It is a complete Dart part of the popular and mature [quill-delta-to-html](https://www.npmjs.com/package/quill-delta-to-html)
Typescript/Javascript package.
this package doesn't convert the HTML back to Quill Delta as far as we know
```
</details>
<details>
<summary>Before</summary>
![image](https://github.com/singerdmx/flutter-quill/assets/73608287/03f5ae20-796c-4e8b-8668-09a994211c1e)
</details>
<details>
<summary>After</summary>
![image](https://github.com/singerdmx/flutter-quill/assets/73608287/7e3a1987-36e7-4665-944a-add87d24e788)
</details>
Markdown, and HTML converting from and to Delta are **currently far from perfect**, the current implementation could improved a lot
however **it will likely not work like expected**, due to differences between HTML and Delta, see this [comment](https://github.com/slab/quill/issues/1551#issuecomment-311458570) for more info.
![Copying Markdown file into Flutter Quill Editor](https://github.com/singerdmx/flutter-quill/assets/73608287/63bd6ba6-cc49-4335-84dc-91a0fa5c95a9)
For more details see #1915
Using or converting to HTML or Markdown is highly experimental and shouldn't be used for production applications.
We use it internally as it is more suitable for our specific use case., copying content from external websites and pasting it into the editor
previously breaks the styles, while the current implementation is not ready, it provides a better user experience and doesn't have many downsides.
Feel free to report any bugs or feature requests at [Issues](https://github.com/singerdmx/flutter-quill/issues) or drop any suggestions and questions at [Discussions](https://github.com/singerdmx/flutter-quill/discussions)
## New Contributors
* @leegh519 made their first contribution in https://github.com/singerdmx/flutter-quill/pull/1911
**Full Changelog**: https://github.com/singerdmx/flutter-quill/compare/v9.3.21...v9.4.0
## 9.3.21
* fix: assertion failure for swipe typing and undo on Android by @crasowas in https://github.com/singerdmx/flutter-quill/pull/1898

@ -1,6 +1,6 @@
name: quill_pdf_converter
description: A extension for flutter_quill package to add support for dealing with conversion to pdf
version: 9.3.21
version: 9.5.0
homepage: https://github.com/singerdmx/flutter-quill/tree/master/quill_pdf_converter/
repository: https://github.com/singerdmx/flutter-quill/tree/master/quill_pdf_converter/
issue_tracker: https://github.com/singerdmx/flutter-quill/issues/

@ -0,0 +1,68 @@
// ignore_for_file: avoid_print
import 'dart:convert';
import 'dart:io' show exit;
import 'package:http/http.dart' as http;
import 'update_package_version.dart';
// A script is used in CI that will fetch release notes using GitHub API
// and create a file from it that will be used by another script
//
// This script should run from root project folder instead of scripts folder
// or others.
const _usage =
'Usage: ./script <github-repository> <release-tag> <github-authorization> (optional)';
Future<void> main(List<String> args) async {
print('📑 Fetch release notes from Github API');
if (args.isEmpty) {
print('Missing required arguments ($args). $_usage');
exit(1);
}
if (args.length > 2) {
print('Too many arguments ($args). $_usage');
exit(1);
}
if (args.length < 2) {
print('Missing arguments ($args). $_usage');
exit(1);
}
final githubRepository = args[0];
final releaseTag = args[1];
final githubAuthorization = args.elementAtOrNull(2);
final response = await http.get(
Uri.parse(
'https://api.github.com/repos/$githubRepository/releases/tags/$releaseTag',
),
headers: {
if (githubAuthorization != null) 'Authorization': githubAuthorization,
},
);
if (response.statusCode != 200) {
print('Response status code is ${response.statusCode} which is not 200');
print('Response body: ${response.body}');
exit(1);
}
final responseBody = response.body;
print(' Validate release notes response');
if (responseBody.trim().isEmpty) {
print('Release notes response is empty.');
exit(1);
}
print('Response body: $responseBody');
final githubReleaseNotes =
(jsonDecode(responseBody) as Map<String, Object?>)['body'] as String?;
if (githubReleaseNotes == null) {
print('Release notes is null.');
exit(1);
}
if (!await versionContentFile.parent.exists()) {
await versionContentFile.parent.create(recursive: true);
}
await versionContentFile.writeAsString(githubReleaseNotes);
}

@ -18,7 +18,7 @@ import 'package:yaml/yaml.dart';
// This must be updated once add or remove some translation keys
// if you update existing keys, no need to update it
const _expectedTranslationKeysLength = 96;
const _expectedTranslationKeysLength = 99;
Future<void> main(List<String> args) async {
final l10nYamlText = await File('l10n.yaml').readAsString();

@ -0,0 +1,24 @@
// ignore_for_file: avoid_print
import 'dart:io' show Directory, Process;
Future<void> main(List<String> args) async {
final generatedDartLocalizationsFolder = Directory('lib/src/l10n/generated');
if (await generatedDartLocalizationsFolder.exists()) {
print(
'Generated directory (${generatedDartLocalizationsFolder.path}) exists, deleting it... 📁',
);
await generatedDartLocalizationsFolder.delete(recursive: true);
}
print('Running flutter pub get... 📦');
await Process.run('flutter', ['pub', 'get']);
print('Running flutter gen-l10n... 🌍');
await Process.run('flutter', ['gen-l10n']);
print('Applying Dart fixes to the newly generated files... 🔧');
await Process.run('dart', ['fix', '--apply', './lib/src/l10n/generated']);
print('Formatting the newly generated Dart files... ✨');
await Process.run('dart', ['format', './lib/src/l10n/generated']);
}

@ -1,29 +0,0 @@
#!/bin/bash
# Important: make sure to run the script in the root folder of the repo:
# ./scripts/regenerate_translations.sh
# otherwise the script could delete the wrong folder in rare cases
# TODO: Refactor this to a dart script to allow developers who use Windows to use it
echo ""
echo "Delete the current generated localizations..."
rm -rf lib/src/l10n/generated
echo ""
echo "Run flutter pub get.."
flutter pub get
echo ""
echo "Run flutter gen-l10n"
flutter gen-l10n
echo ""
echo ""
echo "Apply dart fixes to the newly generated files"
dart fix --apply ./lib/src/l10n/generated
echo ""
echo "Formate the newly generated dart files"
dart format ./lib/src/l10n/generated

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save