diff --git a/.github/ISSUE_TEMPLATE/1_bug.yml b/.github/ISSUE_TEMPLATE/1_bug.yml
new file mode 100644
index 00000000..b4c597dc
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/1_bug.yml
@@ -0,0 +1,118 @@
+name: Report a bug
+description: |
+ You found a bug in Flutter Quill causing your application to crash or
+ throw an exception, a widget is buggy, unexpected behavior or something looks wrong.
+labels: 'bug'
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thank you for using Flutter Quill!
+
+ - type: checkboxes
+ attributes:
+ label: Is there an existing issue for this?
+ options:
+ - label: I have searched the [existing issues](https://github.com/singerdmx/flutter-quill/issues)
+ required: true
+ - type: input
+ attributes:
+ label: Flutter Quill version
+ description: Please tell us which version of `flutter_quill` that you are using.
+ placeholder: For example 9.0.0
+ validations:
+ required: true
+ - type: textarea
+ attributes:
+ label: Other Flutter Quill packages versions
+ description: If you are using any other packages like `flutter_quill_extensions` or `flutter_quill_test` please mention the versions here
+ placeholder: |
+ flutter_quill_extensions: ^0.6.10
+ flutter_quill_test: ^0.0.5
+ validations:
+ required: false
+ - type: textarea
+ attributes:
+ label: Steps to reproduce
+ description: Please tell us exactly how to reproduce the problem you are running into.
+ placeholder: |
+ 1. ...
+ 2. ...
+ 3. ...
+ validations:
+ required: true
+ - type: textarea
+ attributes:
+ label: Expected results
+ description: Please tell us what is expected to happen.
+ validations:
+ required: true
+ - type: textarea
+ attributes:
+ label: Actual results
+ description: Please tell us what is actually happening.
+ validations:
+ required: true
+ - type: textarea
+ attributes:
+ label: Code sample
+ description: |
+ Please create a minimal reproducible sample that shows the problem
+ and attach it below between the lines with the backticks.
+
+ To create it, use the `flutter create bug` command and update the `main.dart` file.
+
+ Alternatively, you can use https://dartpad.dev/ or create a public GitHub
+ repository to share your sample.
+
+ Note: Please do not upload screenshots of text. Instead, use code blocks
+ or the above mentioned ways to upload your code sample.
+ value: |
+ Code sample
+
+ ```dart
+ [Paste your code here]
+ ```
+
+
+ validations:
+ required: true
+ - type: textarea
+ attributes:
+ label: Screenshots or Video
+ description: |
+ Upload any screenshots or video of the bug if applicable.
+ value: |
+
+ Screenshots / Video demonstration
+
+ [Upload media here]
+
+
+ validations:
+ required: false
+ - type: textarea
+ attributes:
+ label: Logs
+ description: |
+ Include the full logs of the commands you are running between the lines
+ with the backticks below. If you are running any `flutter` commands,
+ please include the output of running them with `--verbose`; for example,
+ the output of running `flutter --verbose create foo`.
+
+ If the logs are too large to be uploaded to GitHub, you may upload
+ them as a `txt` file or use online tools like https://pastebin.com to
+ share it.
+
+ Note: Please do not upload screenshots of text. Instead, use code blocks
+ or the above mentioned ways to upload logs.
+ value: |
+ Logs
+
+ ```console
+ [Paste your logs here]
+ ```
+
+
+ validations:
+ required: false
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/2_feature_request.yml b/.github/ISSUE_TEMPLATE/2_feature_request.yml
new file mode 100644
index 00000000..26100b3d
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/2_feature_request.yml
@@ -0,0 +1,41 @@
+name: Feature request
+description: Suggest a new idea for Flutter Quill.
+labels: 'enhancement'
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thank you for using Flutter Quill!
+
+ - type: checkboxes
+ attributes:
+ label: Is there an existing issue for this?
+ description: Please search to see if an issue already exists for this feature request or proposal.
+ options:
+ - label: I have searched the [existing issues](https://github.com/singerdmx/flutter-quill/issues)
+ required: true
+ - type: textarea
+ attributes:
+ label: Use case
+ description: |
+ Please tell us the problem you are running into that led to you wanting
+ a new feature.
+
+ Is your feature request related to a problem? Please give a clear and
+ concise description of what the problem is.
+
+ Describe the alternative solutions you've considered. Is there already a solution that solves this?
+ validations:
+ required: true
+ - type: textarea
+ attributes:
+ label: Proposal
+ description: |
+ Briefly but precisely describe what you would like Flutter Quill to be able to do.
+
+ Consider attaching something showing what you are imagining:
+ * images
+ * videos
+ * code samples
+ validations:
+ required: true
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/3_question.yml b/.github/ISSUE_TEMPLATE/3_question.yml
new file mode 100644
index 00000000..509ae635
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/3_question.yml
@@ -0,0 +1,24 @@
+name: Ask a question
+description: |
+ If you have any questions, feel free to ask
+labels: 'help wanted'
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thank you for using Flutter Quill!
+
+ - type: checkboxes
+ attributes:
+ label: Is there an existing issue for this?
+ options:
+ - label: I have searched the [existing issues](https://github.com/singerdmx/flutter-quill/issues)
+ required: true
+ - type: textarea
+ attributes:
+ label: The question
+ description: Please explain your question here
+ placeholder: |
+ How do I save the images of Quill Editor?
+ validations:
+ required: true
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/issue-template.md b/.github/ISSUE_TEMPLATE/issue-template.md
deleted file mode 100644
index c8a389aa..00000000
--- a/.github/ISSUE_TEMPLATE/issue-template.md
+++ /dev/null
@@ -1,25 +0,0 @@
----
-name: Issue template
-about: Common things to fill
-title: "[Web] or [Mobile] or [Desktop]"
-labels: ''
-assignees: ''
-
----
-
-My issue is about [Web]
-My issue is about [Mobile]
-My issue is about [Desktop]
-
-I have tried running `example` directory successfully before creating an issue here.
-
-Please note that we are using latest flutter version in stable channel on branch master. If you are using beta or master channel, or you are not using latest flutter version in stable channel, you may experience error.
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index c4810500..c62f265d 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,30 +1,20 @@
-# Pull Request
-
## Description
-Provide a brief description of your changes.
+*Replace this paragraph with a description of what this PR is doing. If you're modifying existing behavior, describe the existing behavior, how this PR is changing it, and what motivated the change.*
-## Issues
+## Related Issues
-
-Closes #IssueNumber
-(Replace "IssueNumber" with the actual issue number you are addressing.)
+*Replace this paragraph with a list of issues related to this PR from the [issue database](https://github.com/singerdmx/flutter-quill/issues). Indicate, which of these issues are resolved or fixed by this PR.*
-## Improvements
-
+*e.g.*
+- *Fix #123*
+- *Related #456*
-
-- Improve code readability
-- Improve performance
+## Improvements
+
## Features
-
-
-
-- Add a new feature
-- Allow to customize the widgets
-
-
+
## Additional notes
@@ -34,13 +24,15 @@ Closes #IssueNumber
## Checklist
-
+- [ ] I read the [Contributor Guide](../CONTRIBUTING.md) and followed the process outlined there for submitting PRs.
+- [ ] I titled the PR using [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0).
+- [ ] I did not modify the `CHANGELOG.md` nor the plugin version in `pubspec.yaml` files.
+- [ ] All existing and new tests are passing.
+- [ ] I have run the commands in `./scripts/before-push.sh` and it all passed successfully
+
+## Breaking Change
+
+Does your PR require plugin users to manually update their apps to accommodate your change?
-- [ ] I have added/updated relevant documentation
-- [ ] I have tested these changes locally.
-- [ ] I have followed the code style and guidelines.
-- [ ] I have updated `CHANGELOG.md` with my changes in the next section
-- [ ] I have run `dart format .`` on the project
-- [ ] I have run `dart fix --apply` on the project
-- [ ] I have run `flutter test` and `flutter analyze` and it passed successfully
-- [ ] I have run `./before-push.sh` and everything is fine
+- [ ] Yes, this is a breaking change (please indicate that with a `!` in the title as explained in [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0)).
+- [ ] No, this is *not* a breaking change.
\ No newline at end of file
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index c5314cc0..aa72b67b 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -1,4 +1,4 @@
-name: flutter-quill CI
+name: Flutter Quill CI
on:
push:
@@ -11,10 +11,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
channel: 'stable'
+ cache: true
- name: Check flutter version
run: flutter --version
@@ -25,14 +26,34 @@ jobs:
- name: Install flutter_quill_extensions dependencies
run: flutter pub get -C flutter_quill_extensions
+ - name: Install flutter_quill_test dependencies
+ run: flutter pub get -C flutter_quill_test
+
+ - name: Install quill_html_converter dependencies
+ run: flutter pub get -C packages/quill_html_converter
+
- name: Run flutter analysis
run: flutter analyze
- name: Check dart code formatting
run: dart format --set-exit-if-changed .
+
+ - name: Preview dart proposed changes
+ run: dart fix --dry-run
- name: Check if package is ready for publishing
run: flutter pub publish --dry-run
- name: Run flutter tests
run: flutter test
+
+ - name: Flutter build Web
+ run: flutter build web --release --verbose --dart-define=CI=true
+ working-directory: ./example
+
+ # - name: Install flutter Linux prerequisites
+ # run: sudo apt-get install clang cmake git ninja-build pkg-config libgtk-3-dev liblzma-dev libstdc++-12-dev -y
+
+ # - name: Flutter build Linux
+ # run: flutter build linux --release --verbose --dart-define=CI=true
+ # working-directory: ./example
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
new file mode 100644
index 00000000..644aac7d
--- /dev/null
+++ b/.github/workflows/publish.yml
@@ -0,0 +1,38 @@
+name: Publish to pub.dev
+
+on:
+ push:
+ tags:
+ - 'v[0-9]+.[0-9]+.[0-9]+*'
+
+jobs:
+ publish:
+ permissions:
+ id-token: write
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: subosito/flutter-action@v2
+ with:
+ channel: 'stable'
+
+ - name: Check flutter version
+ run: flutter --version
+
+ # This is needed in order for the authentication to success
+ # pub token add https://pub.dev --env-var PUB_TOKEN
+ # Requests to "https://pub.dev" will now be authenticated using the secret token stored in the environment variable "PUB_TOKEN".
+ - uses: dart-lang/setup-dart@v1
+
+ - name: Install dependencies
+ run: flutter pub get
+
+ # Here you can insert custom steps you need
+ # - run: dart tool/generate-code.dart
+
+ - name: Re-generate the translations
+ run: ./scripts/regenerate-translations.sh
+
+ - name: Publish
+ run: flutter pub publish --force
diff --git a/.github/workflows/welcome.yml b/.github/workflows/welcome.yml
new file mode 100644
index 00000000..54027d81
--- /dev/null
+++ b/.github/workflows/welcome.yml
@@ -0,0 +1,18 @@
+name: 'Welcome New Contributors'
+
+on:
+ issues:
+ types: [opened]
+ pull_request_target:
+ types: [opened]
+
+jobs:
+ welcome-new-contributor:
+ runs-on: ubuntu-latest
+ steps:
+ - name: 'Greet the contributor'
+ uses: garg3133/welcome-new-contributors@v1.2
+ with:
+ token: ${{ secrets.BOT_ACCESS_TOKEN }}
+ issue-message: 'Hello there, on behalf the Flutter Quill Team I would like to thank you for opening your first issue. Your inputs and insights are valuable in shaping a stable and reliable version for all our users. Thank you for being part of the open-source community!'
+ pr-message: 'Hi there, thanks for opening your first Pull Request to this project!!'
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 6b87759f..cee99ed0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@
*.pyc
*.swp
.DS_Store
+**/.DS_Store
.atom/
.buildlog/
.history
@@ -75,3 +76,8 @@ example/ios/Podfile.lock
!**/ios/**/default.pbxuser
!**/ios/**/default.perspectivev3
pubspec.lock
+
+# For local development
+pubspec_overrides.yaml
+
+old_example
\ No newline at end of file
diff --git a/.pubignore b/.pubignore
new file mode 100644
index 00000000..e4e6f2b1
--- /dev/null
+++ b/.pubignore
@@ -0,0 +1,13 @@
+# For local development
+pubspec_overrides.yaml
+pubspec_overrides.yaml.disabled
+
+# Github
+.github/
+
+# The example
+example/.fvm/
+example/build/
+example/.dart_tool/
+
+scripts/
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5776f039..5e024d5d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,549 +1,441 @@
-## [8.1.1]
-- Fix null error in line.dart [#1487](https://github.com/singerdmx/flutter-quill/issues/1487)
+# Changelog
-## [8.1.0]
-- Fixes a word typo of `mirgration` to `migration` in readme & migration document.
-- Updated migration guide
-- Remove property `enableUnfocusOnTapOutside` in QuillEditor Configurations and add `isOnTapOutsideEnabled` instead
-- Add a new callback which is called `onTapOutside` in the `QuillEditorConfigurations` which allow you to do something when tap outside of the edtior
-- Fix a bug which cause the web platform to not unfocus the editor when tap outside of it (the default logic) to override this pleae pass a value to the callback ``onTapOutside``
-- Remove the old proerty of `iconTheme`, instead pass `iconTheme` in the button options, you will find `base` property there, inside it there is `iconTheme`
+All notable changes to this project will be documented in this file.
-## [8.0.0]
-- If you have mirgrated recently, don't get scared from this update, it just add a documentation, mirgration guide and mark the version as more stable release, since we did break a lot of breaking changes (at least that what most developers says) we should have change the major version but when we were in the development of this new version, our time was very tight and now we are fixing the version number
-- It also rename one single property from `code` to `codeBlock` in the `elements` of the new `QuillEditor` Configurations class
-- Updating the README to be more readable
+## 8.5.5
+* Now when opening dialogs by `QuillToolbar` you will not get an exception when you don't use `FlutterQuillLocalizations.delegate` in your `WidgetsApp`, `MaterialApp`, or `CupertinoApp`. The fix is for the `QuillToolbarSearchButton`, `QuillToolbarLinkStyleButton`, and `QuillToolbarColorButton` buttons
-## [7.10.2]
-- Removing line numbers from code block by default, you still can enable this thanks to the new configurations in the `QuillEditor` you will find a `elementOptions` property, in it you will find the code which mean code block options. just pass true to `enableLineNumbers`
+## 8.5.4
+* The `mobileWidth`, `mobileHeight`, `mobileMargin` and `mobileAlignment` is now deprecated in `flutter_quill`, they are are now defined in `flutter_quill_extensions`
+* Deprecate `replaceStyleStringWithSize` function which is in `string.dart`
+* Deprecate `alignment`, and `margin` as they don't conform to official Quill JS
-## [7.10.1]
-- Fixes and use the new parameters
-- You don't need to use MaterialApp anymore to use most of the toolbar buttons childBuilder anymore
-- Compatibility with [fresh_quill_extensions](https://pub.dev/packages/fresh_quill_extensions) which is temporary alternative to [flutter_quill_extensions](https://pub.dev/packages/flutter_quill_extensions)
-- Finally update most of the documentation in `README.md`
+## 8.5.3
+* Update doc
+* Update `README.md` and `CHANGELOG.md`
+* Fix typos
+* Use `immutable` when possible
+* Update `.pubignore`
-## [7.10.0]
-- **Breaking change**: `QuillToolbar.basic()` can be accessed from `QuillToolbar()` directly and the old `QuillToolbar` can be accessed from `QuillBaseToolbar`
-- The Quill editor and toolbar configurations are now refactored in one single class for each one
-- After changing one of the checkbox list values the controller will not request the keyboard focus by default
-- We have moved the configurations of the toolbar and the editor directly into the widget but we are still using inherited widgets internally
-- Fixes to some of the code after the refactoring
+## 8.5.2
+* Updated `README.md`.
+* Feature: Added the ability to include a custom callback when the `QuillToolbarColorButton` is pressed.
+* The `QuillToolbar` now implements `PreferredSizeWidget`, enabling usage in the AppBar, similar to `QuillBaseToolbar`.
-## [7.9.0]
-- Buttons Improvemenets
-- Refactor all the button configurations that used in `QuillToolbar.basic()` but there are still few lefts
-- **Breaking change**: Remove some configurations from the QuillToolbar and move them to the new `QuillProvider`, please notice this is a development version and this might be changed in the next few days, the stable release will be ready in less than 3 weeks
-- Update `flutter_quill_extensions` and it will be published into pub.dev soon.
-- Allow you to customize the search dialog by custom callback with child builder
+## 8.5.1
+* Updated `README.md`.
-## [7.8.0]
-- **Important note**: this is not test release yet, it works but need more test and changes and breaking changes, we don't have development version and it will help us if you try the latest version and report the issues in Github but if you want a stable version please use `7.4.16`. this refactoring process will not take long and should be done less than three weeks with the testing.
-- We managed to refactor most of the buttons configurations and customizations in the `QuillProvider`, only three lefts then will start on refactoring the toolbar configurations
-- Code improvemenets
+## 8.5.0
+* Migrated to `flutter_localizations` for translations.
+* Fixed: Translated all previously untranslated localizations.
+* Fixed: Added translations for missing items.
+* Fixed: Introduced default Chinese fallback translation.
+* Removed: Unused parameters `items` in `QuillToolbarFontFamilyButtonOptions` and `QuillToolbarFontSizeButtonOptions`.
+* Updated: Documentation.
-## [7.7.0]
+## 8.4.4
+* Updated `.pubignore` to ignore unnecessary files and folders.
-- **Breaking change**: We have mirgrated more buttons in the toolbar configurations, you can do change them in the `QuillProvider`
-- Important bug fixes
+## 8.4.3
+* Updated `CHANGELOG.md`.
-## [7.6.1]
+## 8.4.2
+* **Breaking change**: Configuration for `QuillRawEditor` has been moved to a separate class. Additionally, `readOnly` has been renamed to `isReadOnly`. If using `QuillEditor`, no action is required.
+* Introduced the ability for developers to override `TextInputAction` in both `QuillRawEditor` and `QuillEditor`.
+* Enabled using `QuillRawEditor` without `QuillEditorProvider`.
+* Bug fixes.
+* Added image cropping implementation in the example.
+
+## 8.4.1
+* Added `copyWith` in `OptionalSize` class.
-- Bug fixes
+## 8.4.0
+* **Breaking change**: Updated `QuillCustomButton` to use `QuillCustomButtonOptions`. Moved all properties from `QuillCustomButton` to `QuillCustomButtonOptions`, replacing `iconData` with `icon` widget for increased customization.
+* **Breaking change**: `customButtons` in `QuillToolbarConfigurations` is now of type `List`.
+* Bug fixes following the `8.0.0` update.
+* Updated `README.md`.
+* Improved platform checking.
-## [7.6.0]
+## 8.3.0
+* Added `iconButtonFactor` property to `QuillToolbarBaseButtonOptions` for customizing button size relative to its icon size (defaults to `kIconButtonFactor`, consistent with previous releases).
-- **Breaking change**: To customize the buttons in the toolbar, you can do that in the `QuillProvider`
+## 8.2.6
+* Organized `QuillRawEditor` code.
-# [7.5.0]
+## 8.2.5
+* Added `builder` property in `QuillEditorConfigurations`.
-- **Breaking change**: The widgets `QuillEditor` and `QuillToolbar` are no longer have controller parameter, instead you need to make sure in the widget tree you have wrapped them with `QuillProvider` widget and provide the controller and the require configurations
+## 8.2.4
+* Adhered to Flutter best practices.
+* Fixed auto-focus bug.
-# [7.4.16]
+## 8.2.3
+* Updated `README.md`.
-- Update documentation and README.md
+## 8.2.2
+* Moved `flutter_quill_test` to a separate package: [flutter_quill_test](https://pub.dev/packages/flutter_quill_test).
-# [7.4.15]
+## 8.2.1
+* Updated `README.md`.
-- Custom style attrbuites for platforms other than mobile (alignment, margin, width, height)
-- Bug fixes and other improvemenets
+## 8.2.0
+* Added the option to add configurations for `flutter_quill_extensions` using `extraConfigurations`.
-# [7.4.14]
+## 8.1.11
+* Followed Dart best practices by using `lints` and removed `pedantic` and `platform` since they are not used.
+* Fixed text direction bug.
+* Updated `README.md`.
-- Improve performance by reducing the number of widgets rebuilt by listening to media query for only the needed things, for example instead of using `MediaQuery.of(context).size`, now we are using `MediaQuery.sizeOf(context)`
-- Add MediaButton for picking the images only since the video one is not ready
-- A new feature which allows customizing the text selection in quill editor which is useful for custom theme design system for custom app widget
+## 8.1.10
+* Secret for automated publishing to pub.dev.
-# [7.4.13]
+## 8.1.9
+* Fixed automated publishing to pub.dev.
-- Fixed tab editing when in readOnly mode.
+## 8.1.8
+* Fixed automated publishing to pub.dev.
-# [7.4.12]
+## 8.1.7
+* Automated publishing to pub.dev.
-- Update the minimum version of device_info_plus to 9.1.0.
+## 8.1.6
+* Fixed compatibility with `integration_test` by downgrading the minimum version of the platform package to 3.1.0.
-# [7.4.11]
+## 8.1.5
+* Reversed background/font color toolbar button icons.
-- Add sw locale.
+## 8.1.4
+* Reversed background/font color toolbar button tooltips.
-# [7.4.10]
+## 8.1.3
+* Moved images to screenshots instead of `README.md`.
-- Update translations.
+## 8.1.2
+* Fixed a bug related to the regexp of the insert link dialog.
+* Required Dart 3 as the minimum version.
+* Code cleanup.
+* Added a spacer widget between each button in the `QuillToolbar`.
-# [7.4.9]
+## 8.1.1
+* Fixed null error in line.dart #1487(https://github.com/singerdmx/flutter*quill/issues/1487).
-- Style recognition fixes.
+## 8.1.0
+* Fixed a word typo of `mirgration` to `migration` in the readme & migration document.
+* Updated migration guide.
+* Removed property `enableUnfocusOnTapOutside` in `QuillEditor` configurations and added `isOnTapOutsideEnabled` instead.
+* Added a new callback called `onTapOutside` in the `QuillEditorConfigurations` to perform actions when tapping outside the editor.
+* Fixed a bug that caused the web platform to not unfocus the editor when tapping outside of it. To override this, please pass a value to the `onTapOutside` callback.
+* Removed the old property of `iconTheme`. Instead, pass `iconTheme` in the button options; you will find the `base` property inside it with `iconTheme`.
-# [7.4.8]
+## 8.0.0
+* If you have migrated recently, don't be alarmed by this update; it adds documentation, a migration guide, and marks the version as a more stable release. Although there are breaking changes (as reported by some developers), the major version was not changed due to time constraints during development. A single property was also renamed from `code` to `codeBlock` in the `elements` of the new `QuillEditorConfigurations` class.
+* Updated the README for better readability.
-- Upgrade dependencies.
+## 7.10.2
+* Removed line numbers from code blocks by default. You can still enable this feature thanks to the new configurations in the `QuillEditor`. Find the `elementOptions` property and enable `enableLineNumbers`.
-# [7.4.7]
+## 7.10.1
+* Fixed issues and utilized the new parameters.
+* No longer need to use `MaterialApp` for most toolbar button child builders.
+* Compatibility with [fresh_quill_extensions](https://pub.dev/packages/fresh_quill_extensions), a temporary alternative to [flutter_quill_extensions](https://pub.dev/packages/flutter_quill_extensions).
+* Updated most of the documentation in `README.md`.
-- Add Vietnamese and German translations.
+## 7.10.0
+* **Breaking change**: `QuillToolbar.basic()` can be accessed directly from `QuillToolbar()`, and the old `QuillToolbar` can be accessed from `QuillBaseToolbar`.
+* Refactored Quill editor and toolbar configurations into a single class each.
+* After changing checkbox list values, the controller will not request keyboard focus by default.
+* Moved toolbar and editor configurations directly into the widget but still use inherited widgets internally.
+* Fixes to some code after the refactoring.
-# [7.4.6]
+## 7.1.14
-- Fix more null errors in Leaf.retain [#1394](https://github.com/singerdmx/flutter-quill/issues/1394) and Line.delete [#1395](https://github.com/singerdmx/flutter-quill/issues/1395).
+* Add indents change for multiline selection.
-# [7.4.5]
+## 7.1.13
-- Fix null error in Container.insert [#1392](https://github.com/singerdmx/flutter-quill/issues/1392).
+* Add custom recognizer.
-# [7.4.4]
+## 7.1.12
-- Fix extra padding on checklists [#1131](https://github.com/singerdmx/flutter-quill/issues/1131).
+* Add superscript and subscript styles.
-# [7.4.3]
+## 7.1.11
-- Fixed a space input error on iPad.
+* Add inserting indents for lines of list if text is selected.
-# [7.4.2]
+## 7.1.10
-- Fix bug with keepStyleOnNewLine for link.
+* Image embedding tweaks
+ * Add MediaButton which is intened to superseed the ImageButton and VideoButton. Only image selection is working.
+ * Implement image insert for web (image as base64)
-# [7.4.1]
+## 7.1.9
-- Fix toolbar dividers condition.
+* Editor tweaks PR from bambinoua(https://github.com/bambinoua).
+ * Shortcuts now working in Mac OS
+ * QuillDialogTheme is extended with new properties buttonStyle, linkDialogConstraints, imageDialogConstraints, isWrappable, runSpacing,
+ * Added LinkStyleButton2 with new LinkStyleDialog (similar to Quill implementation
+ * Conditinally use Row or Wrap for dialog's children.
+ * Update minimum Dart SDK version to 2.17.0 to use enum extensions.
+ * Use merging shortcuts and actions correclty (if the key combination is the same)
-# [7.4.0]
+## 7.1.8
-- Support Flutter version 3.13.0.
+* Dropdown tweaks
+ * Add itemHeight, itemPadding, defaultItemColor for customization of dropdown items.
+ * Remove alignment property as useless.
+ * Fix bugs with max width when width property is null.
-# [7.3.3]
+## 7.1.7
-- Updated Dependencies conflicting.
-
-# [7.3.2]
-
-- Added builder for custom button in _LinkDialog.
-
-# [7.3.1]
-
-- Added case sensitive and whole word search parameters.
-- Added wrap around.
-- Moved search dialog to the bottom in order not to override the editor and the text found.
-- Other minor search dialog enhancements.
-
-# [7.3.0]
-
-- Add default attributes to basic factory.
-
-# [7.2.19]
-
-- Feat/link regexp.
-
-# [7.2.18]
-
-- Fix paste block text in words apply same style.
-
-# [7.2.17]
-
-- Fix paste text mess up style.
-- Add support copy/cut block text.
-
-# [7.2.16]
-
-- Allow for custom context menu.
-
-# [7.2.15]
-
-- Add flutter_quill.delta library which only exposes Delta datatype.
-
-# [7.2.14]
-
-- Fix errors when the editor is used in the `screenshot` package.
-
-# [7.2.13]
-
-- Fix around image can't delete line break.
-
-# [7.2.12]
-
-- Add support for copy/cut select image and text together.
-
-# [7.2.11]
-
-- Add affinity for localPosition.
-
-# [7.2.10]
-
-- LINE._getPlainText queryChild inclusive=false.
-
-# [7.2.9]
-
-- Add toPlainText method to `EmbedBuilder`.
-
-# [7.2.8]
-
-- Add custom button widget in toolbar.
-
-# [7.2.7]
-
-- Fix language code of Japan.
-
-# [7.2.6]
-
-- Style custom toolbar buttons like builtins.
-
-# [7.2.5]
-
-- Always use text cursor for editor on desktop.
-
-# [7.2.4]
-
-- Fixed keepStyleOnNewLine.
-
-# [7.2.3]
-
-- Get pixel ratio from view.
-
-# [7.2.2]
-
-- Prevent operations on stale editor state.
-
-# [7.2.1]
-
-- Add support for android keyboard content insertion.
-- Enhance color picker, enter hex color and color palette option.
-
-# [7.2.0]
-
-- Checkboxes, bullet points, and number points are now scaled based on the default paragraph font size.
-
-# [7.1.20]
-
-- Pass linestyle to embedded block.
-
-# [7.1.19]
-
-- Fix Rtl leading alignment problem.
-
-# [7.1.18]
-
-- Support flutter latest version.
-
-# [7.1.17+1]
-
-- Updates `device_info_plus` to version 9.0.0 to benefit from AGP 8 (see [changelog#900](https://pub.dev/packages/device_info_plus/changelog#900)).
-
-# [7.1.16]
-
-- Fixed subscript key from 'sup' to 'sub'.
-
-# [7.1.15]
-
-- Fixed a bug introduced in 7.1.7 where each section in `QuillToolbar` was displayed on its own line.
-
-# [7.1.14]
-
-- Add indents change for multiline selection.
-
-# [7.1.13]
-
-- Add custom recognizer.
-
-# [7.1.12]
-
-- Add superscript and subscript styles.
-
-# [7.1.11]
-
-- Add inserting indents for lines of list if text is selected.
-
-# [7.1.10]
-
-- Image embedding tweaks
- - Add MediaButton which is intened to superseed the ImageButton and VideoButton. Only image selection is working.
- - Implement image insert for web (image as base64)
-
-# [7.1.9]
-
-- Editor tweaks PR from [bambinoua](https://github.com/bambinoua).
- - Shortcuts now working in Mac OS
- - QuillDialogTheme is extended with new properties buttonStyle, linkDialogConstraints, imageDialogConstraints, isWrappable, runSpacing,
- - Added LinkStyleButton2 with new LinkStyleDialog (similar to Quill implementation
- - Conditinally use Row or Wrap for dialog's children.
- - Update minimum Dart SDK version to 2.17.0 to use enum extensions.
- - Use merging shortcuts and actions correclty (if the key combination is the same)
-
-# [7.1.8]
-
-- Dropdown tweaks
- - Add itemHeight, itemPadding, defaultItemColor for customization of dropdown items.
- - Remove alignment property as useless.
- - Fix bugs with max width when width property is null.
-
-# [7.1.7]
-
-- Toolbar tweaks.
- - Implement tooltips for embed CameraButton, VideoButton, FormulaButton, ImageButton.
- - Extends customization for SelectAlignmentButton, QuillFontFamilyButton, QuillFontSizeButton adding padding, text style, alignment, width.
- - Add renderFontFamilies to QuillFontFamilyButton to show font faces in dropdown.
- - Add AxisDivider and its named constructors for for use in parent project.
- - Export ToolbarButtons enum to allow specify tooltips for SelectAlignmentButton.
- - Export QuillFontFamilyButton, SearchButton as they were not exported before.
- - Deprecate items property in QuillFontFamilyButton, QuillFontSizeButton as the it can be built usinr rawItemsMap.
- - Make onSelection QuillFontFamilyButton, QuillFontSizeButton omittable as no need to execute callback outside if controller is passed to widget.
+* Toolbar tweaks.
+ * Implement tooltips for embed CameraButton, VideoButton, FormulaButton, ImageButton.
+ * Extends customization for SelectAlignmentButton, QuillFontFamilyButton, QuillFontSizeButton adding padding, text style, alignment, width.
+ * Add renderFontFamilies to QuillFontFamilyButton to show font faces in dropdown.
+ * Add AxisDivider and its named constructors for for use in parent project.
+ * Export ToolbarButtons enum to allow specify tooltips for SelectAlignmentButton.
+ * Export QuillFontFamilyButton, SearchButton as they were not exported before.
+ * Deprecate items property in QuillFontFamilyButton, QuillFontSizeButton as the it can be built usinr rawItemsMap.
+ * Make onSelection QuillFontFamilyButton, QuillFontSizeButton omittable as no need to execute callback outside if controller is passed to widget.
Now the package is more friendly for web projects.
-# [7.1.6]
+## 7.1.6
-- Add enableUnfocusOnTapOutside field to RawEditor and Editor widgets.
+* Add enableUnfocusOnTapOutside field to RawEditor and Editor widgets.
-# [7.1.5]
+## 7.1.5
-- Add tooltips for toolbar buttons.
+* Add tooltips for toolbar buttons.
-# [7.1.4]
+## 7.1.4
-- Fix inserting tab character in lists.
+* Fix inserting tab character in lists.
-# [7.1.3]
+## 7.1.3
-- Fix ios cursor bug when word.length==1.
+* Fix ios cursor bug when word.length==1.
-# [7.1.2]
+## 7.1.2
-- Fix non scrollable editor exception, when tapped under content.
+* Fix non scrollable editor exception, when tapped under content.
-# [7.1.1]
+## 7.1.1
-- customLinkPrefixes parameter - makes possible to open links with custom protoco.
+* customLinkPrefixes parameter * makes possible to open links with custom protoco.
-# [7.1.0]
+## 7.1.0
-- Fix ordered list numeration with several lists in document.
+* Fix ordered list numeration with several lists in document.
-# [7.0.9]
+## 7.0.9
-- Use const constructor for EmbedBuilder.
+* Use const constructor for EmbedBuilder.
-# [7.0.8]
+## 7.0.8
-- Fix IME position bug with scroller.
+* Fix IME position bug with scroller.
-# [7.0.7]
+## 7.0.7
-- Add TextFieldTapRegion for contextMenu.
+* Add TextFieldTapRegion for contextMenu.
-# [7.0.6]
+## 7.0.6
-- Fix line style loss on new line from non string.
+* Fix line style loss on new line from non string.
-# [7.0.5]
+## 7.0.5
-- Fix IME position bug for Mac and Windows.
-- Unfocus when tap outside editor. fix the bug that cant refocus in afterButtonPressed after click ToggleStyleButton on Mac.
+* Fix IME position bug for Mac and Windows.
+* Unfocus when tap outside editor. fix the bug that cant refocus in afterButtonPressed after click ToggleStyleButton on Mac.
-# [7.0.4]
+## 7.0.4
-- Have text selection span full line height for uneven sized text.
+* Have text selection span full line height for uneven sized text.
-# [7.0.3]
+## 7.0.3
-- Fix ordered list numeration for lists with more than one level of list.
+* Fix ordered list numeration for lists with more than one level of list.
-# [7.0.2]
+## 7.0.2
-- Allow widgets to override widget span properties.
+* Allow widgets to override widget span properties.
-# [7.0.1]
+## 7.0.1
-- Update i18n_extension dependency to version 8.0.0.
+* Update i18n_extension dependency to version 8.0.0.
-# [7.0.0]
+## 7.0.0
-- Breaking change: Tuples are no longer used. They have been replaced with a number of data classes.
+* Breaking change: Tuples are no longer used. They have been replaced with a number of data classes.
-# [6.4.4]
+## 6.4.4
-- Increased compatibility with Flutter widget tests.
+* Increased compatibility with Flutter widget tests.
-# [6.4.3]
+## 6.4.3
-- Update dependencies (collection: 1.17.0, flutter_keyboard_visibility: 5.4.0, quiver: 3.2.1, tuple: 2.0.1, url_launcher: 6.1.9, characters: 1.2.1, i18n_extension: 7.0.0, device_info_plus: 8.1.0)
+* Update dependencies (collection: 1.17.0, flutter_keyboard_visibility: 5.4.0, quiver: 3.2.1, tuple: 2.0.1, url_launcher: 6.1.9, characters: 1.2.1, i18n_extension: 7.0.0, device_info_plus: 8.1.0)
-# [6.4.2]
+## 6.4.2
-- Replace `buildToolbar` with `contextMenuBuilder`.
+* Replace `buildToolbar` with `contextMenuBuilder`.
-# [6.4.1]
+## 6.4.1
-- Control the detect word boundary behaviour.
+* Control the detect word boundary behaviour.
-# [6.4.0]
+## 6.4.0
-- Use `axis` to make the toolbar vertical.
-- Use `toolbarIconCrossAlignment` to align the toolbar icons on the cross axis.
-- Breaking change: `QuillToolbar`'s parameter `toolbarHeight` was renamed to `toolbarSize`.
+* Use `axis` to make the toolbar vertical.
+* Use `toolbarIconCrossAlignment` to align the toolbar icons on the cross axis.
+* Breaking change: `QuillToolbar`'s parameter `toolbarHeight` was renamed to `toolbarSize`.
-# [6.3.5]
+## 6.3.5
-- Ability to add custom shortcuts.
+* Ability to add custom shortcuts.
-# [6.3.4]
+## 6.3.4
-- Update clipboard status prior to showing selected text overlay.
+* Update clipboard status prior to showing selected text overlay.
-# [6.3.3]
+## 6.3.3
-- Fixed handling of mac intents.
+* Fixed handling of mac intents.
-# [6.3.2]
+## 6.3.2
-- Added `unknownEmbedBuilder` to QuillEditor.
-- Fix error style when input chinese japanese or korean.
+* Added `unknownEmbedBuilder` to QuillEditor.
+* Fix error style when input chinese japanese or korean.
-# [6.3.1]
+## 6.3.1
-- Add color property to the basic factory function.
+* Add color property to the basic factory function.
-# [6.3.0]
+## 6.3.0
-- Support Flutter 3.7.
+* Support Flutter 3.7.
-# [6.2.2]
+## 6.2.2
-- Fix: nextLine getter null where no assertion.
+* Fix: nextLine getter null where no assertion.
-# [6.2.1]
+## 6.2.1
-- Revert "Align numerical and bullet lists along with text content".
+* Revert "Align numerical and bullet lists along with text content".
-# [6.2.0]
+## 6.2.0
-- Align numerical and bullet lists along with text content.
+* Align numerical and bullet lists along with text content.
-# [6.1.12]
+## 6.1.12
-- Apply i18n for default font dropdown option labels corresponding to 'Clear'.
+* Apply i18n for default font dropdown option labels corresponding to 'Clear'.
-# [6.1.11]
+## 6.1.11
-- Remove iOS hack for delaying focus calculation.
+* Remove iOS hack for delaying focus calculation.
-# [6.1.10]
+## 6.1.10
-- Delay focus calculation for iOS.
+* Delay focus calculation for iOS.
-# [6.1.9]
+## 6.1.9
-- Bump keyboard show up wait to 1 sec.
+* Bump keyboard show up wait to 1 sec.
-# [6.1.8]
+## 6.1.8
-- Recalculate focus when showing keyboard.
+* Recalculate focus when showing keyboard.
-# [6.1.7]
+## 6.1.7
-- Add czech localizations.
+* Add czech localizations.
-# [6.1.6]
+## 6.1.6
-- Upgrade i18n_extension to 6.0.0.
+* Upgrade i18n_extension to 6.0.0.
-# [6.1.5]
+## 6.1.5
-- Fix formatting exception.
+* Fix formatting exception.
-# [6.1.4]
+## 6.1.4
-- Add double quotes validation.
+* Add double quotes validation.
-# [6.1.3]
+## 6.1.3
-- Revert "fix order list numbering (#988)".
+* Revert "fix order list numbering (##988)".
-# [6.1.2]
+## 6.1.2
-- Add typing shortcuts.
+* Add typing shortcuts.
-# [6.1.1]
+## 6.1.1
-- Fix order list numbering.
+* Fix order list numbering.
-# [6.1.0]
+## 6.1.0
-- Add keyboard shortcuts for editor actions.
+* Add keyboard shortcuts for editor actions.
-# [6.0.10]
+## 6.0.10
-- Upgrade device info plus to ^7.0.0.
+* Upgrade device info plus to ^7.0.0.
-# [6.0.9]
+## 6.0.9
-- Don't throw showAutocorrectionPromptRect not implemented. The function is called with every keystroke as a user is typing.
+* Don't throw showAutocorrectionPromptRect not implemented. The function is called with every keystroke as a user is typing.
-# [6.0.8+1]
+## 6.0.8+1
-- Fixes null pointer when setting documents.
+* Fixes null pointer when setting documents.
-# [6.0.8]
+## 6.0.8
-- Make QuillController.document mutable.
+* Make QuillController.document mutable.
-# [6.0.7]
+## 6.0.7
-- Allow disabling of selection toolbar.
+* Allow disabling of selection toolbar.
-# [6.0.6+1]
+## 6.0.6+1
-- Revert 6.0.6.
+* Revert 6.0.6.
-# [6.0.6]
+## 6.0.6
-- Fix wrong custom embed key.
+* Fix wrong custom embed key.
-# [6.0.5]
+## 6.0.5
-- Fixes toolbar buttons stealing focus from editor.
+* Fixes toolbar buttons stealing focus from editor.
-# [6.0.4]
+## 6.0.4
-- Bug fix for Type 'Uint8List' not found.
+* Bug fix for Type 'Uint8List' not found.
-# [6.0.3]
+## 6.0.3
-- Add ability to paste images.
+* Add ability to paste images.
-# [6.0.2]
+## 6.0.2
-- Address Dart Analysis issues.
+* Address Dart Analysis issues.
-# [6.0.1]
+## 6.0.1
-- Changed translation country code (zh_HK -> zh_hk) to lower case, which is required for i18n_extension used in flutter_quill.
-- Add localization in example's main to demonstrate translation.
-- Issue [Windows] selection's copy / paste tool bar not shown #861: add selection's copy / paste toolbar, escape to hide toolbar, mouse right click to show toolbar, ctrl-Y / ctrl-Z to undo / redo.
-- Image and video displayed in Windows platform caused screen flickering while selecting text, a sample_data_nomedia.json asset is added for Desktop to demonstrate the added features.
-- Known issue: keyboard action sometimes causes exception mentioned in Flutter's issue #106475 ([Windows] Keyboard shortcuts stop working after modifier key repeat flutter/flutter#106475).
-- Know issue: user needs to click the editor to get focus before toolbar is able to display.
+* Changed translation country code (zh_HK -> zh_hk) to lower case, which is required for i18n_extension used in flutter_quill.
+* Add localization in example's main to demonstrate translation.
+* Issue Windows selection's copy / paste tool bar not shown ##861: add selection's copy / paste toolbar, escape to hide toolbar, mouse right click to show toolbar, ctrl-Y / ctrl-Z to undo / redo.
+* Image and video displayed in Windows platform caused screen flickering while selecting text, a sample_data_nomedia.json asset is added for Desktop to demonstrate the added features.
+* Known issue: keyboard action sometimes causes exception mentioned in Flutter's issue ##106475 (Windows Keyboard shortcuts stop working after modifier key repeat flutter/flutter##106475).
+* Know issue: user needs to click the editor to get focus before toolbar is able to display.
-# [6.0.0] BREAKING CHANGE
+## 6.0.0 BREAKING CHANGE
-- Removed embed (image, video & formula) blocks from the package to reduce app size.
+* Removed embed (image, video & formula) blocks from the package to reduce app size.
These blocks have been moved to the package `flutter_quill_extensions`, migrate by filling the `embedBuilders` and `embedButtons` parameters as follows:
@@ -561,929 +453,929 @@ QuillToolbar.basic(
);
```
-# [5.4.2]
+## 5.4.2
-- Upgrade i18n_extension.
+* Upgrade i18n_extension.
-# [5.4.1]
+## 5.4.1
-- Update German Translation.
+* Update German Translation.
-# [5.4.0]
+## 5.4.0
-- Added Formula Button (for maths support).
+* Added Formula Button (for maths support).
-# [5.3.2]
+## 5.3.2
-- Add more font family.
+* Add more font family.
-# [5.3.1]
+## 5.3.1
-- Enable search when text is not empty.
+* Enable search when text is not empty.
-# [5.3.0]
+## 5.3.0
-- Added search function.
+* Added search function.
-# [5.2.11]
+## 5.2.11
-- Remove default small color.
+* Remove default small color.
-# [5.2.10]
+## 5.2.10
-- Don't wrap the QuillEditor's child in the EditorTextSelectionGestureDetector if selection is disabled.
+* Don't wrap the QuillEditor's child in the EditorTextSelectionGestureDetector if selection is disabled.
-# [5.2.9]
+## 5.2.9
-- Added option to modify SelectHeaderStyleButton options.
-- Added option to click again on h1, h2, h3 button to go back to normal.
+* Added option to modify SelectHeaderStyleButton options.
+* Added option to click again on h1, h2, h3 button to go back to normal.
-# [5.2.8]
+## 5.2.8
-- Remove tooltip for LinkStyleButton.
-- Make link match regex case insensitive.
+* Remove tooltip for LinkStyleButton.
+* Make link match regex case insensitive.
-# [5.2.7]
+## 5.2.7
-- Add locale to QuillEditor.basic.
+* Add locale to QuillEditor.basic.
-# [5.2.6]
+## 5.2.6
-- Fix keyboard pops up when resizing the image.
+* Fix keyboard pops up when resizing the image.
-# [5.2.5]
+## 5.2.5
-- Upgrade youtube_player_flutter_quill to 8.2.2.
+* Upgrade youtube_player_flutter_quill to 8.2.2.
-# [5.2.4]
+## 5.2.4
-- Upgrade youtube_player_flutter_quill to 8.2.1.
+* Upgrade youtube_player_flutter_quill to 8.2.1.
-# [5.2.3]
+## 5.2.3
-- Flutter Quill Doesn't Work On iOS 16 or Xcode 14 Betas (Stored properties cannot be marked potentially unavailable with '@available').
+* Flutter Quill Doesn't Work On iOS 16 or Xcode 14 Betas (Stored properties cannot be marked potentially unavailable with '@available').
-# [5.2.2]
+## 5.2.2
-- Fix Web Unsupported operation: Platform.\_operatingSystem error.
+* Fix Web Unsupported operation: Platform.\_operatingSystem error.
-# [5.2.1]
+## 5.2.1
-- Rename QuillCustomIcon to QuillCustomButton.
+* Rename QuillCustomIcon to QuillCustomButton.
-# [5.2.0]
+## 5.2.0
-- Support font family selection.
+* Support font family selection.
-# [5.1.1]
+## 5.1.1
-- Update README.
+* Update README.
-# [5.1.0]
+## 5.1.0
-- Added CustomBlockEmbed and customElementsEmbedBuilder.
+* Added CustomBlockEmbed and customElementsEmbedBuilder.
-# [5.0.5]
+## 5.0.5
-- Upgrade device_info_plus to 4.0.0.
+* Upgrade device_info_plus to 4.0.0.
-# [5.0.4]
+## 5.0.4
-- Added onVideoInit callback for video documents.
+* Added onVideoInit callback for video documents.
-# [5.0.3]
+## 5.0.3
-- Update dependencies.
+* Update dependencies.
-# [5.0.2]
+## 5.0.2
-- Keep cursor position on checkbox tap.
+* Keep cursor position on checkbox tap.
-# [5.0.1]
+## 5.0.1
-- Fix static analysis errors.
+* Fix static analysis errors.
-# [5.0.0]
+## 5.0.0
-- Flutter 3.0.0 support.
+* Flutter 3.0.0 support.
-# [4.2.3]
+## 4.2.3
-- Ignore color:inherit and convert double to int for level.
+* Ignore color:inherit and convert double to int for level.
-# [4.2.2]
+## 4.2.2
-- Add clear option to font size dropdown.
+* Add clear option to font size dropdown.
-# [4.2.1]
+## 4.2.1
-- Refactor font size dropdown.
+* Refactor font size dropdown.
-# [4.2.0]
+## 4.2.0
-- Ensure selectionOverlay is available for showToolbar.
+* Ensure selectionOverlay is available for showToolbar.
-# [4.1.9]
+## 4.1.9
-- Using properly iconTheme colors.
+* Using properly iconTheme colors.
-# [4.1.8]
+## 4.1.8
-- Update font size dropdown.
+* Update font size dropdown.
-# [4.1.7]
+## 4.1.7
-- Convert FontSize to a Map to allow for named Font Size.
+* Convert FontSize to a Map to allow for named Font Size.
-# [4.1.6]
+## 4.1.6
-- Update quill_dropdown_button.dart.
+* Update quill_dropdown_button.dart.
-# [4.1.5]
+## 4.1.5
-- Add Font Size dropdown to the toolbar.
+* Add Font Size dropdown to the toolbar.
-# [4.1.4]
+## 4.1.4
-- New borderRadius for iconTheme.
+* New borderRadius for iconTheme.
-# [4.1.3]
+## 4.1.3
-- Fix selection handles show/hide after paste, backspace, copy.
+* Fix selection handles show/hide after paste, backspace, copy.
-# [4.1.2]
+## 4.1.2
-- Add full support for hardware keyboards (Chromebook, Android tablets, etc) that don't alter screen UI.
+* Add full support for hardware keyboards (Chromebook, Android tablets, etc) that don't alter screen UI.
-# [4.1.1]
+## 4.1.1
-- Added textSelectionControls field in QuillEditor.
+* Added textSelectionControls field in QuillEditor.
-# [4.1.0]
+## 4.1.0
-- Added Node to linkActionPickerDelegate.
+* Added Node to linkActionPickerDelegate.
-# [4.0.12]
+## 4.0.12
-- Add Persian(fa) language.
+* Add Persian(fa) language.
-# [4.0.11]
+## 4.0.11
-- Fix cut selection error in multi-node line.
+* Fix cut selection error in multi-node line.
-# [4.0.10]
+## 4.0.10
-- Fix vertical caret position bug.
+* Fix vertical caret position bug.
-# [4.0.9]
+## 4.0.9
-- Request keyboard focus when no child is found.
+* Request keyboard focus when no child is found.
-# [4.0.8]
+## 4.0.8
-- Fix blank lines do not display when --web-renderer=html.
+* Fix blank lines do not display when **web*renderer=html.
-# [4.0.7]
+## 4.0.7
-- Refactor getPlainText (better handling of blank lines and lines with multiple markups.
+* Refactor getPlainText (better handling of blank lines and lines with multiple markups.
-# [4.0.6]
+## 4.0.6
-- Bug fix for copying text with new lines.
+* Bug fix for copying text with new lines.
-# [4.0.5]
+## 4.0.5
-- Fixed casting null to Tuple2 when link dialog is dismissed without any input (e.g. barrier dismissed).
+* Fixed casting null to Tuple2 when link dialog is dismissed without any input (e.g. barrier dismissed).
-# [4.0.4]
+## 4.0.4
-- Bug fix for text direction rtl.
+* Bug fix for text direction rtl.
-# [4.0.3]
+## 4.0.3
-- Support text direction rtl.
+* Support text direction rtl.
-# [4.0.2]
+## 4.0.2
-- Clear toggled style on selection change.
+* Clear toggled style on selection change.
-# [4.0.1]
+## 4.0.1
-- Fix copy/cut/paste/selectAll not working.
+* Fix copy/cut/paste/selectAll not working.
-# [4.0.0]
+## 4.0.0
-- Upgrade for Flutter 2.10.
+* Upgrade for Flutter 2.10.
-# [3.9.11]
+## 3.9.11
-- Added Indonesian translation.
+* Added Indonesian translation.
-# [3.9.10]
+## 3.9.10
-- Fix for undoing a modification ending with an indented line.
+* Fix for undoing a modification ending with an indented line.
-# [3.9.9]
+## 3.9.9
-- iOS: Save image whose filename does not end with image file extension.
+* iOS: Save image whose filename does not end with image file extension.
-# [3.9.8]
+## 3.9.8
-- Added Urdu translation.
+* Added Urdu translation.
-# [3.9.7]
+## 3.9.7
-- Fix for clicking on the Link button without any text on a new line crashes.
+* Fix for clicking on the Link button without any text on a new line crashes.
-# [3.9.6]
+## 3.9.6
-- Apply locale to QuillEditor(contents).
+* Apply locale to QuillEditor(contents).
-# [3.9.5]
+## 3.9.5
-- Fix image pasting.
+* Fix image pasting.
-# [3.9.4]
+## 3.9.4
-- Hiding dialog after selecting action for image.
+* Hiding dialog after selecting action for image.
-# [3.9.3]
+## 3.9.3
-- Update ImageResizer for Android.
+* Update ImageResizer for Android.
-# [3.9.2]
+## 3.9.2
-- Copy image with its style.
+* Copy image with its style.
-# [3.9.1]
+## 3.9.1
-- Support resizing image.
+* Support resizing image.
-# [3.9.0]
+## 3.9.0
-- Image menu options for copy/remove.
+* Image menu options for copy/remove.
-# [3.8.8]
+## 3.8.8
-- Update set textEditingValue.
+* Update set textEditingValue.
-# [3.8.7]
+## 3.8.7
-- Fix checkbox not toggled correctly in toolbar button.
+* Fix checkbox not toggled correctly in toolbar button.
-# [3.8.6]
+## 3.8.6
-- Fix cursor position changes when checking/unchecking the checkbox.
+* Fix cursor position changes when checking/unchecking the checkbox.
-# [3.8.5]
+## 3.8.5
-- Fix \_handleDragUpdate in \_TextSelectionHandleOverlayState.
+* Fix \_handleDragUpdate in \_TextSelectionHandleOverlayState.
-# [3.8.4]
+## 3.8.4
-- Fix link dialog layout.
+* Fix link dialog layout.
-# [3.8.3]
+## 3.8.3
-- Fix for errors on a non scrollable editor.
+* Fix for errors on a non scrollable editor.
-# [3.8.2]
+## 3.8.2
-- Fix certain keys not working on web when editor is a child of a scroll view.
+* Fix certain keys not working on web when editor is a child of a scroll view.
-# [3.8.1]
+## 3.8.1
-- Refactor \_QuillEditorState to QuillEditorState.
+* Refactor \_QuillEditorState to QuillEditorState.
-# [3.8.0]
+## 3.8.0
-- Support pasting with format.
+* Support pasting with format.
-# [3.7.3]
+## 3.7.3
-- Fix selection overlay for collapsed selection.
+* Fix selection overlay for collapsed selection.
-# [3.7.2]
+## 3.7.2
-- Reverted Embed toPlainText change.
+* Reverted Embed toPlainText change.
-# [3.7.1]
+## 3.7.1
-- Change Embed toPlainText to be empty string.
+* Change Embed toPlainText to be empty string.
-# [3.7.0]
+## 3.7.0
-- Replace Toolbar showHistory group with individual showRedo and showUndo.
+* Replace Toolbar showHistory group with individual showRedo and showUndo.
-# [3.6.5]
+## 3.6.5
-- Update Link dialogue for image/video.
+* Update Link dialogue for image/video.
-# [3.6.4]
+## 3.6.4
-- Link dialogue TextInputType.multiline.
+* Link dialogue TextInputType.multiline.
-# [3.6.3]
+## 3.6.3
-- Bug fix for link button text selection.
+* Bug fix for link button text selection.
-# [3.6.2]
+## 3.6.2
-- Improve link button.
+* Improve link button.
-# [3.6.1]
+## 3.6.1
-- Remove SnackBar 'What is entered is not a link'.
+* Remove SnackBar 'What is entered is not a link'.
-# [3.6.0]
+## 3.6.0
-- Allow link button to enter text.
+* Allow link button to enter text.
-# [3.5.3]
+## 3.5.3
-- Change link button behavior.
+* Change link button behavior.
-# [3.5.2]
+## 3.5.2
-- Bug fix for embed.
+* Bug fix for embed.
-# [3.5.1]
+## 3.5.1
-- Bug fix for platform util.
+* Bug fix for platform util.
-# [3.5.0]
+## 3.5.0
-- Removed redundant classes.
+* Removed redundant classes.
-# [3.4.4]
+## 3.4.4
-- Add more translations.
+* Add more translations.
-# [3.4.3]
+## 3.4.3
-- Preset link from attributes.
+* Preset link from attributes.
-# [3.4.2]
+## 3.4.2
-- Fix launch link edit mode.
+* Fix launch link edit mode.
-# [3.4.1]
+## 3.4.1
-- Placeholder effective in scrollable.
+* Placeholder effective in scrollable.
-# [3.4.0]
+## 3.4.0
-- Option to save image in read-only mode.
+* Option to save image in read-only mode.
-# [3.3.1]
+## 3.3.1
-- Pass any specified key in QuillEditor constructor to super.
+* Pass any specified key in QuillEditor constructor to super.
-# [3.3.0]
+## 3.3.0
-- Fixed Style toggle issue.
+* Fixed Style toggle issue.
-# [3.2.1]
+## 3.2.1
-- Added new translations.
+* Added new translations.
-# [3.2.0]
+## 3.2.0
-- Support multiple links insertion on the go.
+* Support multiple links insertion on the go.
-# [3.1.1]
+## 3.1.1
-- Add selection completed callback.
+* Add selection completed callback.
-# [3.1.0]
+## 3.1.0
-- Fixed image ontap functionality.
+* Fixed image ontap functionality.
-# [3.0.4]
+## 3.0.4
-- Add maxContentWidth constraint to editor.
+* Add maxContentWidth constraint to editor.
-# [3.0.3]
+## 3.0.3
-- Do not show caret on screen when the editor is not focused.
+* Do not show caret on screen when the editor is not focused.
-# [3.0.2]
+## 3.0.2
-- Fix launch link for read-only mode.
+* Fix launch link for read-only mode.
-# [3.0.1]
+## 3.0.1
-- Handle null value of Attribute.link.
+* Handle null value of Attribute.link.
-# [3.0.0]
+## 3.0.0
-- Launch link improvements.
-- Removed QuillSimpleViewer.
+* Launch link improvements.
+* Removed QuillSimpleViewer.
-# [2.5.2]
+## 2.5.2
-- Skip image when pasting.
+* Skip image when pasting.
-# [2.5.1]
+## 2.5.1
-- Bug fix for Desktop `Shift` + `Click` support.
+* Bug fix for Desktop `Shift` + `Click` support.
-# [2.5.0]
+## 2.5.0
-- Update checkbox list.
+* Update checkbox list.
-# [2.4.1]
+## 2.4.1
-- Desktop selection improvements.
+* Desktop selection improvements.
-# [2.4.0]
+## 2.4.0
-- Improve inline code style.
+* Improve inline code style.
-# [2.3.3]
+## 2.3.3
-- Improves selection rects to have consistent height regardless of individual segment text styles.
+* Improves selection rects to have consistent height regardless of individual segment text styles.
-# [2.3.2]
+## 2.3.2
-- Allow disabling floating cursor.
+* Allow disabling floating cursor.
-# [2.3.1]
+## 2.3.1
-- Preserve last newline character on delete.
+* Preserve last newline character on delete.
-# [2.3.0]
+## 2.3.0
-- Massive changes to support flutter 2.8.
+* Massive changes to support flutter 2.8.
-# [2.2.2]
+## 2.2.2
-- iOS - floating cursor.
+* iOS - floating cursor.
-# [2.2.1]
+## 2.2.1
-- Bug fix for imports supporting flutter 2.8.
+* Bug fix for imports supporting flutter 2.8.
-# [2.2.0]
+## 2.2.0
-- Support flutter 2.8.
+* Support flutter 2.8.
-# [2.1.1]
+## 2.1.1
-- Add methods of clearing editor and moving cursor.
+* Add methods of clearing editor and moving cursor.
-# [2.1.0]
+## 2.1.0
-- Add delete handler.
+* Add delete handler.
-# [2.0.23]
+## 2.0.23
-- Support custom replaceText handler.
+* Support custom replaceText handler.
-# [2.0.22]
+## 2.0.22
-- Fix attribute compare and fix font size parsing.
+* Fix attribute compare and fix font size parsing.
-# [2.0.21]
+## 2.0.21
-- Handle click on embed object.
+* Handle click on embed object.
-# [2.0.20]
+## 2.0.20
-- Improved UX/UI of Image widget.
+* Improved UX/UI of Image widget.
-# [2.0.19]
+## 2.0.19
-- When uploading a video, applying indicator.
+* When uploading a video, applying indicator.
-# [2.0.18]
+## 2.0.18
-- Make toolbar dividers optional.
+* Make toolbar dividers optional.
-# [2.0.17]
+## 2.0.17
-- Allow alignment of the toolbar icons to match WrapAlignment.
+* Allow alignment of the toolbar icons to match WrapAlignment.
-# [2.0.16]
+## 2.0.16
-- Add hide / show alignment buttons.
+* Add hide / show alignment buttons.
-# [2.0.15]
+## 2.0.15
-- Implement change cursor to SystemMouseCursors.click when hovering a link styled text.
+* Implement change cursor to SystemMouseCursors.click when hovering a link styled text.
-# [2.0.14]
+## 2.0.14
-- Enable customize the checkbox widget using DefaultListBlockStyle style.
+* Enable customize the checkbox widget using DefaultListBlockStyle style.
-# [2.0.13]
+## 2.0.13
-- Improve the scrolling performance by reducing the repaint areas.
+* Improve the scrolling performance by reducing the repaint areas.
-# [2.0.12]
+## 2.0.12
-- Fix the selection effect can't be seen as the textLine with background color.
+* Fix the selection effect can't be seen as the textLine with background color.
-# [2.0.11]
+## 2.0.11
-- Fix visibility of text selection handlers on scroll.
+* Fix visibility of text selection handlers on scroll.
-# [2.0.10]
+## 2.0.10
-- cursorConnt.color notify the text_line to repaint if it was disposed.
+* cursorConnt.color notify the text_line to repaint if it was disposed.
-# [2.0.9]
+## 2.0.9
-- Improve UX when trying to add a link.
+* Improve UX when trying to add a link.
-# [2.0.8]
+## 2.0.8
-- Adding translations to the toolbar.
+* Adding translations to the toolbar.
-# [2.0.7]
+## 2.0.7
-- Added theming options for toolbar icons and LinkDialog.
+* Added theming options for toolbar icons and LinkDialog.
-# [2.0.6]
+## 2.0.6
-- Avoid runtime error when placed inside TabBarView.
+* Avoid runtime error when placed inside TabBarView.
-# [2.0.5]
+## 2.0.5
-- Support inline code formatting.
+* Support inline code formatting.
-# [2.0.4]
+## 2.0.4
-- Enable history shortcuts for desktop.
+* Enable history shortcuts for desktop.
-# [2.0.3]
+## 2.0.3
-- Fix cursor when line contains image.
+* Fix cursor when line contains image.
-# [2.0.2]
+## 2.0.2
-- Address KeyboardListener class name conflict.
+* Address KeyboardListener class name conflict.
-# [2.0.1]
+## 2.0.1
-- Upgrade flutter_colorpicker to 0.5.0.
+* Upgrade flutter_colorpicker to 0.5.0.
-# [2.0.0]
+## 2.0.0
-- Text Alignment functions + Block Format standards.
+* Text Alignment functions + Block Format standards.
-# [1.9.6]
+## 1.9.6
-- Support putting QuillEditor inside a Scrollable view.
+* Support putting QuillEditor inside a Scrollable view.
-# [1.9.5]
+## 1.9.5
-- Skip image when pasting.
+* Skip image when pasting.
-# [1.9.4]
+## 1.9.4
-- Bug fix for cursor position when tapping at the end of line with image(s).
+* Bug fix for cursor position when tapping at the end of line with image(s).
-# [1.9.3]
+## 1.9.3
-- Bug fix when line only contains one image.
+* Bug fix when line only contains one image.
-# [1.9.2]
+## 1.9.2
-- Support for building custom inline styles.
+* Support for building custom inline styles.
-# [1.9.1]
+## 1.9.1
-- Cursor jumps to the most appropriate offset to display selection.
+* Cursor jumps to the most appropriate offset to display selection.
-# [1.9.0]
+## 1.9.0
-- Support inline image.
+* Support inline image.
-# [1.8.3]
+## 1.8.3
-- Updated quill_delta.
+* Updated quill_delta.
-# [1.8.2]
+## 1.8.2
-- Support mobile image alignment.
+* Support mobile image alignment.
-# [1.8.1]
+## 1.8.1
-- Support mobile custom size image.
+* Support mobile custom size image.
-# [1.8.0]
+## 1.8.0
-- Support entering link for image/video.
+* Support entering link for image/video.
-# [1.7.3]
+## 1.7.3
-- Bumps photo_view version.
+* Bumps photo_view version.
-# [1.7.2]
+## 1.7.2
-- Fix static analysis error.
+* Fix static analysis error.
-# [1.7.1]
+## 1.7.1
-- Support Youtube video.
+* Support Youtube video.
-# [1.7.0]
+## 1.7.0
-- Support video.
+* Support video.
-# [1.6.4]
+## 1.6.4
-- Bug fix for clear format button.
+* Bug fix for clear format button.
-# [1.6.3]
+## 1.6.3
-- Fixed dragging right handle scrolling issue.
+* Fixed dragging right handle scrolling issue.
-# [1.6.2]
+## 1.6.2
-- Fixed the position of the selection status drag handle.
+* Fixed the position of the selection status drag handle.
-# [1.6.1]
+## 1.6.1
-- Upgrade image_picker and flutter_colorpicker.
+* Upgrade image_picker and flutter_colorpicker.
-# [1.6.0]
+## 1.6.0
-- Support Multi Row Toolbar.
+* Support Multi Row Toolbar.
-# [1.5.0]
+## 1.5.0
-- Remove file_picker dependency.
+* Remove file_picker dependency.
-# [1.4.1]
+## 1.4.1
-- Remove filesystem_picker dependency.
+* Remove filesystem_picker dependency.
-# [1.4.0]
+## 1.4.0
-- Remove path_provider dependency.
+* Remove path_provider dependency.
-# [1.3.4]
+## 1.3.4
-- Add option to paintCursorAboveText.
+* Add option to paintCursorAboveText.
-# [1.3.3]
+## 1.3.3
-- Upgrade file_picker version.
+* Upgrade file_picker version.
-# [1.3.2]
+## 1.3.2
-- Fix copy/paste bug.
+* Fix copy/paste bug.
-# [1.3.1]
+## 1.3.1
-- New logo.
+* New logo.
-# [1.3.0]
+## 1.3.0
-- Support flutter 2.2.0.
+* Support flutter 2.2.0.
-# [1.2.2]
+## 1.2.2
-- Checkbox supports tapping.
+* Checkbox supports tapping.
-# [1.2.1]
+## 1.2.1
-- Indented position not holding while editing.
+* Indented position not holding while editing.
-# [1.2.0]
+## 1.2.0
-- Fix image button cancel causes crash.
+* Fix image button cancel causes crash.
-# [1.1.8]
+## 1.1.8
-- Fix height of empty line bug.
+* Fix height of empty line bug.
-# [1.1.7]
+## 1.1.7
-- Fix text selection in read-only mode.
+* Fix text selection in read-only mode.
-# [1.1.6]
+## 1.1.6
-- Remove universal_html dependency.
+* Remove universal_html dependency.
-# [1.1.5]
+## 1.1.5
-- Enable "Select", "Select All" and "Copy" in read-only mode.
+* Enable "Select", "Select All" and "Copy" in read-only mode.
-# [1.1.4]
+## 1.1.4
-- Fix text selection issue.
+* Fix text selection issue.
-# [1.1.3]
+## 1.1.3
-- Update example folder.
+* Update example folder.
-# [1.1.2]
+## 1.1.2
-- Add pedantic.
+* Add pedantic.
-# [1.1.1]
+## 1.1.1
-- Base64 image support.
+* Base64 image support.
-# [1.1.0]
+## 1.1.0
-- Support null safety.
+* Support null safety.
-# [1.0.9]
+## 1.0.9
-- Web support for raw editor and keyboard listener.
+* Web support for raw editor and keyboard listener.
-# [1.0.8]
+## 1.0.8
-- Support token attribute.
+* Support token attribute.
-# [1.0.7]
+## 1.0.7
-- Fix crash on web (dart:io).
+* Fix crash on web (dart:io).
-# [1.0.6]
+## 1.0.6
-- Add desktop support - WINDOWS, MACOS and LINUX.
+* Add desktop support WINDOWS, MACOS and LINUX.
-# [1.0.5]
+## 1.0.5
-- Bug fix: Can not insert newline when Bold is toggled ON.
+* Bug fix: Can not insert newline when Bold is toggled ON.
-# [1.0.4]
+## 1.0.4
-- Upgrade photo_view to ^0.11.0.
+* Upgrade photo_view to ^0.11.0.
-# [1.0.3]
+## 1.0.3
-- Fix issue that text is not displayed while typing [WEB].
+* Fix issue that text is not displayed while typing WEB.
-# [1.0.2]
+## 1.0.2
-- Update toolbar in sample home page.
+* Update toolbar in sample home page.
-# [1.0.1]
+## 1.0.1
-- Fix static analysis errors.
+* Fix static analysis errors.
-# [1.0.0]
+## 1.0.0
-- Support flutter 2.0.
+* Support flutter 2.0.
-# [1.0.0-dev.2]
+## 1.0.0-dev.2
-- Improve link handling for tel, mailto and etc.
+* Improve link handling for tel, mailto and etc.
-# [1.0.0-dev.1]
+## 1.0.0-dev.1
-- Upgrade prerelease SDK & Bump for master.
+* Upgrade prerelease SDK & Bump for master.
-# [0.3.5]
+## 0.3.5
-- Fix for cursor focus issues when keyboard is on.
+* Fix for cursor focus issues when keyboard is on.
-# [0.3.4]
+## 0.3.4
-- Improve link handling for tel, mailto and etc.
+* Improve link handling for tel, mailto and etc.
-# [0.3.3]
+## 0.3.3
-- More fix on cursor focus issue when keyboard is on.
+* More fix on cursor focus issue when keyboard is on.
-# [0.3.2]
+## 0.3.2
-- Fix cursor focus issue when keyboard is on.
+* Fix cursor focus issue when keyboard is on.
-# [0.3.1]
+## 0.3.1
-- cursor focus when keyboard is on.
+* cursor focus when keyboard is on.
-# [0.3.0]
+## 0.3.0
-- Line Height calculated based on font size.
+* Line Height calculated based on font size.
-# [0.2.12]
+## 0.2.12
-- Support placeholder.
+* Support placeholder.
-# [0.2.11]
+## 0.2.11
-- Fix static analysis error.
+* Fix static analysis error.
-# [0.2.10]
+## 0.2.10
-- Update TextInputConfiguration autocorrect to true in stable branch.
+* Update TextInputConfiguration autocorrect to true in stable branch.
-# [0.2.9]
+## 0.2.9
-- Update TextInputConfiguration autocorrect to true.
+* Update TextInputConfiguration autocorrect to true.
-# [0.2.8]
+## 0.2.8
-- Support display local image besides network image in stable branch.
+* Support display local image besides network image in stable branch.
-# [0.2.7]
+## 0.2.7
-- Support display local image besides network image.
+* Support display local image besides network image.
-# [0.2.6]
+## 0.2.6
-- Fix cursor after pasting.
+* Fix cursor after pasting.
-# [0.2.5]
+## 0.2.5
-- Toggle text/background color button in toolbar.
+* Toggle text/background color button in toolbar.
-# [0.2.4]
+## 0.2.4
-- Support the use of custom icon size in toolbar.
+* Support the use of custom icon size in toolbar.
-# [0.2.3]
+## 0.2.3
-- Support custom styles and image on local device storage without uploading.
+* Support custom styles and image on local device storage without uploading.
-# [0.2.2]
+## 0.2.2
-- Update git repo.
+* Update git repo.
-# [0.2.1]
+## 0.2.1
-- Fix static analysis error.
+* Fix static analysis error.
-# [0.2.0]
+## 0.2.0
-- Add checked/unchecked list button in toolbar.
+* Add checked/unchecked list button in toolbar.
-# [0.1.8]
+## 0.1.8
-- Support font and size attributes.
+* Support font and size attributes.
-# [0.1.7]
+## 0.1.7
-- Support checked/unchecked list.
+* Support checked/unchecked list.
-# [0.1.6]
+## 0.1.6
-- Fix getExtentEndpointForSelection.
+* Fix getExtentEndpointForSelection.
-# [0.1.5]
+## 0.1.5
-- Support text alignment.
+* Support text alignment.
-# [0.1.4]
+## 0.1.4
-- Handle url with trailing spaces.
+* Handle url with trailing spaces.
-# [0.1.3]
+## 0.1.3
-- Handle cursor position change when undo/redo.
+* Handle cursor position change when undo/redo.
-# [0.1.2]
+## 0.1.2
-- Handle more text colors.
+* Handle more text colors.
-# [0.1.1]
+## 0.1.1
-- Fix cursor issue when undo.
+* Fix cursor issue when undo.
-# [0.1.0]
+## 0.1.0
-- Fix insert image.
+* Fix insert image.
-# [0.0.9]
+## 0.0.9
-- Handle rgba color.
+* Handle rgba color.
-# [0.0.8]
+## 0.0.8
-- Fix launching url.
+* Fix launching url.
-# [0.0.7]
+## 0.0.7
-- Handle multiple image inserts.
+* Handle multiple image inserts.
-# [0.0.6]
+## 0.0.6
-- More toolbar functionality.
+* More toolbar functionality.
-# [0.0.5]
+## 0.0.5
-- Update example.
+* Update example.
-# [0.0.4]
+## 0.0.4
-- Update example.
+* Update example.
-# [0.0.3]
+## 0.0.3
-- Update home page meta data.
+* Update home page meta data.
-# [0.0.2]
+## 0.0.2
-- Support image upload and launch url in read-only mode.
+* Support image upload and launch url in read-only mode.
-# [0.0.1]
+## 0.0.1
-- Rich text editor based on Quill Delta.
+* Rich text editor based on Quill Delta.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 00000000..85d81a00
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,64 @@
+# Contributing
+
+The contributions are more than welcome!
+This project will be better with the open-source community help
+
+You can check the [Todo](./doc/todo.md) list if you want to
+
+There are no guidelines for now.
+This page will be updated in the future.
+
+## Steps to contributing
+
+You will need a GitHub account as well as Git installed and configured with your GitHub account on your machine
+
+1. Fork the repository in GitHub
+2. clone the forked repository using `git`
+3. Add the `upstream` repository using:
+ ```
+ 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.
+5. Create a new git branch and switch to it using:
+
+ ```
+ git checkout -b your-branch-name
+ ```
+ The `your-branch-name` is your choice
+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 libraries (`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:
+ ```
+ ./scripts/enable_local_dev.sh
+ ```
+8. Test them in the [example](./example) and add changes in there if necessary
+9. Mention the new changes in the [CHANGELOG.md](../CHANGELOG.md) in the next block
+10. Run the following script if possible
+ ```
+ ./scripts/before-push.sh
+ ```
+11. When you are done sending your pull request, run:
+ ```
+ git add .
+ git commit -m "Your commit message"
+ git push origin your-branch-name
+ ```
+ this will push the new branch to your forked repository
+12. 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
+13. Please wait for the review, and we might ask you to make more changes, then run:
+```
+git add .
+git commit -m "Your new commit message"
+git push origin your-branch-name
+```
+
+Thank you for your time and efforts in this open-source community project!!
+
+## Development Notes
+Please read the [Development Notes](./doc/development_notes.md) as they are important while development
\ No newline at end of file
diff --git a/FETCH_HEAD b/FETCH_HEAD
deleted file mode 100644
index e69de29b..00000000
diff --git a/LICENSE b/LICENSE
index 498b3e0a..aaec6d89 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2020 Xin Yao
+Copyright (c) 2023 Xin Yao
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index 916a1642..3db5e322 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@
[![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]: https://github.com/singerdmx/flutter-quill/blob/master/LICENSE
+[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
@@ -26,51 +26,51 @@
FlutterQuill is a rich text editor and a [Quill] component for [Flutter].
-This library is a WYSIWYG editor built for the modern Android, iOS, web and desktop platforms. Check out our [Youtube Playlist] or [Code Introduction] to take a detailed walkthrough of the code base. You can join our [Slack Group] for discussion.
+This library is a WYSIWYG (What You See Is What You Get) editor built
+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.
-Pub: [FlutterQuill]
+Pub: [FlutterQuill]
+If you are viewing this page from pub.dev page, then you
+might have some issues with opening some links, please open
+it in GitHub repo instead.
## Table of contents
- [Flutter Quill](#flutter-quill)
- [Table of contents](#table-of-contents)
- - [Demo](#demo)
+ - [Screenshots](#screenshots)
- [Installation](#installation)
- [Usage](#usage)
- [Migration](#migration)
- [Input / Output](#input--output)
+ - [Links](#links)
- [Configurations](#configurations)
- - [Using Custom App Widget](#using-custom-app-widget)
- - [Font Size](#font-size)
+ - [Links](#links-1)
- [Font Family](#font-family)
- - [Custom Buttons](#custom-buttons)
- [Embed Blocks](#embed-blocks)
- [Using the embed blocks from `flutter_quill_extensions`](#using-the-embed-blocks-from-flutter_quill_extensions)
- - [Custom Size Image for Mobile](#custom-size-image-for-mobile)
- - [Custom Size Image for other platforms (excluding web)](#custom-size-image-for-other-platforms-excluding-web)
- - [Custom Embed Blocks](#custom-embed-blocks)
- - [Custom Toolbar](#custom-toolbar)
- - [Translation](#translation)
- - [](#)
- - [Contributing to translations](#contributing-to-translations)
+ - [Links](#links-2)
- [Conversion to HTML](#conversion-to-html)
+ - [Translation](#translation)
- [Testing](#testing)
- - [License](#license)
- [Contributors](#contributors)
- - [Sponsors](#sponsors)
-## Demo
+## Screenshots
-
-
-
-
+
+Tap to show/hide screenshots
-
-
-
-
+
----
+
+
+
+
+
+
## Installation
@@ -87,24 +87,37 @@ dependencies:
git: https://github.com/singerdmx/flutter-quill.git
```
-> **Important note**
+
>
-> Currently, we're in the process of refactoring the library's configurations. We're actively working on this, and while we don't have a development version available at the moment, your feedback is essential to us.
+> Note: At this time, we are making too many changes to the library, and you might see a new version almost every day
>
-> 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 our users. 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 our users. Thank you for being part of the open-source community!
>
-> also [flutter_quill_extensions](https://pub.dev/packages/flutter_quill_extensions) will not work with the latest versions, please use [fresh_quill_extensions](https://pub.dev/packages/fresh_quill_extensions) as temporary alternative
+> If the latest version of [FlutterQuill Extensions] is pre-release, then please use it to work with the latest stable version of [FlutterQuill]
>
+Compatible versions:
+
+| flutter_quill | flutter_quill_extensions | flutter_quill_test |
+|-------------------------|--------------------------|-------------------------|
+| 8.5.x | 0.6.x | 0.0.5 |
+| 8.5.1 | 0.6.7 | 0.0.5 |
+| 8.5.0 | 0.6.7 | 0.0.5 |
+
+These versions are tested and well-supported, you shouldn't get a build failure
+
## Usage
-See the `example` directory for a minimal example of how to use FlutterQuill. You typically just need to instantiate a controller:
+First, you need to instantiate a controller
```dart
QuillController _controller = QuillController.basic();
```
-and then embed the toolbar and the editor, within your app. For example:
+And then use the `QuillEditor`, `QuillToolbar` widgets,
+connect the `QuillController` to them
+using `QuillProvider` inherited widget
```dart
QuillProvider(
@@ -129,147 +142,75 @@ QuillProvider(
)
```
-And depending on your use case, you might want to dispose the `_controller` in dispose mehtod
+And depending on your use case, you might want to dispose the `_controller` in dispose method
+
+in most cases, it's better to.
Check out [Sample Page] for more advanced usage.
## Migration
-We have recently add [migration guide](/doc/migration.md) for migration from different versions
+Starting from version `8.0.0`
+We have added [Migration Guide](/doc/migration.md) for migration from different versions
## Input / Output
-This library uses [Quill] as an internal data format.
+This library uses [Quill Delta](https://quilljs.com/docs/delta/)
+to represent the document content.
+The Delta format is a compact and versatile way to describe document changes.
+It consists of a series of operations, each representing an insertion, deletion,
+or formatting change within the document.
+
+Don’t be confused by its name Delta—Deltas represents both documents and changes to documents.
+If you think of Deltas as the instructions from going from one document to another,
+the way Deltas represent a document is by expressing the instructions starting from an empty document.
* Use `_controller.document.toDelta()` to extract the deltas.
* Use `_controller.document.toPlainText()` to extract plain text.
-FlutterQuill provides some JSON serialization support, so that you can save and open documents. To save a document as JSON, do something like the following:
+FlutterQuill provides some JSON serialization support, so that you can save and open documents.
+To save a document as JSON, do something like the following:
```dart
-var json = jsonEncode(_controller.document.toDelta().toJson());
+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, you can do something like this:
+To open a FlutterQuill editor with an existing JSON representation that you've previously stored,
+you can do something like this:
```dart
-var myJSON = jsonDecode(r'{"insert":"hello\n"}');
-_controller = QuillController(
- document: Document.fromJson(myJSON),
- selection: TextSelection.collapsed(offset: 0),
- );
-```
-
-## Configurations
-
-The `QuillToolbar` class lets you customize which formatting options are available.
-[Sample Page] provides sample code for advanced usage and configuration.
-
-For **web development**, use `flutter config --enable-web` for flutter or use [ReactQuill] for React.
-
-It is required to provide `EmbedBuilder`, e.g. [defaultEmbedBuildersWeb](https://github.com/singerdmx/flutter-quill/blob/master/example/lib/universal_ui/universal_ui.dart#L99).
-Also it is required to provide `webImagePickImpl`, e.g. [Sample Page](https://github.com/singerdmx/flutter-quill/blob/master/example/lib/pages/home_page.dart#L317).
-
-For **desktop platforms** It is required to provide `filePickImpl` for toolbar image button, e.g. [Sample Page](https://github.com/singerdmx/flutter-quill/blob/master/example/lib/pages/home_page.dart#L297).
-
-
-### Using Custom App Widget
+final json = jsonDecode(r'{"insert":"hello\n"}');
-This project use some adaptive widgets like `AdaptiveTextSelectionToolbar` which require the following delegates:
-
-1. Default Material Localizations delegate
-2. Default Cupertino Localizations delegate
-3. Defualt Widgets Localizations delegate
-
-You don't need to include those since there are defined by default
- but if you are using Custom app or you are overriding the `localizationsDelegates` in the App widget
-then please make sure it's including those:
-
-```dart
-localizationsDelegates: const [
- DefaultCupertinoLocalizations.delegate,
- DefaultMaterialLocalizations.delegate,
- DefaultWidgetsLocalizations.delegate,
-],
-```
-
-And you might need more depending on your use case, for example if you are using custom localizations for your app, using custom app widget like [FluentApp](https://pub.dev/packages/fluent_ui)
-which will also need
-
-```dart
-localizationsDelegates: const [
- // Required localizations delegates ...
- FluentLocalizations.delegate,
- AppLocalizations.delegate,
-],
+_controller.document = Document.fromJson(json);
```
-in addition to the required delegates by this library
-
-### Font Size
-
-Within the editor toolbar, a drop-down with font-sizing capabilities is available. This can be enabled or disabled with `showFontSize`.
+### Links
-When enabled, the default font-size values can be modified via _optional_ `fontSizeValues`. `fontSizeValues` accepts a `Map` consisting of a `String` title for the font size and a `String` value for the font size. Example:
+- [Quill Delta](https://quilljs.com/docs/delta/)
+- [Quill Delta Formats](https://quilljs.com/docs/formats)
+- [Why Quill](https://quilljs.com/guides/why-quill/)
+- [Quill JS Configurations](https://quilljs.com/docs/configuration/)
+- [Quill JS Interactive Playground](https://quilljs.com/playground/)
+- [Quill JS GitHub repo](https://github.com/quilljs/quill)
-```dart
-fontSizeValues: const {'Small': '8', 'Medium': '24.5', 'Large': '46'}
-```
+## Configurations
-Font size can be cleared with a value of `0`, for example:
+The `QuillToolbar` and `QuillEditor` widgets lets you customize a lot of things
+[Sample Page] provides sample code for advanced usage and configuration.
-```dart
-fontSizeValues: const {'Small': '8', 'Medium': '24.5', 'Large': '46', 'Clear': '0'}
-```
+### 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
-To use your own fonts, update your [assets folder](https://github.com/singerdmx/flutter-quill/tree/master/example/assets/fonts) and pass in `fontFamilyValues`. More details at [this change](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/).
-
-### Custom Buttons
-
-You may add custom buttons to the _end_ of the toolbar, via the `customButtons` option, which is a `List` of `QuillCustomButton`.
-
-To add an Icon, we should use a new QuillCustomButton class
-
-```dart
- QuillCustomButton(
- iconData: Icons.ac_unit,
- onTap: () {
- debugPrint('snowflake');
- }
- ),
-```
-
-Each `QuillCustomButton` is used as part of the `customButtons` option as follows:
-
-```dart
-QuillToolbar(
- configurations: QuillToolbarConfigurations(
- customButtons: [
- QuillCustomButton(
- iconData: Icons.ac_unit,
- onTap: () {
- debugPrint('snowflake1');
- },
- ),
- QuillCustomButton(
- iconData: Icons.ac_unit,
- onTap: () {
- debugPrint('snowflake2');
- },
- ),
- QuillCustomButton(
- iconData: Icons.ac_unit,
- onTap: () {
- debugPrint('snowflake3');
- },
- ),
- ],
- ),
-),
-```
+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
@@ -279,454 +220,45 @@ Provide a list of embed
### Using the embed blocks from `flutter_quill_extensions`
-```dart
-QuillToolbar(
- configurations: QuillToolbarConfigurations(
- embedButtons: FlutterQuillEmbeds.toolbarButtons(
- imageButtonOptions: QuillToolbarImageButtonOptions(
- onImagePickCallback: (file) async {
- return file.path;
- },
- ),
- ),
- ),
-),
-```
+To see how to use the extension package, please take a look at the [README](./flutter_quill_extensions/README.md) of [FlutterQuill Extensions]
-```dart
-Expanded(
- child: QuillEditor.basic(
- configurations: QuillEditorConfigurations(
- readOnly: true,
- embedBuilders: FlutterQuillEmbeds.editorBuilders(
- imageEmbedConfigurations:
- const QuillEditorImageEmbedConfigurations(
- forceUseMobileOptionMenuForImageClick: true,
- ),
- ),
- ),
- ),
-)
-```
+### Links
-
-
-
-### Custom Size Image for Mobile
-
-Define `mobileWidth`, `mobileHeight`, `mobileMargin`, `mobileAlignment` as follows:
-
-```dart
-{
- "insert": {
- "image": "https://user-images.githubusercontent.com/122956/72955931-ccc07900-3d52-11ea-89b1-d468a6e2aa2b.png"
- },
- "attributes":{
- "style":"mobileWidth: 50; mobileHeight: 50; mobileMargin: 10; mobileAlignment: topLeft"
- }
-}
-```
-
-### Custom Size Image for other platforms (excluding web)
-
-Define `width`, `height`, `margin`, `alignment` as follows:
-
-```dart
-{
- "insert": {
- "image": "https://user-images.githubusercontent.com/122956/72955931-ccc07900-3d52-11ea-89b1-d468a6e2aa2b.png"
- },
- "attributes":{
- "style":"width: 50; height: 50; margin: 10; alignment: topLeft"
- }
-}
-```
-
-### Custom Embed Blocks
-
-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.
-
-The only thing that you need is to add a `CustomBlockEmbed` and provider a builder for it to the `embedBuilders` parameter, to transform the data inside of 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, that will be the `Document`, used by the `flutter_quill` to render the rich text.
-
-```dart
-class NotesBlockEmbed extends CustomBlockEmbed {
- const NotesBlockEmbed(String value) : super(noteType, value);
-
- static const String noteType = 'notes';
-
- static NotesBlockEmbed fromDocument(Document document) =>
- NotesBlockEmbed(jsonEncode(document.toDelta().toJson()));
-
- Document get document => Document.fromJson(jsonDecode(data));
-}
-```
-
-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
-class NotesEmbedBuilder extends EmbedBuilder {
- NotesEmbedBuilder({required this.addEditNote});
-
- Future Function(BuildContext context, {Document? document}) addEditNote;
-
- @override
- String get key => 'notes';
-
- @override
- Widget build(
- BuildContext context,
- QuillController controller,
- Embed node,
- bool readOnly,
- bool inline,
- TextStyle textStyle,
- ) {
- final notes = NotesBlockEmbed(node.value.data).document;
-
- return Material(
- color: Colors.transparent,
- child: ListTile(
- title: Text(
- notes.toPlainText().replaceAll('\n', ' '),
- maxLines: 3,
- overflow: TextOverflow.ellipsis,
- ),
- leading: const Icon(Icons.notes),
- onTap: () => addEditNote(context, document: notes),
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(10),
- side: const BorderSide(color: Colors.grey),
- ),
- ),
- );
- }
-}
-```
-
-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 _addEditNote(BuildContext context, {Document? document}) async {
- final isEditing = document != null;
- final quillEditorController = QuillController(
- document: document ?? Document(),
- selection: const TextSelection.collapsed(offset: 0),
- );
-
- await showDialog(
- context: context,
- builder: (context) => AlertDialog(
- titlePadding: const EdgeInsets.only(left: 16, top: 8),
- title: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text('${isEditing ? 'Edit' : 'Add'} note'),
- IconButton(
- onPressed: () => Navigator.of(context).pop(),
- icon: const Icon(Icons.close),
- )
- ],
- ),
- content: QuillEditor.basic(
- controller: quillEditorController,
- readOnly: false,
- ),
- ),
- );
-
- if (quillEditorController.document.isEmpty()) return;
-
- final block = BlockEmbed.custom(
- NotesBlockEmbed.fromDocument(quillEditorController.document),
- );
- final controller = _controller!;
- final index = controller.selection.baseOffset;
- final length = controller.selection.extentOffset - index;
-
- if (isEditing) {
- final offset = getEmbedNode(controller, controller.selection.start).offset;
- controller.replaceText(
- offset, 1, block, TextSelection.collapsed(offset: offset));
- } else {
- controller.replaceText(index, length, block, null);
- }
-}
-```
-
-And voila, we have a custom widget inside of the rich text editor!
-
-
-
-
-
-> 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)
-
-
-### Custom Toolbar
-If you want to use custom toolbar but still want the support of this libray
-You can use the `QuillBaseToolbar` which is the base for the `QuillToolbar`
-
-> If you are using the toolbar buttons like `QuillToolbarHistoryButton`, `QuillToolbarToggleStyleButton` in the somewhere like the the custom toolbar then you must provide them with `QuillToolbarProvider` inherited widget, you don't have to do this if you are using the `QuillToolbar` since it will be done for you
-
-Example:
-
-```dart
-QuillProvider(
- configurations: QuillConfigurations(
- controller: _controller,
- sharedConfigurations: const QuillSharedConfigurations(),
- ),
- child: Column(
- children: [
- QuillToolbarProvider(
- toolbarConfigurations: const QuillToolbarConfigurations(),
- child: QuillBaseToolbar(
- configurations: QuillBaseToolbarConfigurations(
- toolbarSize: 15 * 2,
- multiRowsDisplay: false,
- childrenBuilder: (context) {
- final controller = context.requireQuillController;
- return [
- QuillToolbarHistoryButton(
- controller: controller,
- options: const QuillToolbarHistoryButtonOptions(
- isUndo: true),
- ),
- QuillToolbarHistoryButton(
- controller: controller,
- options: const QuillToolbarHistoryButtonOptions(
- isUndo: false),
- ),
- QuillToolbarToggleStyleButton(
- attribute: Attribute.bold,
- controller: controller,
- options: const QuillToolbarToggleStyleButtonOptions(
- iconData: Icons.format_bold,
- iconSize: 20,
- ),
- ),
- QuillToolbarToggleStyleButton(
- attribute: Attribute.italic,
- controller: controller,
- options: const QuillToolbarToggleStyleButtonOptions(
- iconData: Icons.format_italic,
- iconSize: 20,
- ),
- ),
- QuillToolbarToggleStyleButton(
- attribute: Attribute.underline,
- controller: controller,
- options: const QuillToolbarToggleStyleButtonOptions(
- iconData: Icons.format_underline,
- iconSize: 20,
- ),
- ),
- QuillToolbarClearFormatButton(
- controller: controller,
- options: const QuillToolbarClearFormatButtonOptions(
- iconData: Icons.format_clear,
- iconSize: 20,
- ),
- ),
- VerticalDivider(
- indent: 12,
- endIndent: 12,
- color: Colors.grey.shade400,
- ),
- QuillToolbarSelectHeaderStyleButtons(
- controller: controller,
- options:
- const QuillToolbarSelectHeaderStyleButtonsOptions(
- iconSize: 20,
- ),
- ),
- QuillToolbarToggleStyleButton(
- attribute: Attribute.ol,
- controller: controller,
- options: const QuillToolbarToggleStyleButtonOptions(
- iconData: Icons.format_list_numbered,
- iconSize: 20,
- ),
- ),
- QuillToolbarToggleStyleButton(
- attribute: Attribute.ul,
- controller: controller,
- options: const QuillToolbarToggleStyleButtonOptions(
- iconData: Icons.format_list_bulleted,
- iconSize: 20,
- ),
- ),
- QuillToolbarToggleStyleButton(
- attribute: Attribute.blockQuote,
- controller: controller,
- options: const QuillToolbarToggleStyleButtonOptions(
- iconData: Icons.format_quote,
- iconSize: 20,
- ),
- ),
- VerticalDivider(
- indent: 12,
- endIndent: 12,
- color: Colors.grey.shade400,
- ),
- QuillToolbarIndentButton(
- controller: controller,
- isIncrease: true,
- options: const QuillToolbarIndentButtonOptions(
- iconData: Icons.format_indent_increase,
- iconSize: 20,
- )),
- QuillToolbarIndentButton(
- controller: controller,
- isIncrease: false,
- options: const QuillToolbarIndentButtonOptions(
- iconData: Icons.format_indent_decrease,
- iconSize: 20,
- ),
- ),
- ];
- },
- ),
- ),
- ),
- Expanded(
- child: QuillEditor.basic(
- configurations: const QuillEditorConfigurations(
- readOnly: false,
- placeholder: 'Write your notes',
- padding: EdgeInsets.all(16),
- ),
- ),
- )
- ],
- ),
-)
-```
-
-if you want 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 works
-
-### Translation
-
-The package offers translations for the quill toolbar and editor, it will follow the system locale unless you set your own locale with:
-
-```dart
- QuillProvider(
- configurations: QuillConfigurations(
- controller: _controller,
- sharedConfigurations: const QuillSharedConfigurations(
- locale: Locale('fr'),
- ),
- ),
- child: Column(
- children: [
- const QuillToolbar(
- configurations: QuillToolbarConfigurations(),
- ),
- Expanded(
- child: QuillEditor.basic(
- configurations: const QuillEditorConfigurations(),
- ),
- )
- ],
- ),
-)
-```
-
-###
-
-Currently, translations are available for these 31 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('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('fa')`
-* `Locale('hi')`
-* `Locale('sr')`
-* `Locale('sw')`
-* `Locale('ja')`
-
-#### Contributing to translations
-
-The translation file is located at [toolbar.i18n.dart](lib/src/translations/toolbar.i18n.dart). Feel free to contribute your own translations, just copy the English translations map and replace the values with your translations. Then open a pull request so everyone can benefit from your translations!
+- [Custom Embed Blocks](./doc/custom_embed_blocks.md)
+- [Custom Toolbar](./doc/custom_toolbar.md)
## 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 in order to publish it, or send an email. One option is to use
+it to other formats such as HTML to publish it, or send an email.
+
+You have two options:
+
+1. Using [quill_html_converter](./packages/quill_html_converter/) to convert to/from 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)) but the converting from HTML back to Quill delta is experimental
+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. Conversion can be performed in vanilla Dart (i.e., server-side or CLI) or in Flutter.
+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
-## Testing
-
-To aid in testing applications using the editor an extension to the flutter `WidgetTester` is provided which includes methods to simplify interacting with the editor in test cases.
-
-Import the test utilities in your test file:
-
-```dart
-import 'package:flutter_quill/flutter_quill_test.dart';
-```
+## Translation
-and then enter text using `quillEnterText`:
+The package offers translations for the quill toolbar and editor, it will follow the system locale unless you set your own locale.
-```dart
-await tester.quillEnterText(find.byType(QuillEditor), 'test\n');
-```
+Open this [page](./doc/translation.md) for more info
-## License
+## Testing
-[MIT](LICENSE)
+Please use [flutter_quill_test](https://pub.dev/packages/flutter_quill_test) for testing
## Contributors
-Special thanks for everyone that have contributed to this project...
+Special thanks for everyone that has contributed to this project...
@@ -736,15 +268,22 @@ Special thanks for everyone that have contributed to this project...
Made with [contrib.rocks](https://contrib.rocks).
+We welcome contributions!
+
+Please follow these guidelines when contributing to the project. See [CONTRIBUTING.md](./CONTRIBUTING.md) for more details.
+
+We must mention that the [CONTRIBUTING.md](./CONTRIBUTING.md) have a development notes, so if you're planning on contributing to the repo,
+please consider reading it.
+
+You can check the [Todo](./doc/todo.md) list if you want to
+
[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/pages/home_page.dart
-[Code Introduction]: https://github.com/singerdmx/flutter-quill/blob/master/CodeIntroduction.md
-
-
+[Sample Page]: ./example/lib/pages/home_page.dart
+[FluentUI]: https://pub.dev/packages/fluent_ui
-[中文文档](./doc_cn.md)
diff --git a/analysis_options.yaml b/analysis_options.yaml
index 7749c861..f1a38172 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -1,4 +1,4 @@
-include: package:pedantic/analysis_options.yaml
+include: package:flutter_lints/flutter.yaml
analyzer:
errors:
@@ -6,32 +6,31 @@ analyzer:
unsafe_html: ignore
linter:
rules:
- - always_declare_return_types
- - always_put_required_named_parameters_first
- - annotate_overrides
- - avoid_empty_else
- - avoid_escaping_inner_quotes
- - avoid_print
- - avoid_redundant_argument_values
- - avoid_types_on_closure_parameters
- - avoid_void_async
- - cascade_invocations
- - directives_ordering
- - lines_longer_than_80_chars
- - omit_local_variable_types
- - prefer_const_constructors
- - prefer_const_constructors_in_immutables
- - prefer_const_declarations
- - prefer_final_fields
- - prefer_final_in_for_each
- - prefer_final_locals
- - prefer_initializing_formals
- - prefer_int_literals
- - prefer_interpolation_to_compose_strings
- - prefer_relative_imports
- - prefer_single_quotes
- - sort_constructors_first
- - sort_unnamed_constructors_first
- - unnecessary_lambdas
- - unnecessary_parenthesis
- - unnecessary_string_interpolations
+ always_declare_return_types: true
+ always_put_required_named_parameters_first: true
+ annotate_overrides: true
+ avoid_empty_else: true
+ avoid_escaping_inner_quotes: true
+ avoid_print: true
+ avoid_redundant_argument_values: true
+ avoid_types_on_closure_parameters: true
+ avoid_void_async: true
+ cascade_invocations: true
+ directives_ordering: true
+ omit_local_variable_types: true
+ prefer_const_constructors: true
+ prefer_const_constructors_in_immutables: true
+ prefer_const_declarations: true
+ prefer_final_fields: true
+ prefer_final_in_for_each: true
+ prefer_final_locals: true
+ prefer_initializing_formals: true
+ prefer_int_literals: true
+ prefer_interpolation_to_compose_strings: true
+ prefer_relative_imports: true
+ prefer_single_quotes: true
+ sort_constructors_first: true
+ sort_unnamed_constructors_first: true
+ unnecessary_lambdas: true
+ unnecessary_parenthesis: true
+ unnecessary_string_interpolations: true
diff --git a/CodeIntroduction.md b/doc/code_introduction.md
similarity index 100%
rename from CodeIntroduction.md
rename to doc/code_introduction.md
diff --git a/doc/configurations/custom_buttons.md b/doc/configurations/custom_buttons.md
new file mode 100644
index 00000000..24f9e1ca
--- /dev/null
+++ b/doc/configurations/custom_buttons.md
@@ -0,0 +1,43 @@
+# Custom `QuillToolbar` Buttons
+
+You may add custom buttons to the _end_ of the toolbar, via the `customButtons` option, which is a `List` of `QuillToolbarCustomButtonOptions`.
+
+To add an Icon, we should use a new `QuillToolbarCustomButtonOptions` class
+
+```dart
+ QuillToolbarCustomButtonOptions(
+ icon: const Icon(Icons.ac_unit),
+ tooltip: '',
+ onPressed: () {},
+ afterButtonPressed: () {},
+ ),
+```
+
+Each `QuillCustomButton` is used as part of the `customButtons` option as follows:
+
+```dart
+QuillToolbar(
+ configurations: QuillToolbarConfigurations(
+ customButtons: [
+ QuillToolbarCustomButtonOptions(
+ icon: const Icon(Icons.ac_unit),
+ onPressed: () {
+ debugPrint('snowflake1');
+ },
+ ),
+ QuillToolbarCustomButtonOptions(
+ icon: const Icon(Icons.ac_unit),
+ onPressed: () {
+ debugPrint('snowflake2');
+ },
+ ),
+ QuillToolbarCustomButtonOptions(
+ icon: const Icon(Icons.ac_unit),
+ onPressed: () {
+ debugPrint('snowflake3');
+ },
+ ),
+ ],
+ ),
+),
+```
\ No newline at end of file
diff --git a/doc/configurations/font_size.md b/doc/configurations/font_size.md
new file mode 100644
index 00000000..c73ddcb8
--- /dev/null
+++ b/doc/configurations/font_size.md
@@ -0,0 +1,15 @@
+# Font Size
+
+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` 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'}
+```
+
+Font size can be cleared with a value of `0`, for example:
+
+```dart
+fontSizeValues: const {'Small': '8', 'Medium': '24.5', 'Large': '46', 'Clear': '0'}
+```
\ No newline at end of file
diff --git a/doc/configurations/localizations_setup.md b/doc/configurations/localizations_setup.md
new file mode 100644
index 00000000..1d373a27
--- /dev/null
+++ b/doc/configurations/localizations_setup.md
@@ -0,0 +1,25 @@
+# Localizations Setup
+in addition to the required delegatess which mentioned above in [Using custom app widget](./using_custom_app_widget.md)
+
+which are:
+```dart
+localizationsDelegates: const [
+ DefaultCupertinoLocalizations.delegate,
+ DefaultMaterialLocalizations.delegate,
+ DefaultWidgetsLocalizations.delegate,
+],
+```
+which are used by offical flutter widgets
+
+The library also needs the
+```dart
+// Required localizations delegates ...
+FlutterQuillLocalizations.delegate
+```
+
+To offer the default localizations.
+
+But **you don't have to** since we have wraped 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
+
+There are additional notes in the [Translation](../translation.md) section
\ No newline at end of file
diff --git a/doc/configurations/using_custom_app_widget.md b/doc/configurations/using_custom_app_widget.md
new file mode 100644
index 00000000..8a69f9d0
--- /dev/null
+++ b/doc/configurations/using_custom_app_widget.md
@@ -0,0 +1,34 @@
+# Using Custom App Widget
+
+This project use some adaptive widgets like `AdaptiveTextSelectionToolbar` which require the following delegates:
+
+1. Default Material Localizations delegate
+2. Default Cupertino Localizations delegate
+3. Defualt Widgets Localizations delegate
+
+You don't need to include those since there are defined by default
+ but if you are using Custom app or you are overriding the `localizationsDelegates` in the App widget
+then please make sure it's including those:
+
+```dart
+localizationsDelegates: const [
+ DefaultCupertinoLocalizations.delegate,
+ DefaultMaterialLocalizations.delegate,
+ DefaultWidgetsLocalizations.delegate,
+],
+```
+
+And you might need more depending on your use case, for example if you are using custom localizations for your app, using custom app widget like `FluentApp` from [FluentUI]
+which will also need
+
+```dart
+localizationsDelegates: const [
+ // Required localizations delegates ...
+ FluentLocalizations.delegate,
+ AppLocalizations.delegate,
+],
+```
+
+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)
+
+There are additonal notes in [Localizations](./localizations_setup.md) page
\ No newline at end of file
diff --git a/doc/custom_embed_blocks.md b/doc/custom_embed_blocks.md
new file mode 100644
index 00000000..82a1e44c
--- /dev/null
+++ b/doc/custom_embed_blocks.md
@@ -0,0 +1,124 @@
+# 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.
+
+The only thing that you need is to add a `CustomBlockEmbed` and provider a builder for it to the `embedBuilders` parameter, to transform the data inside of 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, that will be the `Document`, used by the `flutter_quill` to render the rich text.
+
+```dart
+class NotesBlockEmbed extends CustomBlockEmbed {
+ const NotesBlockEmbed(String value) : super(noteType, value);
+
+ static const String noteType = 'notes';
+
+ static NotesBlockEmbed fromDocument(Document document) =>
+ NotesBlockEmbed(jsonEncode(document.toDelta().toJson()));
+
+ Document get document => Document.fromJson(jsonDecode(data));
+}
+```
+
+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
+class NotesEmbedBuilder extends EmbedBuilder {
+ NotesEmbedBuilder({required this.addEditNote});
+
+ Future Function(BuildContext context, {Document? document}) addEditNote;
+
+ @override
+ String get key => 'notes';
+
+ @override
+ Widget build(
+ BuildContext context,
+ QuillController controller,
+ Embed node,
+ bool readOnly,
+ bool inline,
+ TextStyle textStyle,
+ ) {
+ final notes = NotesBlockEmbed(node.value.data).document;
+
+ return Material(
+ color: Colors.transparent,
+ child: ListTile(
+ title: Text(
+ notes.toPlainText().replaceAll('\n', ' '),
+ maxLines: 3,
+ overflow: TextOverflow.ellipsis,
+ ),
+ leading: const Icon(Icons.notes),
+ onTap: () => addEditNote(context, document: notes),
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(10),
+ side: const BorderSide(color: Colors.grey),
+ ),
+ ),
+ );
+ }
+}
+```
+
+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 _addEditNote(BuildContext context, {Document? document}) async {
+ final isEditing = document != null;
+ final quillEditorController = QuillController(
+ document: document ?? Document(),
+ selection: const TextSelection.collapsed(offset: 0),
+ );
+
+ await showDialog(
+ context: context,
+ builder: (context) => AlertDialog(
+ titlePadding: const EdgeInsets.only(left: 16, top: 8),
+ title: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text('${isEditing ? 'Edit' : 'Add'} note'),
+ IconButton(
+ onPressed: () => Navigator.of(context).pop(),
+ icon: const Icon(Icons.close),
+ )
+ ],
+ ),
+ content: QuillEditor.basic(
+ controller: quillEditorController,
+ readOnly: false,
+ ),
+ ),
+ );
+
+ if (quillEditorController.document.isEmpty()) return;
+
+ final block = BlockEmbed.custom(
+ NotesBlockEmbed.fromDocument(quillEditorController.document),
+ );
+ final controller = _controller!;
+ final index = controller.selection.baseOffset;
+ final length = controller.selection.extentOffset - index;
+
+ if (isEditing) {
+ final offset = getEmbedNode(controller, controller.selection.start).offset;
+ controller.replaceText(
+ offset, 1, block, TextSelection.collapsed(offset: offset));
+ } else {
+ controller.replaceText(index, length, block, null);
+ }
+}
+```
+
+And voila, we have a custom widget inside of the rich text editor!
+
+
+
+
+
+> 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)
\ No newline at end of file
diff --git a/doc/custom_toolbar.md b/doc/custom_toolbar.md
new file mode 100644
index 00000000..06c8bb72
--- /dev/null
+++ b/doc/custom_toolbar.md
@@ -0,0 +1,143 @@
+# Custom Toolbar
+
+If you want to use custom toolbar but still want the support of this libray
+You can use the `QuillBaseToolbar` which is the base for the `QuillToolbar`
+
+> If you are using the toolbar buttons like `QuillToolbarHistoryButton`, `QuillToolbarToggleStyleButton` in the somewhere like the the custom toolbar then you must provide them with `QuillToolbarProvider` inherited widget, you don't have to do this if you are using the `QuillToolbar` since it will be done for you
+
+Example:
+
+```dart
+QuillProvider(
+ configurations: QuillConfigurations(
+ controller: _controller,
+ sharedConfigurations: const QuillSharedConfigurations(),
+ ),
+ child: Column(
+ children: [
+ QuillToolbarProvider(
+ toolbarConfigurations: const QuillToolbarConfigurations(),
+ child: QuillBaseToolbar(
+ configurations: QuillBaseToolbarConfigurations(
+ toolbarSize: 15 * 2,
+ multiRowsDisplay: false,
+ childrenBuilder: (context) {
+ final controller = context.requireQuillController;
+ return [
+ QuillToolbarHistoryButton(
+ controller: controller,
+ options: const QuillToolbarHistoryButtonOptions(
+ isUndo: true),
+ ),
+ QuillToolbarHistoryButton(
+ controller: controller,
+ options: const QuillToolbarHistoryButtonOptions(
+ isUndo: false),
+ ),
+ QuillToolbarToggleStyleButton(
+ attribute: Attribute.bold,
+ controller: controller,
+ options: const QuillToolbarToggleStyleButtonOptions(
+ iconData: Icons.format_bold,
+ iconSize: 20,
+ ),
+ ),
+ QuillToolbarToggleStyleButton(
+ attribute: Attribute.italic,
+ controller: controller,
+ options: const QuillToolbarToggleStyleButtonOptions(
+ iconData: Icons.format_italic,
+ iconSize: 20,
+ ),
+ ),
+ QuillToolbarToggleStyleButton(
+ attribute: Attribute.underline,
+ controller: controller,
+ options: const QuillToolbarToggleStyleButtonOptions(
+ iconData: Icons.format_underline,
+ iconSize: 20,
+ ),
+ ),
+ QuillToolbarClearFormatButton(
+ controller: controller,
+ options: const QuillToolbarClearFormatButtonOptions(
+ iconData: Icons.format_clear,
+ iconSize: 20,
+ ),
+ ),
+ VerticalDivider(
+ indent: 12,
+ endIndent: 12,
+ color: Colors.grey.shade400,
+ ),
+ QuillToolbarSelectHeaderStyleButtons(
+ controller: controller,
+ options:
+ const QuillToolbarSelectHeaderStyleButtonsOptions(
+ iconSize: 20,
+ ),
+ ),
+ QuillToolbarToggleStyleButton(
+ attribute: Attribute.ol,
+ controller: controller,
+ options: const QuillToolbarToggleStyleButtonOptions(
+ iconData: Icons.format_list_numbered,
+ iconSize: 20,
+ ),
+ ),
+ QuillToolbarToggleStyleButton(
+ attribute: Attribute.ul,
+ controller: controller,
+ options: const QuillToolbarToggleStyleButtonOptions(
+ iconData: Icons.format_list_bulleted,
+ iconSize: 20,
+ ),
+ ),
+ QuillToolbarToggleStyleButton(
+ attribute: Attribute.blockQuote,
+ controller: controller,
+ options: const QuillToolbarToggleStyleButtonOptions(
+ iconData: Icons.format_quote,
+ iconSize: 20,
+ ),
+ ),
+ VerticalDivider(
+ indent: 12,
+ endIndent: 12,
+ color: Colors.grey.shade400,
+ ),
+ QuillToolbarIndentButton(
+ controller: controller,
+ isIncrease: true,
+ options: const QuillToolbarIndentButtonOptions(
+ iconData: Icons.format_indent_increase,
+ iconSize: 20,
+ )),
+ QuillToolbarIndentButton(
+ controller: controller,
+ isIncrease: false,
+ options: const QuillToolbarIndentButtonOptions(
+ iconData: Icons.format_indent_decrease,
+ iconSize: 20,
+ ),
+ ),
+ ];
+ },
+ ),
+ ),
+ ),
+ Expanded(
+ child: QuillEditor.basic(
+ configurations: const QuillEditorConfigurations(
+ readOnly: false,
+ placeholder: 'Write your notes',
+ padding: EdgeInsets.all(16),
+ ),
+ ),
+ )
+ ],
+ ),
+)
+```
+
+if you want 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 works
\ No newline at end of file
diff --git a/doc/development_notes.md b/doc/development_notes.md
new file mode 100644
index 00000000..603e40b2
--- /dev/null
+++ b/doc/development_notes.md
@@ -0,0 +1,3 @@
+# Development notes
+
+- When update the translations or localizations in the app, please take a look at the [Translation](./translation.md) page as it have important notes in order to work, if you also adding a feature that add new localizations then you need to the instructions of it in order for the translations to take affect
\ No newline at end of file
diff --git a/doc/migration.md b/doc/migration.md
index 4de759f7..7a65b3ef 100644
--- a/doc/migration.md
+++ b/doc/migration.md
@@ -79,10 +79,14 @@ All the options have parent `QuillToolbarBaseButtonOptions` which have common th
final IconData? iconData;
/// To change the icon size pass a different value, by default will be
- /// [kDefaultIconSize]
+ /// [kDefaultIconSize].
/// This will be used for all the buttons but you can override this
final double globalIconSize;
+ /// The factor of how much larger the button is in relation to the icon,
+ /// by default it will be [kIconButtonFactor].
+ final double globalIconButtonFactor;
+
/// To do extra logic after pressing the button
final VoidCallback? afterButtonPressed;
diff --git a/doc_cn.md b/doc/readme/cn.md
similarity index 99%
rename from doc_cn.md
rename to doc/readme/cn.md
index 680b4d72..098110dd 100644
--- a/doc_cn.md
+++ b/doc/readme/cn.md
@@ -24,7 +24,7 @@
---
-> This documentation is outdated. Please check the English version.
+> This documentation is outdated. Please check the [English version](../../README.md).
`FlutterQuill` 是一个富文本编辑器,也是 [Quill](https://quilljs.com/docs/formats) 在 [Flutter](https://github.com/flutter/flutter) 的版本
diff --git a/doc/todo.md b/doc/todo.md
new file mode 100644
index 00000000..0dd2f43f
--- /dev/null
+++ b/doc/todo.md
@@ -0,0 +1,48 @@
+# 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`
+
+### Bugs
+
+Empty for now.
+Please go to the [issues](https://github.com/singerdmx/flutter-quill/issues)
+
+
+## Flutter Quill Extensions
+
+### Features
+- Add support for copying images to the Clipboard
+
+### Improvemenets
+
+Please check the todos, this list will be updated soon.
+
+### Bugs
+
+Please check the todos, this list will be updated soon.
\ No newline at end of file
diff --git a/doc/translation.md b/doc/translation.md
new file mode 100644
index 00000000..a33d3213
--- /dev/null
+++ b/doc/translation.md
@@ -0,0 +1,65 @@
+# 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 follow the system local and it unless you set your own locale with:
+
+```dart
+ QuillProvider(
+ configurations: QuillConfigurations(
+ controller: _controller,
+ sharedConfigurations: const QuillSharedConfigurations(
+ locale: Locale('fr'), // will take affect only if FlutterQuillLocalizations.delegate is not defined in the Widget app
+ ),
+ ),
+ child: Column(
+ children: [
+ const QuillToolbar(
+ configurations: QuillToolbarConfigurations(),
+ ),
+ Expanded(
+ child: QuillEditor.basic(
+ configurations: const QuillEditorConfigurations(),
+ ),
+ )
+ ],
+ ),
+)
+```
+
+Currently, translations are available for these 31 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('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('fa')`, `Locale('hi')`
+* `Locale('sr')`, `Locale('sw')`, `Locale('ja')`
+
+#### Contributing to translations
+
+The translation files is located at [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.
+
+Add new file in the l10n folder with the following name
+`quill_${localName}.arb` for example `quill_de.arb`
+
+paste the English version and replace the values
+
+Also you can take a look at the [untranslated.json](../lib/src/l10n/untranslated.json) json file, which is a generated file that tell you which keys with which locales hasn't translated so you can find the missings easily
+
+After you are done and want to test the changes, run the following in the root folder (preferred):
+
+```
+flutter gen-l10n
+```
+
+or:
+
+```
+./scripts/regenerate-translations.sh
+```
+
+
+This will generate the new dart files from the arb files in order to take affect, otherwise you won't notice a difference
+
+ Then open a pull request so everyone can benefit from your translations!
\ No newline at end of file
diff --git a/example/.gitignore b/example/.gitignore
index 15e3a7c7..24476c5d 100644
--- a/example/.gitignore
+++ b/example/.gitignore
@@ -8,6 +8,7 @@
.buildlog/
.history
.svn/
+migrate_working_dir/
# IntelliJ related
*.iml
@@ -31,12 +32,13 @@
.pub/
/build/
-# Web related
-lib/generated_plugin_registrant.dart
-
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
-pubspec.lock
\ No newline at end of file
+
+# Android Studio will place build artifacts here
+/android/app/debug
+/android/app/profile
+/android/app/release
diff --git a/example/.metadata b/example/.metadata
index 24544cb7..a778330b 100644
--- a/example/.metadata
+++ b/example/.metadata
@@ -4,7 +4,42 @@
# This file should be version controlled and should not be manually edited.
version:
- revision: 84f3d28555368a70270e9ac8390a9441df95e752
- channel: stable
+ revision: "d211f42860350d914a5ad8102f9ec32764dc6d06"
+ channel: "stable"
project_type: app
+
+# Tracks metadata for the flutter migrate command
+migration:
+ platforms:
+ - platform: root
+ create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
+ base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
+ - platform: android
+ create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
+ base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
+ - platform: ios
+ create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
+ base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
+ - platform: linux
+ create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
+ base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
+ - platform: macos
+ create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
+ base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
+ - platform: web
+ create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
+ base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
+ - platform: windows
+ create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
+ base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06
+
+ # User provided section
+
+ # List of Local paths (relative to this file) that should be
+ # ignored by the migrate tool.
+ #
+ # Files that are not part of the templates will be ignored by default.
+ unmanaged_files:
+ - 'lib/main.dart'
+ - 'ios/Runner.xcodeproj/project.pbxproj'
diff --git a/example/README.md b/example/README.md
index 5c0ad6d4..c727691c 100644
--- a/example/README.md
+++ b/example/README.md
@@ -1,16 +1,18 @@
-# app
+# Demo
-demo app
+This is just a demo of Flutter Quill
-## Getting Started
-This project is a starting point for a Flutter application.
+## Screenshots
-A few resources to get you started if this is your first Flutter project:
+
+
+
+
-- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
-- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
+## Development notes
-For help getting started with Flutter, view our
-[online documentation](https://flutter.dev/docs), which offers tutorials,
-samples, guidance on mobile development, and a full API reference.
+- When changing the `assets` please run:
+```
+dart run build_runner build --delete-conflicting-outputs
+```
\ No newline at end of file
diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml
new file mode 100644
index 00000000..735e0400
--- /dev/null
+++ b/example/analysis_options.yaml
@@ -0,0 +1,37 @@
+include: package:flutter_lints/flutter.yaml
+
+analyzer:
+ errors:
+ invalid_annotation_target: ignore
+
+linter:
+ rules:
+ always_declare_return_types: true
+ always_put_required_named_parameters_first: true
+ annotate_overrides: true
+ avoid_empty_else: true
+ avoid_escaping_inner_quotes: true
+ avoid_print: false
+ avoid_redundant_argument_values: false
+ avoid_types_on_closure_parameters: true
+ avoid_void_async: true
+ cascade_invocations: true
+ directives_ordering: true
+ omit_local_variable_types: true
+ prefer_const_constructors: true
+ prefer_const_constructors_in_immutables: true
+ prefer_const_declarations: true
+ prefer_final_fields: true
+ prefer_final_in_for_each: true
+ prefer_final_locals: true
+ prefer_initializing_formals: true
+ prefer_int_literals: true
+ prefer_interpolation_to_compose_strings: true
+ prefer_relative_imports: true
+ prefer_single_quotes: true
+ sort_constructors_first: true
+ sort_unnamed_constructors_first: true
+ unnecessary_lambdas: true
+ unnecessary_parenthesis: true
+ unnecessary_string_interpolations: true
+ library_private_types_in_public_api: false
\ No newline at end of file
diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle
index 4eef2899..d2bb9117 100644
--- a/example/android/app/build.gradle
+++ b/example/android/app/build.gradle
@@ -1,3 +1,9 @@
+plugins {
+ id "com.android.application"
+ id "kotlin-android"
+ id "dev.flutter.flutter-gradle-plugin"
+}
+
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
@@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) {
}
}
-def flutterRoot = localProperties.getProperty('flutter.sdk')
-if (flutterRoot == null) {
- throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
-}
-
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
@@ -21,16 +22,14 @@ if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
-apply plugin: 'com.android.application'
-apply plugin: 'kotlin-android'
-apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
-
android {
- compileSdkVersion flutter.compileSdkVersion
+ namespace "com.example.example"
+ compileSdk flutter.compileSdkVersion
+ ndkVersion flutter.ndkVersion
compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
+ sourceCompatibility JavaVersion.VERSION_17
+ targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
@@ -42,17 +41,15 @@ android {
}
defaultConfig {
- applicationId "com.example.app"
- minSdkVersion 21
+ applicationId "com.example.example"
+ minSdkVersion 23
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
- // Multidex is not required for api level 21
}
buildTypes {
release {
- // Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
@@ -62,6 +59,4 @@ flutter {
source '../..'
}
-dependencies {
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
-}
+dependencies {}
diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml
index 9bc03bb2..399f6981 100644
--- a/example/android/app/src/debug/AndroidManifest.xml
+++ b/example/android/app/src/debug/AndroidManifest.xml
@@ -1,8 +1,7 @@
-
-
-
diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml
index aeb699d6..e047f006 100644
--- a/example/android/app/src/main/AndroidManifest.xml
+++ b/example/android/app/src/main/AndroidManifest.xml
@@ -1,38 +1,54 @@
-
-
+
+
+
+
-
+
-
-
-
-
+
+
+
+
+
+
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name">
+ android:name="io.flutter.embedding.android.NormalTheme"
+ android:resource="@style/NormalTheme" />
-
-
+
+
+
+
-
diff --git a/example/android/build.gradle b/example/android/build.gradle
index 85ed7b51..d73bed24 100644
--- a/example/android/build.gradle
+++ b/example/android/build.gradle
@@ -1,12 +1,14 @@
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
buildscript {
- ext.kotlin_version = '1.9.0'
+ ext.kotlin_version = '1.9.20'
repositories {
google()
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:7.4.0'
+ classpath 'com.android.tools.build:gradle:8.1.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
@@ -21,6 +23,47 @@ allprojects {
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
+
+ // For mode details visit https://gist.github.com/freshtechtips/93fefb39e48c40592bda3931e05fd35c
+ afterEvaluate {
+ // check if android block is available
+
+ if (it.hasProperty('android')) {
+
+ if (it.android.namespace == null) {
+ def manifest = new XmlSlurper().parse(file(it.android.sourceSets.main.manifest.srcFile))
+ def packageName = manifest.@package.text()
+ println("Setting ${packageName} as android namespace in build.gradle from the AndroidManifest.xml")
+ android.namespace = packageName
+ }
+
+ def javaVersion = JavaVersion.VERSION_17
+ println("Changes will be applied for the following packages:")
+ android {
+ def androidApiVersion = 34
+// compileSdkVersion androidApiVersion
+ compileSdk androidApiVersion
+ defaultConfig {
+ targetSdkVersion androidApiVersion
+ }
+ compileOptions {
+ sourceCompatibility javaVersion
+ targetCompatibility javaVersion
+ }
+ tasks.withType(KotlinCompile).configureEach {
+ buildscript {
+ ext.kotlin_version = kotlin_version
+ }
+ kotlinOptions {
+ jvmTarget = javaVersion.toString()
+ }
+ }
+ String message = "For package ${android.namespace} by update compileSdkVersion, targetSdkVersion \n to $androidApiVersion and java version to ${javaVersion.toString()}"
+ println(message)
+ }
+ }
+
+ }
}
subprojects {
project.evaluationDependsOn(':app')
diff --git a/example/android/gradle.properties b/example/android/gradle.properties
index 94adc3a3..b9a9a246 100644
--- a/example/android/gradle.properties
+++ b/example/android/gradle.properties
@@ -1,3 +1,6 @@
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
+android.defaults.buildfeatures.buildconfig=true
+android.nonTransitiveRClass=false
+android.nonFinalResIds=false
diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties
index 6b665338..8bc9958a 100644
--- a/example/android/gradle/wrapper/gradle-wrapper.properties
+++ b/example/android/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,5 @@
-#Fri Jun 23 08:50:38 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip
diff --git a/example/android/settings.gradle b/example/android/settings.gradle
index 44e62bcf..55c4ca8b 100644
--- a/example/android/settings.gradle
+++ b/example/android/settings.gradle
@@ -1,11 +1,20 @@
-include ':app'
+pluginManagement {
+ def flutterSdkPath = {
+ def properties = new Properties()
+ file("local.properties").withInputStream { properties.load(it) }
+ def flutterSdkPath = properties.getProperty("flutter.sdk")
+ assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+ return flutterSdkPath
+ }
+ settings.ext.flutterSdkPath = flutterSdkPath()
-def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
-def properties = new Properties()
+ includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")
-assert localPropertiesFile.exists()
-localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
+ plugins {
+ id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false
+ }
+}
-def flutterSdkPath = properties.getProperty("flutter.sdk")
-assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
-apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
+include ":app"
+
+apply from: "${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle/app_plugin_loader.gradle"
diff --git a/example/assets/images/screenshot_1.png b/example/assets/images/screenshot_1.png
new file mode 100644
index 00000000..b42afad0
Binary files /dev/null and b/example/assets/images/screenshot_1.png differ
diff --git a/example/assets/images/screenshot_2.png b/example/assets/images/screenshot_2.png
new file mode 100644
index 00000000..c492b3c8
Binary files /dev/null and b/example/assets/images/screenshot_2.png differ
diff --git a/example/assets/images/screenshot_3.png b/example/assets/images/screenshot_3.png
new file mode 100644
index 00000000..3debe473
Binary files /dev/null and b/example/assets/images/screenshot_3.png differ
diff --git a/example/assets/images/screenshot_4.png b/example/assets/images/screenshot_4.png
new file mode 100644
index 00000000..cafcd86e
Binary files /dev/null and b/example/assets/images/screenshot_4.png differ
diff --git a/example/assets/sample_data.json b/example/assets/sample_data.json
deleted file mode 100644
index 467bcadd..00000000
--- a/example/assets/sample_data.json
+++ /dev/null
@@ -1,540 +0,0 @@
-[
- {
- "insert": {
- "image": "iVBORw0KGgoAAAANSUhEUgAAAdIAAAHSCAYAAABYYEo2AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3hpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDY3IDc5LjE1Nzc0NywgMjAxNS8wMy8zMC0yMzo0MDo0MiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDphZDc4NWQ1MS1mMWI2LTQzZDUtOWYxOC00MGRiZWZhNWZjYWIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NDRBMkRDNkEzNUI4MTFFQTk0QTlDRTRGNzJBQkE1N0UiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NDRBMkRDNjkzNUI4MTFFQTk0QTlDRTRGNzJBQkE1N0UiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDphZDc4NWQ1MS1mMWI2LTQzZDUtOWYxOC00MGRiZWZhNWZjYWIiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6YWQ3ODVkNTEtZjFiNi00M2Q1LTlmMTgtNDBkYmVmYTVmY2Fi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rIIIdDVCG4Uv77DDDq+pwhak+re1xX9fa5BGUn7r1q0/4GERbCSfUaBAgQIFqkIgySAc4iPFGtKiRYuV7vuJ2QS92sCjax2Qbr/99vN4v3DhQv8gg1YaKFCgQPkFUgCUlptooe3atZvmvv8mU3Gc2lIXvdahjANSn0tKsBFl0mx1jUCBAgUKVDXk24mlTLXk8X/44YdewenQocN4uTQy1cANQFrDJCIempN+PmjatKmPFps7d24ZbTVQoECBAlW9ZgqIErVLsfltttnmVYFotnrpAUhrCCHxoHluueWW86g1yvtp06bVmgcVKFCgQDWdB4umT5/uC9jTUtAB6WvJY22qTADSGkgtWrT4ori4eAYS0NSpU32ZquAnDRQoUKD8aKNSYgDLNm3azNh6660XWABNgmgA0hpGSoVxYPow7+XsDo2LAwUKFCg/hPKyYMECz3ebN2/+MHV2rRYqXl2rhIjaZlrAnHDooYc+BKC+99570bvvvuvBVJ3ObeAR+9SdIFCgQIECrR3ZOJTPPvssmjx5sldq9tprr4dVEKQ2aaC1Gkh5mCQAY05o2bLlDID19ddfTyfB86qoMklHyQbJgQIFChRozQiwVLGFl19+2Ss0TZs2ndG1a9d360JBkFoFpAJHB6RR586dHwZYX3zxxXTbKktoprLp2z6OgQIFChRozYhKRdJKx40b5y19Tpl5uF27dnXi/mudaReiqfFuu+32EFISZapUwF698SArJYU800CBAgVadyLt8IMPPvCKS7du3R6mXGsA0kK7GadhyufZu3fvGY0bN55BdSMkJNU5TRbw5oGHWqSBAgUKtPaEMgIfHT9+vC+E42jG/vvv/y51dgOQFhDJrACYpqShaMcdd3yI/aNGjfLf4R+1lY44zpp4AwUKFCjQ2vPfV1991VsAyZzYbbfd/D5rCQxAWsPJtpBC60T73HfffR+msgYRZETvQuzXg1W7n0CBAgUKtPZE0OYXX3wRTZw40fPfffbZ56HNNtusztx/rWrsLTAljwkqKSmZ0ahRoxnY7d98803fG89KT3ofSggGChQo0LpppMSjoLA0bNgQs+4M+DGuNrVMC0BaAIRmqfwkHNw8QKeRRt27d3/o22+/je69915f95Fms3wvXyl2/WDaDRQoUKDslKklJTxUfBQees8990Rff/111L59+4f32msvb/2rK+mF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"
- },
- "attributes": {
- "width": "230",
- "style": "display: block; margin: auto; mobileWidth: 50; mobileHeight: 50; mobileMargin: 10; mobileAlignment: topLeft"
- }
- },
- {
- "insert": "Flutter Quill"
- },
- {
- "attributes": {
- "header": 1
- },
- "insert": "\n"
- },
- {
- "insert": {
- "video": "https://www.youtube.com/watch?v=V4hgdKhIqtc&list=PLbhaS_83B97s78HsDTtplRTEhcFsqSqIK&index=1"
- }
- },
- {
- "insert": {
- "video": "https://user-images.githubusercontent.com/122956/126238875-22e42501-ad41-4266-b1d6-3f89b5e3b79b.mp4"
- }
- },
- {
- "insert": "\nRich text editor for Flutter"
- },
- {
- "attributes": {
- "header": 2
- },
- "insert": "\n"
- },
- {
- "insert": "Quill component for Flutter"
- },
- {
- "attributes": {
- "header": 3
- },
- "insert": "\n"
- },
- {
- "insert": "This "
- },
- {
- "attributes": {
- "italic": true,
- "background": "transparent"
- },
- "insert": "library"
- },
- {
- "insert": " supports "
- },
- {
- "attributes": {
- "bold": true,
- "background": "#ebd6ff"
- },
- "insert": "mobile"
- },
- {
- "insert": " platform "
- },
- {
- "attributes": {
- "underline": true,
- "bold": true,
- "color": "#e60000"
- },
- "insert": "only"
- },
- {
- "attributes": {
- "color": "rgba(0, 0, 0, 0.847)"
- },
- "insert": " and "
- },
- {
- "attributes": {
- "strike": true,
- "color": "black"
- },
- "insert": "web"
- },
- {
- "insert": " is not supported.\nYou are welcome to use "
- },
- {
- "attributes": {
- "link": "https://bulletjournal.us/home/index.html"
- },
- "insert": "Bullet Journal"
- },
- {
- "insert": ":\nTrack personal and group journals (ToDo, Note, Ledger) from multiple views with timely reminders"
- },
- {
- "attributes": {
- "list": "ordered"
- },
- "insert": "\n"
- },
- {
- "insert": "Share your tasks and notes with teammates, and see changes as they happen in real-time, across all devices"
- },
- {
- "attributes": {
- "list": "ordered"
- },
- "insert": "\n"
- },
- {
- "insert": "Check out what you and your teammates are working on each day"
- },
- {
- "attributes": {
- "list": "ordered"
- },
- "insert": "\n"
- },
- {
- "insert": "\nSplitting bills with friends can never be easier."
- },
- {
- "attributes": {
- "list": "bullet"
- },
- "insert": "\n"
- },
- {
- "insert": "Start creating a group and invite your friends to join."
- },
- {
- "attributes": {
- "list": "bullet"
- },
- "insert": "\n"
- },
- {
- "insert": "Create a BuJo of Ledger type to see expense or balance summary."
- },
- {
- "attributes": {
- "list": "bullet"
- },
- "insert": "\n"
- },
- {
- "insert": "\nAttach one or multiple labels to tasks, notes or transactions. Later you can track them just using the label(s)."
- },
- {
- "attributes": {
- "blockquote": true
- },
- "insert": "\n"
- },
- {
- "insert": "\nvar BuJo = 'Bullet' + 'Journal'"
- },
- {
- "attributes": {
- "code-block": true
- },
- "insert": "\n"
- },
- {
- "insert": "\nStart tracking in your browser"
- },
- {
- "attributes": {
- "indent": 1
- },
- "insert": "\n"
- },
- {
- "insert": "Stop the timer on your phone"
- },
- {
- "attributes": {
- "indent": 1
- },
- "insert": "\n"
- },
- {
- "insert": "All your time entries are synced"
- },
- {
- "attributes": {
- "indent": 2
- },
- "insert": "\n"
- },
- {
- "insert": "between the phone apps"
- },
- {
- "attributes": {
- "indent": 2
- },
- "insert": "\n"
- },
- {
- "insert": "and the website."
- },
- {
- "attributes": {
- "indent": 3
- },
- "insert": "\n"
- },
- {
- "insert": "\n"
- },
- {
- "insert": "\nCenter Align"
- },
- {
- "attributes": {
- "align": "center"
- },
- "insert": "\n"
- },
- {
- "insert": "Right Align"
- },
- {
- "attributes": {
- "align": "right"
- },
- "insert": "\n"
- },
- {
- "insert": "Justify Align"
- },
- {
- "attributes": {
- "align": "justify"
- },
- "insert": "\n"
- },
- {
- "insert": "Have trouble finding things? "
- },
- {
- "attributes": {
- "list": "ordered"
- },
- "insert": "\n"
- },
- {
- "insert": "Just type in the search bar"
- },
- {
- "attributes": {
- "indent": 1,
- "list": "ordered"
- },
- "insert": "\n"
- },
- {
- "insert": "and easily find contents"
- },
- {
- "attributes": {
- "indent": 2,
- "list": "ordered"
- },
- "insert": "\n"
- },
- {
- "insert": "across projects or folders."
- },
- {
- "attributes": {
- "indent": 2,
- "list": "ordered"
- },
- "insert": "\n"
- },
- {
- "insert": "It matches text in your note or task."
- },
- {
- "attributes": {
- "indent": 1,
- "list": "ordered"
- },
- "insert": "\n"
- },
- {
- "insert": "Enable reminders so that you will get notified by"
- },
- {
- "attributes": {
- "list": "ordered"
- },
- "insert": "\n"
- },
- {
- "insert": "email"
- },
- {
- "attributes": {
- "indent": 1,
- "list": "ordered"
- },
- "insert": "\n"
- },
- {
- "insert": "message on your phone"
- },
- {
- "attributes": {
- "indent": 1,
- "list": "ordered"
- },
- "insert": "\n"
- },
- {
- "insert": "popup on the web site"
- },
- {
- "attributes": {
- "indent": 1,
- "list": "ordered"
- },
- "insert": "\n"
- },
- {
- "insert": "Create a BuJo serving as project or folder"
- },
- {
- "attributes": {
- "list": "bullet"
- },
- "insert": "\n"
- },
- {
- "insert": "Organize your"
- },
- {
- "attributes": {
- "indent": 1,
- "list": "bullet"
- },
- "insert": "\n"
- },
- {
- "insert": "tasks"
- },
- {
- "attributes": {
- "indent": 2,
- "list": "bullet"
- },
- "insert": "\n"
- },
- {
- "insert": "notes"
- },
- {
- "attributes": {
- "indent": 2,
- "list": "bullet"
- },
- "insert": "\n"
- },
- {
- "insert": "transactions"
- },
- {
- "attributes": {
- "indent": 2,
- "list": "bullet"
- },
- "insert": "\n"
- },
- {
- "insert": "under BuJo "
- },
- {
- "attributes": {
- "indent": 3,
- "list": "bullet"
- },
- "insert": "\n"
- },
- {
- "insert": "See them in Calendar"
- },
- {
- "attributes": {
- "list": "bullet"
- },
- "insert": "\n"
- },
- {
- "insert": "or hierarchical view"
- },
- {
- "attributes": {
- "indent": 1,
- "list": "bullet"
- },
- "insert": "\n"
- },
- {
- "insert": "this is a check list"
- },
- {
- "attributes": {
- "list": "checked"
- },
- "insert": "\n"
- },
- {
- "insert": "this is a uncheck list"
- },
- {
- "attributes": {
- "list": "unchecked"
- },
- "insert": "\n"
- },
- {
- "insert": "Font "
- },
- {
- "attributes": {
- "font": "sans-serif"
- },
- "insert": "Sans Serif"
- },
- {
- "insert": " "
- },
- {
- "attributes": {
- "font": "serif"
- },
- "insert": "Serif"
- },
- {
- "insert": " "
- },
- {
- "attributes": {
- "font": "monospace"
- },
- "insert": "Monospace"
- },
- {
- "insert": " Size "
- },
- {
- "attributes": {
- "size": "small"
- },
- "insert": "Small"
- },
- {
- "insert": " "
- },
- {
- "attributes": {
- "size": "large"
- },
- "insert": "Large"
- },
- {
- "insert": " "
- },
- {
- "attributes": {
- "size": "huge"
- },
- "insert": "Huge"
- },
- {
- "attributes": {
- "size": "15.0"
- },
- "insert": "font size 15"
- },
- {
- "insert": " "
- },
- {
- "attributes": {
- "size": "35"
- },
- "insert": "font size 35"
- },
- {
- "insert": " "
- },
- {
- "attributes": {
- "size": "20"
- },
- "insert": "font size 20"
- },
- {
- "attributes": {
- "token": "built_in"
- },
- "insert": " diff"
- },
- {
- "attributes": {
- "token": "operator"
- },
- "insert": "-match"
- },
- {
- "attributes": {
- "token": "literal"
- },
- "insert": "-patch"
- },
- {
- "insert": {
- "image": "https://user-images.githubusercontent.com/122956/72955931-ccc07900-3d52-11ea-89b1-d468a6e2aa2b.png"
- },
- "attributes": {
- "width": "230",
- "style": "display: block; margin: auto;"
- }
- },
- {
- "insert": "\n"
- }
-]
\ No newline at end of file
diff --git a/example/assets/sample_data_nomedia.json b/example/assets/sample_data_nomedia.json
deleted file mode 100644
index e541d153..00000000
--- a/example/assets/sample_data_nomedia.json
+++ /dev/null
@@ -1,521 +0,0 @@
-[
- {
- "insert": "Flutter Quill"
- },
- {
- "attributes": {
- "header": 1
- },
- "insert": "\n"
- },
- {
- "insert": "\nRich text editor for Flutter"
- },
- {
- "attributes": {
- "header": 2
- },
- "insert": "\n"
- },
- {
- "insert": "Quill component for Flutter"
- },
- {
- "attributes": {
- "header": 3
- },
- "insert": "\n"
- },
- {
- "insert": "This "
- },
- {
- "attributes": {
- "italic": true,
- "background": "transparent"
- },
- "insert": "library"
- },
- {
- "insert": " supports "
- },
- {
- "attributes": {
- "bold": true,
- "background": "#ebd6ff"
- },
- "insert": "mobile"
- },
- {
- "insert": " platform "
- },
- {
- "attributes": {
- "underline": true,
- "bold": true,
- "color": "#e60000"
- },
- "insert": "only"
- },
- {
- "attributes": {
- "color": "rgba(0, 0, 0, 0.847)"
- },
- "insert": " and "
- },
- {
- "attributes": {
- "strike": true,
- "color": "black"
- },
- "insert": "web"
- },
- {
- "insert": " is not supported.\nYou are welcome to use "
- },
- {
- "attributes": {
- "link": "https://bulletjournal.us/home/index.html"
- },
- "insert": "Bullet Journal"
- },
- {
- "insert": ":\nTrack personal and group journals (ToDo, Note, Ledger) from multiple views with timely reminders"
- },
- {
- "attributes": {
- "list": "ordered"
- },
- "insert": "\n"
- },
- {
- "insert": "Share your tasks and notes with teammates, and see changes as they happen in real-time, across all devices"
- },
- {
- "attributes": {
- "list": "ordered"
- },
- "insert": "\n"
- },
- {
- "insert": "Check out what you and your teammates are working on each day"
- },
- {
- "attributes": {
- "list": "ordered"
- },
- "insert": "\n"
- },
- {
- "insert": "\nSplitting bills with friends can never be easier."
- },
- {
- "attributes": {
- "list": "bullet"
- },
- "insert": "\n"
- },
- {
- "insert": "Start creating a group and invite your friends to join."
- },
- {
- "attributes": {
- "list": "bullet"
- },
- "insert": "\n"
- },
- {
- "insert": "Create a BuJo of Ledger type to see expense or balance summary."
- },
- {
- "attributes": {
- "list": "bullet"
- },
- "insert": "\n"
- },
- {
- "insert": "\nAttach one or multiple labels to tasks, notes or transactions. Later you can track them just using the label(s)."
- },
- {
- "attributes": {
- "blockquote": true
- },
- "insert": "\n"
- },
- {
- "insert": "\nvar BuJo = 'Bullet' + 'Journal'"
- },
- {
- "attributes": {
- "code-block": true
- },
- "insert": "\n"
- },
- {
- "insert": "\nStart tracking in your browser"
- },
- {
- "attributes": {
- "indent": 1
- },
- "insert": "\n"
- },
- {
- "insert": "Stop the timer on your phone"
- },
- {
- "attributes": {
- "indent": 1
- },
- "insert": "\n"
- },
- {
- "insert": "All your time entries are synced"
- },
- {
- "attributes": {
- "indent": 2
- },
- "insert": "\n"
- },
- {
- "insert": "between the phone apps"
- },
- {
- "attributes": {
- "indent": 2
- },
- "insert": "\n"
- },
- {
- "insert": "and the website."
- },
- {
- "attributes": {
- "indent": 3
- },
- "insert": "\n"
- },
- {
- "insert": "\n"
- },
- {
- "insert": "\nCenter Align"
- },
- {
- "attributes": {
- "align": "center"
- },
- "insert": "\n"
- },
- {
- "insert": "Right Align"
- },
- {
- "attributes": {
- "align": "right"
- },
- "insert": "\n"
- },
- {
- "insert": "Justify Align"
- },
- {
- "attributes": {
- "align": "justify"
- },
- "insert": "\n"
- },
- {
- "insert": "Have trouble finding things? "
- },
- {
- "attributes": {
- "list": "ordered"
- },
- "insert": "\n"
- },
- {
- "insert": "Just type in the search bar"
- },
- {
- "attributes": {
- "indent": 1,
- "list": "ordered"
- },
- "insert": "\n"
- },
- {
- "insert": "and easily find contents"
- },
- {
- "attributes": {
- "indent": 2,
- "list": "ordered"
- },
- "insert": "\n"
- },
- {
- "insert": "across projects or folders."
- },
- {
- "attributes": {
- "indent": 2,
- "list": "ordered"
- },
- "insert": "\n"
- },
- {
- "insert": "It matches text in your note or task."
- },
- {
- "attributes": {
- "indent": 1,
- "list": "ordered"
- },
- "insert": "\n"
- },
- {
- "insert": "Enable reminders so that you will get notified by"
- },
- {
- "attributes": {
- "list": "ordered"
- },
- "insert": "\n"
- },
- {
- "insert": "email"
- },
- {
- "attributes": {
- "indent": 1,
- "list": "ordered"
- },
- "insert": "\n"
- },
- {
- "insert": "message on your phone"
- },
- {
- "attributes": {
- "indent": 1,
- "list": "ordered"
- },
- "insert": "\n"
- },
- {
- "insert": "popup on the web site"
- },
- {
- "attributes": {
- "indent": 1,
- "list": "ordered"
- },
- "insert": "\n"
- },
- {
- "insert": "Create a BuJo serving as project or folder"
- },
- {
- "attributes": {
- "list": "bullet"
- },
- "insert": "\n"
- },
- {
- "insert": "Organize your"
- },
- {
- "attributes": {
- "indent": 1,
- "list": "bullet"
- },
- "insert": "\n"
- },
- {
- "insert": "tasks"
- },
- {
- "attributes": {
- "indent": 2,
- "list": "bullet"
- },
- "insert": "\n"
- },
- {
- "insert": "notes"
- },
- {
- "attributes": {
- "indent": 2,
- "list": "bullet"
- },
- "insert": "\n"
- },
- {
- "insert": "transactions"
- },
- {
- "attributes": {
- "indent": 2,
- "list": "bullet"
- },
- "insert": "\n"
- },
- {
- "insert": "under BuJo "
- },
- {
- "attributes": {
- "indent": 3,
- "list": "bullet"
- },
- "insert": "\n"
- },
- {
- "insert": "See them in Calendar"
- },
- {
- "attributes": {
- "list": "bullet"
- },
- "insert": "\n"
- },
- {
- "insert": "or hierarchical view"
- },
- {
- "attributes": {
- "indent": 1,
- "list": "bullet"
- },
- "insert": "\n"
- },
- {
- "insert": "this is a check list"
- },
- {
- "attributes": {
- "list": "checked"
- },
- "insert": "\n"
- },
- {
- "insert": "this is a uncheck list"
- },
- {
- "attributes": {
- "list": "unchecked"
- },
- "insert": "\n"
- },
- {
- "insert": "Font "
- },
- {
- "attributes": {
- "font": "sans-serif"
- },
- "insert": "Sans Serif"
- },
- {
- "insert": " "
- },
- {
- "attributes": {
- "font": "serif"
- },
- "insert": "Serif"
- },
- {
- "insert": " "
- },
- {
- "attributes": {
- "font": "monospace"
- },
- "insert": "Monospace"
- },
- {
- "insert": " Size "
- },
- {
- "attributes": {
- "size": "small"
- },
- "insert": "Small"
- },
- {
- "insert": " "
- },
- {
- "attributes": {
- "size": "large"
- },
- "insert": "Large"
- },
- {
- "insert": " "
- },
- {
- "attributes": {
- "size": "huge"
- },
- "insert": "Huge"
- },
- {
- "attributes": {
- "size": "15.0"
- },
- "insert": "font size 15"
- },
- {
- "insert": " "
- },
- {
- "attributes": {
- "size": "35"
- },
- "insert": "font size 35"
- },
- {
- "insert": " "
- },
- {
- "attributes": {
- "size": "20"
- },
- "insert": "font size 20"
- },
- {
- "attributes": {
- "token": "built_in"
- },
- "insert": " diff"
- },
- {
- "attributes": {
- "token": "operator"
- },
- "insert": "-match"
- },
- {
- "attributes": {
- "token": "literal"
- },
- "insert": "-patch"
- },
- {
- "insert": {
- "image": "https://user-images.githubusercontent.com/122956/72955931-ccc07900-3d52-11ea-89b1-d468a6e2aa2b.png"
- },
- "attributes": {
- "width": "230",
- "style": "display: block; margin: auto;"
- }
- },
- {
- "insert": "\n"
- }
-]
\ No newline at end of file
diff --git a/example/ios/.gitignore b/example/ios/.gitignore
index e96ef602..7a7f9873 100644
--- a/example/ios/.gitignore
+++ b/example/ios/.gitignore
@@ -1,3 +1,4 @@
+**/dgph
*.mode1v3
*.mode2v3
*.moved-aside
@@ -18,6 +19,7 @@ Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
+Flutter/ephemeral/
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist
index 4f8d4d24..9625e105 100644
--- a/example/ios/Flutter/AppFrameworkInfo.plist
+++ b/example/ios/Flutter/AppFrameworkInfo.plist
@@ -3,7 +3,7 @@
CFBundleDevelopmentRegion
- $(DEVELOPMENT_LANGUAGE)
+ en
CFBundleExecutable
App
CFBundleIdentifier
diff --git a/example/ios/Podfile b/example/ios/Podfile
index 88359b22..fdcc671e 100644
--- a/example/ios/Podfile
+++ b/example/ios/Podfile
@@ -32,6 +32,9 @@ target 'Runner' do
use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
+ target 'RunnerTests' do
+ inherit! :search_paths
+ end
end
post_install do |installer|
diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj
index 01a3496a..75c0e507 100644
--- a/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/example/ios/Runner.xcodeproj/project.pbxproj
@@ -9,14 +9,23 @@
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
- 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };
- 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
- D290BBC2BCE42906E260DD85 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC245C6D0FF6BF1D0B733C5B /* Pods_Runner.framework */; };
+ 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
/* End PBXBuildFile section */
+/* Begin PBXContainerItemProxy section */
+ 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 97C146E61CF9000F007C117D /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 97C146ED1CF9000F007C117D;
+ remoteInfo = Runner;
+ };
+/* End PBXContainerItemProxy section */
+
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
@@ -33,22 +42,19 @@
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
- 2F435AE316A2CEF9DB2FCB44 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
- 45FB9682812B691627A14497 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
- 48A1E3B04AC3F3F79EE01940 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
- 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
- 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
- 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
- DC245C6D0FF6BF1D0B733C5B /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; };
+ 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -56,24 +62,12 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- D290BBC2BCE42906E260DD85 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
- 54D582D77359D2F1CFEABAD6 /* Pods */ = {
- isa = PBXGroup;
- children = (
- 2F435AE316A2CEF9DB2FCB44 /* Pods-Runner.debug.xcconfig */,
- 48A1E3B04AC3F3F79EE01940 /* Pods-Runner.release.xcconfig */,
- 45FB9682812B691627A14497 /* Pods-Runner.profile.xcconfig */,
- );
- name = Pods;
- path = Pods;
- sourceTree = "";
- };
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
@@ -85,14 +79,21 @@
name = Flutter;
sourceTree = "";
};
+ 331C8082294A63A400263BE5 /* RunnerTests */ = {
+ isa = PBXGroup;
+ children = (
+ 331C807B294A618700263BE5 /* RunnerTests.swift */,
+ );
+ path = RunnerTests;
+ sourceTree = "";
+ };
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
- 54D582D77359D2F1CFEABAD6 /* Pods */,
- 9B5485B940DE97CC1A7B761C /* Frameworks */,
+ 331C8082294A63A400263BE5 /* RunnerTests */,
);
sourceTree = "";
};
@@ -100,6 +101,7 @@
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
+ 331C8081294A63A400263BE5 /* RunnerTests.xctest */,
);
name = Products;
sourceTree = "";
@@ -107,50 +109,49 @@
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
- 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */,
- 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */,
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
- 97C146F11CF9000F007C117D /* Supporting Files */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
);
path = Runner;
sourceTree = "";
};
- 97C146F11CF9000F007C117D /* Supporting Files */ = {
- isa = PBXGroup;
- children = (
- 97C146F21CF9000F007C117D /* main.m */,
- );
- name = "Supporting Files";
- sourceTree = "";
- };
- 9B5485B940DE97CC1A7B761C /* Frameworks */ = {
- isa = PBXGroup;
- children = (
- DC245C6D0FF6BF1D0B733C5B /* Pods_Runner.framework */,
- );
- name = Frameworks;
- sourceTree = "";
- };
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
+ 331C8080294A63A400263BE5 /* RunnerTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
+ buildPhases = (
+ 331C807D294A63A400263BE5 /* Sources */,
+ 331C807E294A63A400263BE5 /* Frameworks */,
+ 331C807F294A63A400263BE5 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 331C8086294A63A400263BE5 /* PBXTargetDependency */,
+ );
+ name = RunnerTests;
+ productName = RunnerTests;
+ productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
- C781F89B47F2E7DB7E2B4898 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
- 869AA06D856BCA582968F111 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@@ -167,11 +168,17 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
+ BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 1430;
ORGANIZATIONNAME = "";
TargetAttributes = {
+ 331C8080294A63A400263BE5 = {
+ CreatedOnToolsVersion = 14.0;
+ TestTargetID = 97C146ED1CF9000F007C117D;
+ };
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
+ LastSwiftMigration = 1100;
};
};
};
@@ -189,11 +196,19 @@
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
+ 331C8080294A63A400263BE5 /* RunnerTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
+ 331C807F294A63A400263BE5 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -224,23 +239,6 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
- 869AA06D856BCA582968F111 /* [CP] Embed Pods Frameworks */ = {
- isa = PBXShellScriptBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- inputFileListPaths = (
- "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
- );
- name = "[CP] Embed Pods Frameworks";
- outputFileListPaths = (
- "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
- );
- runOnlyForDeploymentPostprocessing = 0;
- shellPath = /bin/sh;
- shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
- showEnvVarsInLog = 0;
- };
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
@@ -256,43 +254,36 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
- C781F89B47F2E7DB7E2B4898 /* [CP] Check Pods Manifest.lock */ = {
- isa = PBXShellScriptBuildPhase;
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 331C807D294A63A400263BE5 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- );
- inputFileListPaths = (
- );
- inputPaths = (
- "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
- "${PODS_ROOT}/Manifest.lock",
- );
- name = "[CP] Check Pods Manifest.lock";
- outputFileListPaths = (
- );
- outputPaths = (
- "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+ 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
- shellPath = /bin/sh;
- shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
- showEnvVarsInLog = 0;
};
-/* End PBXShellScriptBuildPhase section */
-
-/* Begin PBXSourcesBuildPhase section */
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */,
- 97C146F31CF9000F007C117D /* main.m in Sources */,
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
+/* Begin PBXTargetDependency section */
+ 331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 97C146ED1CF9000F007C117D /* Runner */;
+ targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
@@ -368,27 +359,72 @@
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
- FRAMEWORK_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
- LIBRARY_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
- PRODUCT_BUNDLE_IDENTIFIER = com.example.app;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
+ 331C8088294A63A400263BE5 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = AE0B7B92F70575B8D7E0D07E /* Pods-RunnerTests.debug.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
+ };
+ name = Debug;
+ };
+ 331C8089294A63A400263BE5 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 89B67EB44CE7B6631473024E /* Pods-RunnerTests.release.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
+ };
+ name = Release;
+ };
+ 331C808A294A63A400263BE5 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 640959BDD8F10B91D80A66BE /* Pods-RunnerTests.profile.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
+ };
+ name = Profile;
+ };
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -489,6 +525,8 @@
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
@@ -499,23 +537,19 @@
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
- FRAMEWORK_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
- LIBRARY_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
- PRODUCT_BUNDLE_IDENTIFIER = com.example.app;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
@@ -525,23 +559,18 @@
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
- FRAMEWORK_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
- LIBRARY_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
- PRODUCT_BUNDLE_IDENTIFIER = com.example.app;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
@@ -549,6 +578,16 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
+ 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 331C8088294A63A400263BE5 /* Debug */,
+ 331C8089294A63A400263BE5 /* Release */,
+ 331C808A294A63A400263BE5 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
index b52b2e69..87131a09 100644
--- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
+++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -27,8 +27,6 @@
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
-
-
-
-
+
+
+
+
+
+
-
-
-
-
diff --git a/example/ios/Runner/AppDelegate.h b/example/ios/Runner/AppDelegate.h
deleted file mode 100644
index 36e21bbf..00000000
--- a/example/ios/Runner/AppDelegate.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#import
-#import
-
-@interface AppDelegate : FlutterAppDelegate
-
-@end
diff --git a/example/ios/Runner/AppDelegate.m b/example/ios/Runner/AppDelegate.m
deleted file mode 100644
index 70e83933..00000000
--- a/example/ios/Runner/AppDelegate.m
+++ /dev/null
@@ -1,13 +0,0 @@
-#import "AppDelegate.h"
-#import "GeneratedPluginRegistrant.h"
-
-@implementation AppDelegate
-
-- (BOOL)application:(UIApplication *)application
- didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
- [GeneratedPluginRegistrant registerWithRegistry:self];
- // Override point for customization after application launch.
- return [super application:application didFinishLaunchingWithOptions:launchOptions];
-}
-
-@end
diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
index 28c6bf03..7353c41e 100644
Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ
diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
index 2ccbfd96..797d452e 100644
Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ
diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
index f091b6b0..6ed2d933 100644
Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ
diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
index 4cde1211..4cd7b009 100644
Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ
diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
index d0ef06e7..fe730945 100644
Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ
diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
index dcdc2306..321773cd 100644
Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ
diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
index 2ccbfd96..797d452e 100644
Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ
diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
index c8f9ed8f..502f463a 100644
Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ
diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
index a6d6b860..0ec30343 100644
Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ
diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
index a6d6b860..0ec30343 100644
Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ
diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
index 75b2d164..e9f5fea2 100644
Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ
diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
index c4df70d3..84ac32ae 100644
Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ
diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
index 6a84f41e..8953cba0 100644
Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ
diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
index d0e1f585..0467bf12 100644
Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist
index b8fc1f1d..be041944 100644
--- a/example/ios/Runner/Info.plist
+++ b/example/ios/Runner/Info.plist
@@ -4,6 +4,8 @@
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
+ CFBundleDisplayName
+ Example
CFBundleExecutable
$(EXECUTABLE_NAME)
CFBundleIdentifier
@@ -11,7 +13,7 @@
CFBundleInfoDictionaryVersion
6.0
CFBundleName
- app
+ example
CFBundlePackageType
APPL
CFBundleShortVersionString
@@ -20,8 +22,6 @@
????
CFBundleVersion
$(FLUTTER_BUILD_NUMBER)
- NSPhotoLibraryUsageDescription
- Need to save image
LSRequiresIPhoneOS
UILaunchStoryboardName
@@ -41,11 +41,17 @@
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
- UIViewControllerBasedStatusBarAppearance
-
CADisableMinimumFrameDurationOnPhone
UIApplicationSupportsIndirectInputEvents
+ NSPhotoLibraryUsageDescription
+ We need permission to the photo library in order for inserting images in the text editor
+ NSCameraUsageDescription
+ We need permission to the camera in order for takeing a photos and record videos in the text editor
+ NSMicrophoneUsageDescription
+ We don't really need that permission
+ NSPhotoLibraryAddUsageDescription
+ We need this permission for saving the images in the editor
diff --git a/example/ios/Runner/main.m b/example/ios/Runner/main.m
deleted file mode 100644
index dff6597e..00000000
--- a/example/ios/Runner/main.m
+++ /dev/null
@@ -1,9 +0,0 @@
-#import
-#import
-#import "AppDelegate.h"
-
-int main(int argc, char* argv[]) {
- @autoreleasepool {
- return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
- }
-}
diff --git a/example/ios/RunnerTests/RunnerTests.swift b/example/ios/RunnerTests/RunnerTests.swift
new file mode 100644
index 00000000..86a7c3b1
--- /dev/null
+++ b/example/ios/RunnerTests/RunnerTests.swift
@@ -0,0 +1,12 @@
+import Flutter
+import UIKit
+import XCTest
+
+class RunnerTests: XCTestCase {
+
+ func testExample() {
+ // If you add code to the Runner application, consider adding tests here.
+ // See https://developer.apple.com/documentation/xctest for more information about using XCTest.
+ }
+
+}
diff --git a/example/lib/gen/assets.gen.dart b/example/lib/gen/assets.gen.dart
new file mode 100644
index 00000000..3075816e
--- /dev/null
+++ b/example/lib/gen/assets.gen.dart
@@ -0,0 +1,114 @@
+/// GENERATED CODE - DO NOT MODIFY BY HAND
+/// *****************************************************
+/// FlutterGen
+/// *****************************************************
+
+// coverage:ignore-file
+// ignore_for_file: type=lint
+// ignore_for_file: directives_ordering,unnecessary_import,implicit_dynamic_list_literal,deprecated_member_use
+
+import 'package:flutter/widgets.dart';
+
+class $AssetsImagesGen {
+ const $AssetsImagesGen();
+
+ /// File path: assets/images/screenshot_1.png
+ AssetGenImage get screenshot1 =>
+ const AssetGenImage('assets/images/screenshot_1.png');
+
+ /// File path: assets/images/screenshot_2.png
+ AssetGenImage get screenshot2 =>
+ const AssetGenImage('assets/images/screenshot_2.png');
+
+ /// File path: assets/images/screenshot_3.png
+ AssetGenImage get screenshot3 =>
+ const AssetGenImage('assets/images/screenshot_3.png');
+
+ /// File path: assets/images/screenshot_4.png
+ AssetGenImage get screenshot4 =>
+ const AssetGenImage('assets/images/screenshot_4.png');
+
+ /// List of all assets
+ List get values =>
+ [screenshot1, screenshot2, screenshot3, screenshot4];
+}
+
+class Assets {
+ Assets._();
+
+ static const $AssetsImagesGen images = $AssetsImagesGen();
+}
+
+class AssetGenImage {
+ const AssetGenImage(this._assetName);
+
+ final String _assetName;
+
+ Image image({
+ Key? key,
+ AssetBundle? bundle,
+ ImageFrameBuilder? frameBuilder,
+ ImageErrorWidgetBuilder? errorBuilder,
+ String? semanticLabel,
+ bool excludeFromSemantics = false,
+ double? scale,
+ double? width,
+ double? height,
+ Color? color,
+ Animation? opacity,
+ BlendMode? colorBlendMode,
+ BoxFit? fit,
+ AlignmentGeometry alignment = Alignment.center,
+ ImageRepeat repeat = ImageRepeat.noRepeat,
+ Rect? centerSlice,
+ bool matchTextDirection = false,
+ bool gaplessPlayback = false,
+ bool isAntiAlias = false,
+ String? package,
+ FilterQuality filterQuality = FilterQuality.low,
+ int? cacheWidth,
+ int? cacheHeight,
+ }) {
+ return Image.asset(
+ _assetName,
+ key: key,
+ bundle: bundle,
+ frameBuilder: frameBuilder,
+ errorBuilder: errorBuilder,
+ semanticLabel: semanticLabel,
+ excludeFromSemantics: excludeFromSemantics,
+ scale: scale,
+ width: width,
+ height: height,
+ color: color,
+ opacity: opacity,
+ colorBlendMode: colorBlendMode,
+ fit: fit,
+ alignment: alignment,
+ repeat: repeat,
+ centerSlice: centerSlice,
+ matchTextDirection: matchTextDirection,
+ gaplessPlayback: gaplessPlayback,
+ isAntiAlias: isAntiAlias,
+ package: package,
+ filterQuality: filterQuality,
+ cacheWidth: cacheWidth,
+ cacheHeight: cacheHeight,
+ );
+ }
+
+ ImageProvider provider({
+ AssetBundle? bundle,
+ String? package,
+ }) {
+ return AssetImage(
+ _assetName,
+ bundle: bundle,
+ package: package,
+ );
+ }
+
+ String get path => _assetName;
+
+ String get keyName => _assetName;
+}
diff --git a/example/lib/gen/fonts.gen.dart b/example/lib/gen/fonts.gen.dart
new file mode 100644
index 00000000..f61cfa18
--- /dev/null
+++ b/example/lib/gen/fonts.gen.dart
@@ -0,0 +1,39 @@
+/// GENERATED CODE - DO NOT MODIFY BY HAND
+/// *****************************************************
+/// FlutterGen
+/// *****************************************************
+
+// coverage:ignore-file
+// ignore_for_file: type=lint
+// ignore_for_file: directives_ordering,unnecessary_import,implicit_dynamic_list_literal,deprecated_member_use
+
+class FontFamily {
+ FontFamily._();
+
+ /// Font family: SF-UI-Display
+ static const String sFUIDisplay = 'SF-UI-Display';
+
+ /// Font family: ibarra-real-nova
+ static const String ibarraRealNova = 'ibarra-real-nova';
+
+ /// Font family: monospace
+ static const String monospace = 'monospace';
+
+ /// Font family: nunito
+ static const String nunito = 'nunito';
+
+ /// Font family: pacifico
+ static const String pacifico = 'pacifico';
+
+ /// Font family: roboto-mono
+ static const String robotoMono = 'roboto-mono';
+
+ /// Font family: sans-serif
+ static const String sansSerif = 'sans-serif';
+
+ /// Font family: serif
+ static const String serif = 'serif';
+
+ /// Font family: square-peg
+ static const String squarePeg = 'square-peg';
+}
diff --git a/example/lib/logic/empty.dart b/example/lib/logic/empty.dart
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/example/lib/logic/empty.dart
@@ -0,0 +1 @@
+
diff --git a/example/lib/main.dart b/example/lib/main.dart
index c4a44613..e263fb25 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -1,33 +1,155 @@
+import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/material.dart';
-import 'package:flutter_localizations/flutter_localizations.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:flutter_localizations/flutter_localizations.dart'
+ show
+ GlobalCupertinoLocalizations,
+ GlobalMaterialLocalizations,
+ GlobalWidgetsLocalizations;
+import 'package:flutter_quill/flutter_quill.dart' show Document;
+import 'package:flutter_quill/translations.dart' show FlutterQuillLocalizations;
+import 'package:hydrated_bloc/hydrated_bloc.dart'
+ show HydratedBloc, HydratedStorage;
+import 'package:path_provider/path_provider.dart'
+ show getApplicationDocumentsDirectory;
-import 'pages/home_page.dart';
+import 'presentation/home/widgets/home_screen.dart';
+import 'presentation/quill/quill_screen.dart';
+import 'presentation/quill/samples/quill_default_sample.dart';
+import 'presentation/quill/samples/quill_images_sample.dart';
+import 'presentation/quill/samples/quill_text_sample.dart';
+import 'presentation/quill/samples/quill_videos_sample.dart';
+import 'presentation/settings/cubit/settings_cubit.dart';
+import 'presentation/settings/widgets/settings_screen.dart';
-void main() {
+void main() async {
WidgetsFlutterBinding.ensureInitialized();
- runApp(MyApp());
+ HydratedBloc.storage = await HydratedStorage.build(
+ storageDirectory: kIsWeb
+ ? HydratedStorage.webStorageDirectory
+ : await getApplicationDocumentsDirectory(),
+ );
+ runApp(const MyApp());
}
class MyApp extends StatelessWidget {
+ const MyApp({super.key});
+
@override
Widget build(BuildContext context) {
- return MaterialApp(
- debugShowCheckedModeBanner: false,
- title: 'Quill Demo',
- theme: ThemeData(
- primarySwatch: Colors.blue,
- visualDensity: VisualDensity.adaptivePlatformDensity,
- ),
- localizationsDelegates: [
- GlobalMaterialLocalizations.delegate,
- GlobalWidgetsLocalizations.delegate,
- GlobalCupertinoLocalizations.delegate,
- ],
- supportedLocales: [
- const Locale('en', 'US'),
- const Locale('zh', 'HK'),
+ return MultiBlocProvider(
+ providers: [
+ BlocProvider(
+ create: (context) => SettingsCubit(),
+ ),
],
- home: HomePage(),
+ child: BlocBuilder(
+ builder: (context, state) {
+ return MaterialApp(
+ title: 'Flutter Quill Demo',
+ theme: ThemeData.light(useMaterial3: true),
+ darkTheme: ThemeData.dark(useMaterial3: true),
+ themeMode: state.themeMode,
+ debugShowCheckedModeBanner: false,
+ localizationsDelegates: const [
+ GlobalMaterialLocalizations.delegate,
+ GlobalWidgetsLocalizations.delegate,
+ GlobalCupertinoLocalizations.delegate,
+ // FlutterQuillLocalizations.delegate,
+ ],
+ supportedLocales: FlutterQuillLocalizations.supportedLocales,
+ routes: {
+ SettingsScreen.routeName: (context) => const SettingsScreen(),
+ },
+ onGenerateRoute: (settings) {
+ final name = settings.name;
+ if (name == HomeScreen.routeName) {
+ return MaterialPageRoute(
+ builder: (context) {
+ return const HomeScreen();
+ },
+ );
+ }
+ if (name == QuillScreen.routeName) {
+ return MaterialPageRoute(
+ builder: (context) {
+ final args = settings.arguments as QuillScreenArgs;
+ return QuillScreen(
+ args: args,
+ );
+ },
+ );
+ }
+ return null;
+ },
+ onUnknownRoute: (settings) {
+ return MaterialPageRoute(
+ builder: (context) => Scaffold(
+ appBar: AppBar(
+ title: const Text('Not found'),
+ ),
+ body: const Text('404'),
+ ),
+ );
+ },
+ home: Builder(
+ builder: (context) {
+ final screen = switch (state.defaultScreen) {
+ DefaultScreen.home => const HomeScreen(),
+ DefaultScreen.settings => const SettingsScreen(),
+ DefaultScreen.imagesSample => QuillScreen(
+ args: QuillScreenArgs(
+ document: Document.fromJson(quillImagesSample),
+ ),
+ ),
+ DefaultScreen.videosSample => QuillScreen(
+ args: QuillScreenArgs(
+ document: Document.fromJson(quillVideosSample),
+ ),
+ ),
+ DefaultScreen.textSample => QuillScreen(
+ args: QuillScreenArgs(
+ document: Document.fromJson(quillTextSample),
+ ),
+ ),
+ DefaultScreen.emptySample => QuillScreen(
+ args: QuillScreenArgs(
+ document: Document(),
+ ),
+ ),
+ DefaultScreen.defaultSample => QuillScreen(
+ args: QuillScreenArgs(
+ document: Document.fromJson(quillDefaultSample),
+ ),
+ ),
+ };
+ return AnimatedSwitcher(
+ duration: const Duration(milliseconds: 330),
+ transitionBuilder: (child, animation) {
+ // This animation is from flutter.dev example
+ const begin = Offset(0, 1);
+ const end = Offset.zero;
+ const curve = Curves.ease;
+
+ final tween = Tween(
+ begin: begin,
+ end: end,
+ ).chain(
+ CurveTween(curve: curve),
+ );
+
+ return SlideTransition(
+ position: animation.drive(tween),
+ child: child,
+ );
+ },
+ child: screen,
+ );
+ },
+ ),
+ );
+ },
+ ),
);
}
}
diff --git a/example/lib/pages/home_page.dart b/example/lib/pages/home_page.dart
deleted file mode 100644
index fffc4abb..00000000
--- a/example/lib/pages/home_page.dart
+++ /dev/null
@@ -1,564 +0,0 @@
-// ignore_for_file: avoid_redundant_argument_values
-
-import 'dart:async';
-import 'dart:convert';
-import 'dart:io' show File, Platform;
-import 'dart:ui';
-
-import 'package:file_picker/file_picker.dart';
-import 'package:filesystem_picker/filesystem_picker.dart';
-import 'package:flutter/foundation.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter/services.dart';
-import 'package:flutter_quill/extensions.dart';
-import 'package:flutter_quill/flutter_quill.dart';
-import 'package:flutter_quill_extensions/flutter_quill_extensions.dart';
-import 'package:path/path.dart' as path;
-import 'package:path_provider/path_provider.dart';
-
-import '../universal_ui/universal_ui.dart';
-import '../widgets/time_stamp_embed_widget.dart';
-import 'read_only_page.dart';
-
-enum _SelectionType {
- none,
- word,
- // line,
-}
-
-class HomePage extends StatefulWidget {
- @override
- _HomePageState createState() => _HomePageState();
-}
-
-class _HomePageState extends State {
- late final QuillController _controller;
- late final Future _loadDocumentFromAssetsFuture;
- final FocusNode _focusNode = FocusNode();
- Timer? _selectAllTimer;
- _SelectionType _selectionType = _SelectionType.none;
-
- @override
- void dispose() {
- _selectAllTimer?.cancel();
- // Dispose the controller to free resources
- _controller.dispose();
- super.dispose();
- }
-
- @override
- void initState() {
- super.initState();
- _loadDocumentFromAssetsFuture = _loadFromAssets();
- }
-
- Future _loadFromAssets() async {
- try {
- final result = await rootBundle.loadString(isDesktop()
- ? 'assets/sample_data_nomedia.json'
- : 'assets/sample_data.json');
- final doc = Document.fromJson(jsonDecode(result));
- _controller = QuillController(
- document: doc,
- selection: const TextSelection.collapsed(offset: 0),
- );
- } catch (error) {
- final doc = Document()..insert(0, 'Empty asset');
- _controller = QuillController(
- document: doc,
- selection: const TextSelection.collapsed(offset: 0),
- );
- }
- }
-
- @override
- Widget build(BuildContext context) {
- return FutureBuilder(
- future: _loadDocumentFromAssetsFuture,
- builder: (context, snapshot) {
- if (snapshot.connectionState == ConnectionState.waiting) {
- return const Scaffold(
- body: Center(child: CircularProgressIndicator.adaptive()),
- );
- }
- return Scaffold(
- appBar: AppBar(
- backgroundColor: Colors.grey.shade800,
- elevation: 0,
- centerTitle: false,
- title: const Text(
- 'Flutter Quill',
- ),
- actions: [
- IconButton(
- onPressed: () => _insertTimeStamp(
- _controller,
- DateTime.now().toString(),
- ),
- icon: const Icon(Icons.add_alarm_rounded),
- ),
- IconButton(
- onPressed: () => showDialog(
- context: context,
- builder: (context) => AlertDialog(
- content: Text(_controller.document.toPlainText([
- ...FlutterQuillEmbeds.builders(),
- TimeStampEmbedBuilderWidget()
- ])),
- ),
- ),
- icon: const Icon(Icons.text_fields_rounded),
- )
- ],
- ),
- drawer: Container(
- constraints: BoxConstraints(
- maxWidth: MediaQuery.sizeOf(context).width * 0.7),
- color: Colors.grey.shade800,
- child: _buildMenuBar(context),
- ),
- body: _buildWelcomeEditor(context),
- );
- },
- );
- }
-
- bool _onTripleClickSelection() {
- final controller = _controller;
-
- _selectAllTimer?.cancel();
- _selectAllTimer = null;
-
- // If you want to select all text after paragraph, uncomment this line
- // if (_selectionType == _SelectionType.line) {
- // final selection = TextSelection(
- // baseOffset: 0,
- // extentOffset: controller.document.length,
- // );
-
- // controller.updateSelection(selection, ChangeSource.REMOTE);
-
- // _selectionType = _SelectionType.none;
-
- // return true;
- // }
-
- if (controller.selection.isCollapsed) {
- _selectionType = _SelectionType.none;
- }
-
- if (_selectionType == _SelectionType.none) {
- _selectionType = _SelectionType.word;
- _startTripleClickTimer();
- return false;
- }
-
- if (_selectionType == _SelectionType.word) {
- final child = controller.document.queryChild(
- controller.selection.baseOffset,
- );
- final offset = child.node?.documentOffset ?? 0;
- final length = child.node?.length ?? 0;
-
- final selection = TextSelection(
- baseOffset: offset,
- extentOffset: offset + length,
- );
-
- controller.updateSelection(selection, ChangeSource.REMOTE);
-
- // _selectionType = _SelectionType.line;
-
- _selectionType = _SelectionType.none;
-
- _startTripleClickTimer();
-
- return true;
- }
-
- return false;
- }
-
- void _startTripleClickTimer() {
- _selectAllTimer = Timer(const Duration(milliseconds: 900), () {
- _selectionType = _SelectionType.none;
- });
- }
-
- QuillEditor get quillEditor {
- if (kIsWeb) {
- return QuillEditor(
- focusNode: _focusNode,
- scrollController: ScrollController(),
- configurations: QuillEditorConfigurations(
- placeholder: 'Add content',
- readOnly: false,
- scrollable: true,
- autoFocus: false,
- expands: false,
- padding: EdgeInsets.zero,
- onTapUp: (details, p1) {
- return _onTripleClickSelection();
- },
- customStyles: const DefaultStyles(
- h1: DefaultTextBlockStyle(
- TextStyle(
- fontSize: 32,
- color: Colors.black,
- height: 1.15,
- fontWeight: FontWeight.w300,
- ),
- VerticalSpacing(16, 0),
- VerticalSpacing(0, 0),
- null),
- sizeSmall: TextStyle(fontSize: 9),
- ),
- embedBuilders: [
- ...defaultEmbedBuildersWeb,
- TimeStampEmbedBuilderWidget()
- ],
- ),
- );
- }
- return QuillEditor(
- configurations: QuillEditorConfigurations(
- placeholder: 'Add content',
- readOnly: false,
- autoFocus: false,
- enableSelectionToolbar: isMobile(),
- expands: false,
- padding: EdgeInsets.zero,
- onImagePaste: _onImagePaste,
- onTapUp: (details, p1) {
- return _onTripleClickSelection();
- },
- customStyles: const DefaultStyles(
- h1: DefaultTextBlockStyle(
- TextStyle(
- fontSize: 32,
- color: Colors.black,
- height: 1.15,
- fontWeight: FontWeight.w300,
- ),
- VerticalSpacing(16, 0),
- VerticalSpacing(0, 0),
- null),
- sizeSmall: TextStyle(fontSize: 9),
- subscript: TextStyle(
- fontFamily: 'SF-UI-Display',
- fontFeatures: [FontFeature.subscripts()],
- ),
- superscript: TextStyle(
- fontFamily: 'SF-UI-Display',
- fontFeatures: [FontFeature.superscripts()],
- ),
- ),
- embedBuilders: [
- ...FlutterQuillEmbeds.builders(),
- TimeStampEmbedBuilderWidget()
- ],
- ),
- scrollController: ScrollController(),
- focusNode: _focusNode,
- );
- }
-
- QuillToolbar get quillToolbar {
- if (kIsWeb) {
- return QuillToolbar(
- configurations: QuillToolbarConfigurations(
- embedButtons: FlutterQuillEmbeds.buttons(
- onImagePickCallback: _onImagePickCallback,
- webImagePickImpl: _webImagePickImpl,
- ),
- buttonOptions: QuillToolbarButtonOptions(
- base: QuillToolbarBaseButtonOptions(
- afterButtonPressed: _focusNode.requestFocus,
- ),
- ),
- ),
- // afterButtonPressed: _focusNode.requestFocus,
- );
- }
- if (_isDesktop()) {
- return QuillToolbar(
- configurations: QuillToolbarConfigurations(
- embedButtons: FlutterQuillEmbeds.buttons(
- onImagePickCallback: _onImagePickCallback,
- filePickImpl: openFileSystemPickerForDesktop,
- ),
- showAlignmentButtons: true,
- buttonOptions: QuillToolbarButtonOptions(
- base: QuillToolbarBaseButtonOptions(
- afterButtonPressed: _focusNode.requestFocus,
- ),
- ),
- ),
- // afterButtonPressed: _focusNode.requestFocus,
- );
- }
- return QuillToolbar(
- configurations: QuillToolbarConfigurations(
- embedButtons: FlutterQuillEmbeds.buttons(
- // provide a callback to enable picking images from device.
- // if omit, "image" button only allows adding images from url.
- // same goes for videos.
- onImagePickCallback: _onImagePickCallback,
- onVideoPickCallback: _onVideoPickCallback,
- // uncomment to provide a custom "pick from" dialog.
- // mediaPickSettingSelector: _selectMediaPickSetting,
- // uncomment to provide a custom "pick from" dialog.
- // cameraPickSettingSelector: _selectCameraPickSetting,
- ),
- showAlignmentButtons: true,
- buttonOptions: QuillToolbarButtonOptions(
- base: QuillToolbarBaseButtonOptions(
- afterButtonPressed: _focusNode.requestFocus,
- ),
- ),
- ),
- // afterButtonPressed: _focusNode.requestFocus,
- );
- }
-
- Widget _buildWelcomeEditor(BuildContext context) {
- // BUG in web!! should not releated to this pull request
- ///
- ///══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═════════════════════
- ///══════════════════════════════════════
- // The following bool object was thrown building MediaQuery
- //(MediaQueryData(size: Size(769.0, 1205.0),
- // devicePixelRatio: 1.0, textScaleFactor: 1.0, platformBrightness:
- //Brightness.dark, padding:
- // EdgeInsets.zero, viewPadding: EdgeInsets.zero, viewInsets:
- // EdgeInsets.zero,
- // systemGestureInsets:
- // EdgeInsets.zero, alwaysUse24HourFormat: false, accessibleNavigation:
- // false,
- // highContrast: false,
- // disableAnimations: false, invertColors: false, boldText: false,
- //navigationMode: traditional,
- // gestureSettings: DeviceGestureSettings(touchSlop: null), displayFeatures:
- // []
- // )):
- // false
- // The relevant error-causing widget was:
- // SafeArea
- ///
- ///
- return SafeArea(
- child: QuillProvider(
- configurations: QuillConfigurations(
- controller: _controller,
- ),
- child: Column(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Expanded(
- flex: 15,
- child: Container(
- color: Colors.white,
- padding: const EdgeInsets.only(left: 16, right: 16),
- child: quillEditor,
- ),
- ),
- kIsWeb
- ? Expanded(
- child: Container(
- padding:
- const EdgeInsets.symmetric(vertical: 16, horizontal: 8),
- child: quillToolbar,
- ))
- : Container(
- child: quillToolbar,
- )
- ],
- ),
- ),
- );
- }
-
- bool _isDesktop() => !kIsWeb && !Platform.isAndroid && !Platform.isIOS;
-
- Future openFileSystemPickerForDesktop(BuildContext context) async {
- return await FilesystemPicker.open(
- context: context,
- rootDirectory: await getApplicationDocumentsDirectory(),
- fsType: FilesystemType.file,
- fileTileSelectMode: FileTileSelectMode.wholeTile,
- );
- }
-
- // Renders the image picked by imagePicker from local file storage
- // You can also upload the picked image to any server (eg : AWS s3
- // or Firebase) and then return the uploaded image URL.
- Future _onImagePickCallback(File file) async {
- // Copies the picked file from temporary cache to applications directory
- final appDocDir = await getApplicationDocumentsDirectory();
- final copiedFile =
- await file.copy('${appDocDir.path}/${path.basename(file.path)}');
- return copiedFile.path.toString();
- }
-
- Future _webImagePickImpl(
- OnImagePickCallback onImagePickCallback) async {
- final result = await FilePicker.platform.pickFiles();
- if (result == null) {
- return null;
- }
-
- // Take first, because we don't allow picking multiple files.
- final fileName = result.files.first.name;
- final file = File(fileName);
-
- return onImagePickCallback(file);
- }
-
- // Renders the video picked by imagePicker from local file storage
- // You can also upload the picked video to any server (eg : AWS s3
- // or Firebase) and then return the uploaded video URL.
- Future _onVideoPickCallback(File file) async {
- // Copies the picked file from temporary cache to applications directory
- final appDocDir = await getApplicationDocumentsDirectory();
- final copiedFile =
- await file.copy('${appDocDir.path}/${path.basename(file.path)}');
- return copiedFile.path.toString();
- }
-
- // ignore: unused_element
- Future _selectMediaPickSetting(BuildContext context) =>
- showDialog(
- context: context,
- builder: (ctx) => AlertDialog(
- contentPadding: EdgeInsets.zero,
- content: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- TextButton.icon(
- icon: const Icon(Icons.collections),
- label: const Text('Gallery'),
- onPressed: () => Navigator.pop(ctx, MediaPickSetting.Gallery),
- ),
- TextButton.icon(
- icon: const Icon(Icons.link),
- label: const Text('Link'),
- onPressed: () => Navigator.pop(ctx, MediaPickSetting.Link),
- )
- ],
- ),
- ),
- );
-
- // ignore: unused_element
- Future _selectCameraPickSetting(BuildContext context) =>
- showDialog(
- context: context,
- builder: (ctx) => AlertDialog(
- contentPadding: EdgeInsets.zero,
- content: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- TextButton.icon(
- icon: const Icon(Icons.camera),
- label: const Text('Capture a photo'),
- onPressed: () => Navigator.pop(ctx, MediaPickSetting.Camera),
- ),
- TextButton.icon(
- icon: const Icon(Icons.video_call),
- label: const Text('Capture a video'),
- onPressed: () => Navigator.pop(ctx, MediaPickSetting.Video),
- )
- ],
- ),
- ),
- );
-
- Widget _buildMenuBar(BuildContext context) {
- final size = MediaQuery.sizeOf(context);
- const itemStyle = TextStyle(
- color: Colors.white,
- fontSize: 18,
- fontWeight: FontWeight.bold,
- );
- return Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- Divider(
- thickness: 2,
- color: Colors.white,
- indent: size.width * 0.1,
- endIndent: size.width * 0.1,
- ),
- ListTile(
- title: const Center(child: Text('Read only demo', style: itemStyle)),
- dense: true,
- visualDensity: VisualDensity.compact,
- onTap: _readOnly,
- ),
- Divider(
- thickness: 2,
- color: Colors.white,
- indent: size.width * 0.1,
- endIndent: size.width * 0.1,
- ),
- ],
- );
- }
-
- void _readOnly() {
- Navigator.pop(super.context);
- Navigator.push(
- super.context,
- MaterialPageRoute(
- builder: (context) => ReadOnlyPage(),
- ),
- );
- }
-
- Future _onImagePaste(Uint8List imageBytes) async {
- // Saves the image to applications directory
- final appDocDir = await getApplicationDocumentsDirectory();
- final file = await File(
- '${appDocDir.path}/${path.basename('${DateTime.now().millisecondsSinceEpoch}.png')}',
- ).writeAsBytes(imageBytes, flush: true);
- return file.path.toString();
- }
-
- static void _insertTimeStamp(QuillController controller, String string) {
- controller.document.insert(controller.selection.extentOffset, '\n');
- controller.updateSelection(
- TextSelection.collapsed(
- offset: controller.selection.extentOffset + 1,
- ),
- ChangeSource.LOCAL,
- );
-
- controller.document.insert(
- controller.selection.extentOffset,
- TimeStampEmbed(string),
- );
-
- controller.updateSelection(
- TextSelection.collapsed(
- offset: controller.selection.extentOffset + 1,
- ),
- ChangeSource.LOCAL,
- );
-
- controller.document.insert(controller.selection.extentOffset, ' ');
- controller.updateSelection(
- TextSelection.collapsed(
- offset: controller.selection.extentOffset + 1,
- ),
- ChangeSource.LOCAL,
- );
-
- controller.document.insert(controller.selection.extentOffset, '\n');
- controller.updateSelection(
- TextSelection.collapsed(
- offset: controller.selection.extentOffset + 1,
- ),
- ChangeSource.LOCAL,
- );
- }
-}
diff --git a/example/lib/pages/read_only_page.dart b/example/lib/pages/read_only_page.dart
deleted file mode 100644
index 5693f6b3..00000000
--- a/example/lib/pages/read_only_page.dart
+++ /dev/null
@@ -1,81 +0,0 @@
-// ignore_for_file: avoid_redundant_argument_values
-
-import 'package:flutter/foundation.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_quill/extensions.dart';
-import 'package:flutter_quill/flutter_quill.dart';
-import 'package:flutter_quill_extensions/flutter_quill_extensions.dart';
-
-import '../universal_ui/universal_ui.dart';
-import '../widgets/demo_scaffold.dart';
-
-class ReadOnlyPage extends StatefulWidget {
- @override
- _ReadOnlyPageState createState() => _ReadOnlyPageState();
-}
-
-class _ReadOnlyPageState extends State {
- final FocusNode _focusNode = FocusNode();
-
- bool _edit = false;
-
- @override
- Widget build(BuildContext context) {
- return DemoScaffold(
- documentFilename: isDesktop()
- ? 'assets/sample_data_nomedia.json'
- : 'sample_data_nomedia.json',
- builder: _buildContent,
- showToolbar: _edit == true,
- floatingActionButton: FloatingActionButton.extended(
- label: Text(_edit == true ? 'Done' : 'Edit'),
- onPressed: _toggleEdit,
- icon: Icon(_edit == true ? Icons.check : Icons.edit),
- ),
- );
- }
-
- Widget _buildContent(BuildContext context, QuillController? controller) {
- var quillEditor = QuillEditor(
- configurations: QuillEditorConfigurations(
- expands: false,
- padding: EdgeInsets.zero,
- embedBuilders: FlutterQuillEmbeds.builders(),
- scrollable: true,
- autoFocus: true,
- ),
- scrollController: ScrollController(),
- focusNode: _focusNode,
- // readOnly: !_edit,
- );
- if (kIsWeb) {
- quillEditor = QuillEditor(
- configurations: QuillEditorConfigurations(
- autoFocus: true,
- expands: false,
- padding: EdgeInsets.zero,
- embedBuilders: defaultEmbedBuildersWeb,
- scrollable: true,
- ),
- scrollController: ScrollController(),
- focusNode: _focusNode,
- );
- }
- return Padding(
- padding: const EdgeInsets.all(8),
- child: Container(
- decoration: BoxDecoration(
- color: Colors.white,
- border: Border.all(color: Colors.grey.shade200),
- ),
- child: quillEditor,
- ),
- );
- }
-
- void _toggleEdit() {
- setState(() {
- _edit = !_edit;
- });
- }
-}
diff --git a/example/lib/presentation/extensions/scaffold_messenger.dart b/example/lib/presentation/extensions/scaffold_messenger.dart
new file mode 100644
index 00000000..c46783de
--- /dev/null
+++ b/example/lib/presentation/extensions/scaffold_messenger.dart
@@ -0,0 +1,13 @@
+import 'package:flutter/material.dart'
+ show ScaffoldMessenger, ScaffoldMessengerState, SnackBar;
+import 'package:flutter/widgets.dart' show BuildContext, Text;
+
+extension ScaffoldMessengerStateExt on ScaffoldMessengerState {
+ void showText(String text) {
+ showSnackBar(SnackBar(content: Text(text)));
+ }
+}
+
+extension BuildContextExt on BuildContext {
+ ScaffoldMessengerState get messenger => ScaffoldMessenger.of(this);
+}
diff --git a/example/lib/presentation/home/widgets/example_item.dart b/example/lib/presentation/home/widgets/example_item.dart
new file mode 100644
index 00000000..5c1badd3
--- /dev/null
+++ b/example/lib/presentation/home/widgets/example_item.dart
@@ -0,0 +1,53 @@
+import 'package:flutter/material.dart';
+
+class HomeScreenExampleItem extends StatelessWidget {
+ const HomeScreenExampleItem({
+ required this.title,
+ required this.icon,
+ required this.text,
+ required this.onPressed,
+ super.key,
+ });
+ final String title;
+ final Widget icon;
+ final String text;
+ final VoidCallback onPressed;
+
+ @override
+ Widget build(BuildContext context) {
+ return Column(
+ children: [
+ SizedBox(
+ height: 200,
+ width: double.infinity,
+ child: GestureDetector(
+ onTap: onPressed,
+ child: Card(
+ child: SingleChildScrollView(
+ child: Column(
+ children: [
+ const SizedBox(height: 2),
+ Text(
+ title,
+ style: Theme.of(context).textTheme.titleLarge,
+ ),
+ const SizedBox(height: 8),
+ icon,
+ const SizedBox(height: 8),
+ Padding(
+ padding: const EdgeInsets.all(8),
+ child: Text(
+ text,
+ style: Theme.of(context).textTheme.bodyMedium,
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ),
+ ],
+ );
+ }
+}
diff --git a/example/lib/presentation/home/widgets/home_screen.dart b/example/lib/presentation/home/widgets/home_screen.dart
new file mode 100644
index 00000000..2ea2a8c7
--- /dev/null
+++ b/example/lib/presentation/home/widgets/home_screen.dart
@@ -0,0 +1,188 @@
+import 'dart:convert' show jsonDecode;
+
+import 'package:cross_file/cross_file.dart';
+import 'package:file_picker/file_picker.dart' show FilePicker, FileType;
+import 'package:flutter/material.dart';
+import 'package:flutter_quill/flutter_quill.dart';
+
+import '../../extensions/scaffold_messenger.dart';
+import '../../quill/quill_screen.dart';
+import '../../quill/samples/quill_default_sample.dart';
+import '../../quill/samples/quill_images_sample.dart';
+import '../../quill/samples/quill_text_sample.dart';
+import '../../quill/samples/quill_videos_sample.dart';
+import '../../settings/widgets/settings_screen.dart';
+import 'example_item.dart';
+
+class HomeScreen extends StatelessWidget {
+ const HomeScreen({super.key});
+
+ static const routeName = '/home';
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text('Flutter Quill Demo'),
+ ),
+ drawer: Drawer(
+ child: ListView(
+ children: [
+ const DrawerHeader(
+ child: Text(
+ 'Flutter Quill Demo',
+ ),
+ ),
+ ListTile(
+ title: const Text('Settings'),
+ leading: const Icon(Icons.settings),
+ onTap: () {
+ Navigator.of(context)
+ ..pop()
+ ..pushNamed(SettingsScreen.routeName);
+ },
+ ),
+ ],
+ ),
+ ),
+ body: SafeArea(
+ child: Column(
+ children: [
+ SizedBox(
+ width: double.infinity,
+ child: Text(
+ 'Welcome to Flutter Quill Demo!',
+ style: Theme.of(context).textTheme.titleLarge,
+ textAlign: TextAlign.center,
+ ),
+ ),
+ Expanded(
+ child: ListView(
+ padding: const EdgeInsets.all(16),
+ children: [
+ HomeScreenExampleItem(
+ title: 'Default',
+ icon: const Icon(
+ Icons.home,
+ size: 50,
+ ),
+ text:
+ 'If you want to see how the editor work with default content, '
+ 'see any samples or you are working on it',
+ onPressed: () => Navigator.of(context).pushNamed(
+ QuillScreen.routeName,
+ arguments: QuillScreenArgs(
+ document: Document.fromJson(quillDefaultSample),
+ ),
+ ),
+ ),
+ const SizedBox(height: 4),
+ HomeScreenExampleItem(
+ title: 'Images',
+ icon: const Icon(
+ Icons.image,
+ size: 50,
+ ),
+ text: 'If you want to see how the editor work with images, '
+ 'see any samples or you are working on it',
+ onPressed: () => Navigator.of(context).pushNamed(
+ QuillScreen.routeName,
+ arguments: QuillScreenArgs(
+ document: Document.fromJson(quillImagesSample),
+ ),
+ ),
+ ),
+ const SizedBox(height: 4),
+ HomeScreenExampleItem(
+ title: 'Videos',
+ icon: const Icon(
+ Icons.video_chat,
+ size: 50,
+ ),
+ text: 'If you want to see how the editor work with videos, '
+ 'see any samples or you are working on it',
+ onPressed: () => Navigator.of(context).pushNamed(
+ QuillScreen.routeName,
+ arguments: QuillScreenArgs(
+ document: Document.fromJson(quillVideosSample),
+ ),
+ ),
+ ),
+ HomeScreenExampleItem(
+ title: 'Text',
+ icon: const Icon(
+ Icons.edit_document,
+ size: 50,
+ ),
+ text: 'If you want to see how the editor work with text, '
+ 'see any samples or you are working on it',
+ onPressed: () => Navigator.of(context).pushNamed(
+ QuillScreen.routeName,
+ arguments: QuillScreenArgs(
+ document: Document.fromJson(quillTextSample),
+ ),
+ ),
+ ),
+ HomeScreenExampleItem(
+ title: 'Open a document by delta json',
+ icon: const Icon(
+ Icons.file_copy,
+ size: 50,
+ ),
+ text: 'If you want to load a document by delta json file',
+ onPressed: () async {
+ final scaffoldMessenger = ScaffoldMessenger.of(context);
+ final navigator = Navigator.of(context);
+ try {
+ final result = await FilePicker.platform.pickFiles(
+ dialogTitle: 'Pick json delta',
+ type: FileType.custom,
+ allowedExtensions: ['json'],
+ allowMultiple: false,
+ );
+ final file = result?.files.firstOrNull;
+ final filePath = file?.path;
+ if (file == null || filePath == null) {
+ return;
+ }
+ final jsonString = await XFile(filePath).readAsString();
+
+ navigator.pushNamed(
+ QuillScreen.routeName,
+ arguments: QuillScreenArgs(
+ document: Document.fromJson(jsonDecode(jsonString)),
+ ),
+ );
+ } catch (e) {
+ print(
+ 'Error while loading json delta file: ${e.toString()}',
+ );
+ scaffoldMessenger.showText(
+ 'Error while loading json delta file: ${e.toString()}',
+ );
+ }
+ },
+ ),
+ HomeScreenExampleItem(
+ title: 'Empty',
+ icon: const Icon(
+ Icons.insert_drive_file,
+ size: 50,
+ ),
+ text: 'Want start clean? be my guest',
+ onPressed: () => Navigator.of(context).pushNamed(
+ QuillScreen.routeName,
+ arguments: QuillScreenArgs(
+ document: Document(),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/example/lib/widgets/time_stamp_embed_widget.dart b/example/lib/presentation/quill/embeds/timestamp_embed.dart
similarity index 80%
rename from example/lib/widgets/time_stamp_embed_widget.dart
rename to example/lib/presentation/quill/embeds/timestamp_embed.dart
index 968cb441..bffe16c6 100644
--- a/example/lib/widgets/time_stamp_embed_widget.dart
+++ b/example/lib/presentation/quill/embeds/timestamp_embed.dart
@@ -1,6 +1,7 @@
-import 'dart:convert';
+import 'dart:convert' show jsonDecode, jsonEncode;
-import 'package:flutter/material.dart';
+import 'package:flutter/material.dart' show Icons;
+import 'package:flutter/widgets.dart';
import 'package:flutter_quill/flutter_quill.dart';
class TimeStampEmbed extends Embeddable {
@@ -21,8 +22,8 @@ class TimeStampEmbedBuilderWidget extends EmbedBuilder {
String get key => 'timeStamp';
@override
- String toPlainText(Embed embed) {
- return embed.value.data;
+ String toPlainText(Embed node) {
+ return node.value.data;
}
@override
diff --git a/example/lib/presentation/quill/quill_editor.dart b/example/lib/presentation/quill/quill_editor.dart
new file mode 100644
index 00000000..133f9997
--- /dev/null
+++ b/example/lib/presentation/quill/quill_editor.dart
@@ -0,0 +1,138 @@
+import 'dart:io' as io show Directory, File;
+import 'dart:ui' show FontFeature;
+
+import 'package:cached_network_image/cached_network_image.dart'
+ show CachedNetworkImageProvider;
+import 'package:desktop_drop/desktop_drop.dart' show DropTarget;
+import 'package:flutter/material.dart';
+import 'package:flutter_quill/extensions.dart' show isAndroid, isIOS, isWeb;
+import 'package:flutter_quill/flutter_quill.dart';
+import 'package:flutter_quill_extensions/flutter_quill_extensions.dart';
+import 'package:flutter_quill_extensions/presentation/embeds/widgets/image.dart'
+ show getImageProviderByImageSource, imageFileExtensions;
+import 'package:path/path.dart' as path;
+
+import '../extensions/scaffold_messenger.dart';
+import 'embeds/timestamp_embed.dart';
+
+class MyQuillEditor extends StatelessWidget {
+ const MyQuillEditor({
+ required this.configurations,
+ required this.scrollController,
+ required this.focusNode,
+ super.key,
+ });
+
+ final QuillEditorConfigurations configurations;
+ final ScrollController scrollController;
+ final FocusNode focusNode;
+
+ @override
+ Widget build(BuildContext context) {
+ return QuillEditor(
+ scrollController: scrollController,
+ focusNode: focusNode,
+ configurations: configurations.copyWith(
+ customStyles: const DefaultStyles(
+ h1: DefaultTextBlockStyle(
+ TextStyle(
+ fontSize: 32,
+ height: 1.15,
+ fontWeight: FontWeight.w300,
+ ),
+ VerticalSpacing(16, 0),
+ VerticalSpacing(0, 0),
+ null,
+ ),
+ sizeSmall: TextStyle(fontSize: 9),
+ subscript: TextStyle(
+ fontFamily: 'SF-UI-Display',
+ fontFeatures: [FontFeature.subscripts()],
+ ),
+ superscript: TextStyle(
+ fontFamily: 'SF-UI-Display',
+ fontFeatures: [FontFeature.superscripts()],
+ ),
+ ),
+ scrollable: true,
+ placeholder: 'Start writting your notes...',
+ padding: const EdgeInsets.all(16),
+ onImagePaste: (imageBytes) async {
+ if (isWeb()) {
+ return null;
+ }
+ // We will save it to system temporary files
+ final newFileName = '${DateTime.now().toIso8601String()}.png';
+ final newPath = path.join(
+ io.Directory.systemTemp.path,
+ newFileName,
+ );
+ final file = await io.File(
+ newPath,
+ ).writeAsBytes(imageBytes, flush: true);
+ return file.path;
+ },
+ embedBuilders: [
+ ...(isWeb()
+ ? FlutterQuillEmbeds.editorWebBuilders()
+ : FlutterQuillEmbeds.editorBuilders(
+ imageEmbedConfigurations: QuillEditorImageEmbedConfigurations(
+ imageErrorWidgetBuilder: (context, error, stackTrace) {
+ return Text(
+ 'Error while loading an image: ${error.toString()}',
+ );
+ },
+ imageProviderBuilder: (imageUrl) {
+ // cached_network_image is supported
+ // only for Android, iOS and web
+
+ // We will use it only if image from network
+ if (isAndroid(supportWeb: false) ||
+ isIOS(supportWeb: false) ||
+ isWeb()) {
+ if (isHttpBasedUrl(imageUrl)) {
+ return CachedNetworkImageProvider(
+ imageUrl,
+ );
+ }
+ }
+ return getImageProviderByImageSource(
+ imageUrl,
+ imageProviderBuilder: null,
+ assetsPrefix: QuillSharedExtensionsConfigurations.get(
+ context: context)
+ .assetsPrefix,
+ );
+ },
+ ),
+ )),
+ TimeStampEmbedBuilderWidget(),
+ ],
+ builder: (context, rawEditor) {
+ // The `desktop_drop` plugin doesn't support iOS platform for now
+ if (isIOS(supportWeb: false)) {
+ return rawEditor;
+ }
+ return DropTarget(
+ onDragDone: (details) {
+ final scaffoldMessenger = ScaffoldMessenger.of(context);
+ final file = details.files.first;
+ final isSupported = imageFileExtensions.any(file.name.endsWith);
+ if (!isSupported) {
+ scaffoldMessenger.showText(
+ 'Only images are supported right now: ${file.mimeType}, ${file.name}, ${file.path}, $imageFileExtensions',
+ );
+ return;
+ }
+ context.requireQuillController.insertImageBlock(
+ imageSource: file.path,
+ );
+ scaffoldMessenger.showText('Image is inserted.');
+ },
+ child: rawEditor,
+ );
+ },
+ ),
+ );
+ }
+}
diff --git a/example/lib/presentation/quill/quill_screen.dart b/example/lib/presentation/quill/quill_screen.dart
new file mode 100644
index 00000000..aab91c83
--- /dev/null
+++ b/example/lib/presentation/quill/quill_screen.dart
@@ -0,0 +1,144 @@
+import 'dart:convert' show jsonEncode;
+
+import 'package:flutter/material.dart';
+import 'package:flutter_quill/flutter_quill.dart';
+import 'package:flutter_quill_extensions/flutter_quill_extensions.dart'
+ show FlutterQuillEmbeds, QuillSharedExtensionsConfigurations;
+
+import 'package:quill_html_converter/quill_html_converter.dart';
+import 'package:share_plus/share_plus.dart' show Share;
+
+import '../extensions/scaffold_messenger.dart';
+import '../shared/widgets/home_screen_button.dart';
+import 'quill_editor.dart';
+import 'quill_toolbar.dart';
+
+@immutable
+class QuillScreenArgs {
+ const QuillScreenArgs({required this.document});
+
+ final Document document;
+}
+
+class QuillScreen extends StatefulWidget {
+ const QuillScreen({
+ required this.args,
+ super.key,
+ });
+
+ final QuillScreenArgs args;
+
+ static const routeName = '/quill';
+
+ @override
+ State createState() => _QuillScreenState();
+}
+
+class _QuillScreenState extends State {
+ final _controller = QuillController.basic();
+ final _editorFocusNode = FocusNode();
+ final _editorScrollController = ScrollController();
+ var _isReadOnly = false;
+
+ @override
+ void initState() {
+ super.initState();
+ _controller.document = widget.args.document;
+ }
+
+ @override
+ void dispose() {
+ _controller.dispose();
+ _editorFocusNode.dispose();
+ _editorScrollController.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text('Flutter Quill'),
+ actions: [
+ IconButton(
+ tooltip: 'Load with HTML',
+ onPressed: () {
+ final html = _controller.document.toDelta().toHtml();
+ _controller.document =
+ Document.fromDelta(DeltaHtmlExt.fromHtml(html));
+ },
+ icon: const Icon(Icons.html),
+ ),
+ IconButton(
+ tooltip: 'Share',
+ onPressed: () {
+ final plainText = _controller.document.toPlainText(
+ FlutterQuillEmbeds.defaultEditorBuilders(),
+ );
+ if (plainText.trim().isEmpty) {
+ ScaffoldMessenger.of(context).showText(
+ "We can't share empty document, please enter some text first",
+ );
+ return;
+ }
+ Share.share(plainText);
+ },
+ icon: const Icon(Icons.share),
+ ),
+ IconButton(
+ tooltip: 'Print to log',
+ onPressed: () {
+ print(
+ jsonEncode(_controller.document.toDelta().toJson()),
+ );
+ ScaffoldMessenger.of(context).showText(
+ 'The quill delta json has been printed to the log.',
+ );
+ },
+ icon: const Icon(Icons.print),
+ ),
+ const HomeScreenButton(),
+ ],
+ ),
+ body: QuillProvider(
+ configurations: QuillConfigurations(
+ controller: _controller,
+ sharedConfigurations: QuillSharedConfigurations(
+ animationConfigurations: QuillAnimationConfigurations.disableAll(),
+ extraConfigurations: const {
+ QuillSharedExtensionsConfigurations.key:
+ QuillSharedExtensionsConfigurations(
+ assetsPrefix: 'assets',
+ ),
+ },
+ ),
+ ),
+ child: Column(
+ children: [
+ if (!_isReadOnly)
+ MyQuillToolbar(
+ focusNode: _editorFocusNode,
+ ),
+ Builder(
+ builder: (context) {
+ return Expanded(
+ child: MyQuillEditor(
+ configurations: QuillEditorConfigurations(
+ readOnly: _isReadOnly,
+ ),
+ scrollController: _editorScrollController,
+ focusNode: _editorFocusNode,
+ ),
+ );
+ },
+ ),
+ ],
+ ),
+ ),
+ floatingActionButton: FloatingActionButton(
+ child: Icon(_isReadOnly ? Icons.lock : Icons.edit),
+ onPressed: () => setState(() => _isReadOnly = !_isReadOnly),
+ ),
+ );
+ }
+}
diff --git a/example/lib/presentation/quill/quill_toolbar.dart b/example/lib/presentation/quill/quill_toolbar.dart
new file mode 100644
index 00000000..b0257dc1
--- /dev/null
+++ b/example/lib/presentation/quill/quill_toolbar.dart
@@ -0,0 +1,301 @@
+import 'dart:io' as io show File;
+
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:flutter_quill/extensions.dart' show isAndroid, isIOS, isWeb;
+import 'package:flutter_quill/flutter_quill.dart';
+import 'package:flutter_quill_extensions/flutter_quill_extensions.dart';
+import 'package:image_cropper/image_cropper.dart';
+import 'package:path/path.dart' as path;
+import 'package:path_provider/path_provider.dart'
+ show getApplicationDocumentsDirectory;
+
+import '../extensions/scaffold_messenger.dart';
+import '../settings/cubit/settings_cubit.dart';
+import 'embeds/timestamp_embed.dart';
+
+class MyQuillToolbar extends StatelessWidget {
+ const MyQuillToolbar({
+ required this.focusNode,
+ super.key,
+ });
+
+ final FocusNode focusNode;
+
+ Future onImageInsertWithCropping(
+ String image,
+ QuillController controller,
+ BuildContext context,
+ ) async {
+ final croppedFile = await ImageCropper().cropImage(
+ sourcePath: image,
+ aspectRatioPresets: [
+ CropAspectRatioPreset.square,
+ CropAspectRatioPreset.ratio3x2,
+ CropAspectRatioPreset.original,
+ CropAspectRatioPreset.ratio4x3,
+ CropAspectRatioPreset.ratio16x9
+ ],
+ uiSettings: [
+ AndroidUiSettings(
+ toolbarTitle: 'Cropper',
+ toolbarColor: Colors.deepOrange,
+ toolbarWidgetColor: Colors.white,
+ initAspectRatio: CropAspectRatioPreset.original,
+ lockAspectRatio: false,
+ ),
+ IOSUiSettings(
+ title: 'Cropper',
+ ),
+ WebUiSettings(
+ context: context,
+ ),
+ ],
+ );
+ final newImage = croppedFile?.path;
+ if (newImage == null) {
+ return;
+ }
+ if (isWeb()) {
+ controller.insertImageBlock(imageSource: newImage);
+ return;
+ }
+ final newSavedImage = await saveImage(io.File(newImage));
+ controller.insertImageBlock(imageSource: newSavedImage);
+ }
+
+ Future onImageInsert(String image, QuillController controller) async {
+ if (isWeb()) {
+ controller.insertImageBlock(imageSource: image);
+ return;
+ }
+ final newSavedImage = await saveImage(io.File(image));
+ controller.insertImageBlock(imageSource: newSavedImage);
+ }
+
+ /// For mobile platforms it will copies the picked file from temporary cache
+ /// to applications directory
+ ///
+ /// for desktop platforms, it will do the same but from user files this time
+ Future saveImage(io.File file) async {
+ final appDocDir = await getApplicationDocumentsDirectory();
+ final fileExt = path.extension(file.path);
+ final newFileName = '${DateTime.now().toIso8601String()}$fileExt';
+ final newPath = path.join(
+ appDocDir.path,
+ newFileName,
+ );
+ final copiedFile = await file.copy(newPath);
+ return copiedFile.path;
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return BlocBuilder(
+ buildWhen: (previous, current) =>
+ previous.useCustomQuillToolbar != current.useCustomQuillToolbar,
+ builder: (context, state) {
+ if (state.useCustomQuillToolbar) {
+ // For more info
+ // https://github.com/singerdmx/flutter-quill/blob/master/doc/custom_toolbar.md
+ return QuillToolbarProvider(
+ toolbarConfigurations: const QuillToolbarConfigurations(),
+ child: QuillBaseToolbar(
+ configurations: QuillBaseToolbarConfigurations(
+ toolbarSize: 15 * 2,
+ multiRowsDisplay: false,
+ childrenBuilder: (context) {
+ final controller = context.requireQuillController;
+ return [
+ QuillToolbarImageButton(
+ controller: controller,
+ options: const QuillToolbarImageButtonOptions(),
+ ),
+ QuillToolbarHistoryButton(
+ controller: controller,
+ options:
+ const QuillToolbarHistoryButtonOptions(isUndo: true),
+ ),
+ QuillToolbarHistoryButton(
+ controller: controller,
+ options:
+ const QuillToolbarHistoryButtonOptions(isUndo: false),
+ ),
+ QuillToolbarToggleStyleButton(
+ attribute: Attribute.bold,
+ controller: controller,
+ options: const QuillToolbarToggleStyleButtonOptions(
+ iconData: Icons.format_bold,
+ iconSize: 20,
+ ),
+ ),
+ QuillToolbarToggleStyleButton(
+ attribute: Attribute.italic,
+ controller: controller,
+ options: const QuillToolbarToggleStyleButtonOptions(
+ iconData: Icons.format_italic,
+ iconSize: 20,
+ ),
+ ),
+ QuillToolbarToggleStyleButton(
+ attribute: Attribute.underline,
+ controller: controller,
+ options: const QuillToolbarToggleStyleButtonOptions(
+ iconData: Icons.format_underline,
+ iconSize: 20,
+ ),
+ ),
+ QuillToolbarClearFormatButton(
+ controller: controller,
+ options: const QuillToolbarClearFormatButtonOptions(
+ iconData: Icons.format_clear,
+ iconSize: 20,
+ ),
+ ),
+ VerticalDivider(
+ indent: 12,
+ endIndent: 12,
+ color: Colors.grey.shade400,
+ ),
+ QuillToolbarSelectHeaderStyleButtons(
+ controller: controller,
+ options:
+ const QuillToolbarSelectHeaderStyleButtonsOptions(
+ iconSize: 20,
+ ),
+ ),
+ QuillToolbarToggleStyleButton(
+ attribute: Attribute.ol,
+ controller: controller,
+ options: const QuillToolbarToggleStyleButtonOptions(
+ iconData: Icons.format_list_numbered,
+ iconSize: 20,
+ ),
+ ),
+ QuillToolbarToggleStyleButton(
+ attribute: Attribute.ul,
+ controller: controller,
+ options: const QuillToolbarToggleStyleButtonOptions(
+ iconData: Icons.format_list_bulleted,
+ iconSize: 20,
+ ),
+ ),
+ QuillToolbarToggleStyleButton(
+ attribute: Attribute.blockQuote,
+ controller: controller,
+ options: const QuillToolbarToggleStyleButtonOptions(
+ iconData: Icons.format_quote,
+ iconSize: 20,
+ ),
+ ),
+ VerticalDivider(
+ indent: 12,
+ endIndent: 12,
+ color: Colors.grey.shade400,
+ ),
+ QuillToolbarIndentButton(
+ controller: controller,
+ isIncrease: true,
+ options: const QuillToolbarIndentButtonOptions(
+ iconData: Icons.format_indent_increase,
+ iconSize: 20,
+ )),
+ QuillToolbarIndentButton(
+ controller: controller,
+ isIncrease: false,
+ options: const QuillToolbarIndentButtonOptions(
+ iconData: Icons.format_indent_decrease,
+ iconSize: 20,
+ ),
+ ),
+ ];
+ },
+ ),
+ ),
+ );
+ }
+ return QuillToolbar(
+ configurations: QuillToolbarConfigurations(
+ showAlignmentButtons: true,
+ buttonOptions: QuillToolbarButtonOptions(
+ base: QuillToolbarBaseButtonOptions(
+ // Request editor focus when any button is pressed
+ afterButtonPressed: focusNode.requestFocus,
+ ),
+ ),
+ customButtons: [
+ QuillToolbarCustomButtonOptions(
+ icon: const Icon(Icons.add_alarm_rounded),
+ onPressed: () {
+ final controller = context.requireQuillController;
+ controller.document
+ .insert(controller.selection.extentOffset, '\n');
+ controller.updateSelection(
+ TextSelection.collapsed(
+ offset: controller.selection.extentOffset + 1,
+ ),
+ ChangeSource.local,
+ );
+
+ controller.document.insert(
+ controller.selection.extentOffset,
+ TimeStampEmbed(
+ DateTime.now().toString(),
+ ),
+ );
+
+ controller.updateSelection(
+ TextSelection.collapsed(
+ offset: controller.selection.extentOffset + 1,
+ ),
+ ChangeSource.local,
+ );
+
+ controller.document
+ .insert(controller.selection.extentOffset, ' ');
+ controller.updateSelection(
+ TextSelection.collapsed(
+ offset: controller.selection.extentOffset + 1,
+ ),
+ ChangeSource.local,
+ );
+
+ controller.document
+ .insert(controller.selection.extentOffset, '\n');
+ controller.updateSelection(
+ TextSelection.collapsed(
+ offset: controller.selection.extentOffset + 1,
+ ),
+ ChangeSource.local,
+ );
+ },
+ ),
+ QuillToolbarCustomButtonOptions(
+ icon: const Icon(Icons.ac_unit),
+ onPressed: () {
+ ScaffoldMessenger.of(context)
+ ..clearSnackBars()
+ ..showText(
+ 'Custom button!',
+ );
+ },
+ ),
+ ],
+ embedButtons: FlutterQuillEmbeds.toolbarButtons(
+ imageButtonOptions: QuillToolbarImageButtonOptions(
+ imageButtonConfigurations: QuillToolbarImageConfigurations(
+ onImageInsertCallback: isAndroid(supportWeb: false) ||
+ isIOS(supportWeb: false) ||
+ isWeb()
+ ? (image, controller) =>
+ onImageInsertWithCropping(image, controller, context)
+ : onImageInsert,
+ ),
+ ),
+ ),
+ ),
+ );
+ },
+ );
+ }
+}
diff --git a/example/lib/presentation/quill/samples/quill_default_sample.dart b/example/lib/presentation/quill/samples/quill_default_sample.dart
new file mode 100644
index 00000000..618775cf
--- /dev/null
+++ b/example/lib/presentation/quill/samples/quill_default_sample.dart
@@ -0,0 +1,295 @@
+import '../../../gen/assets.gen.dart';
+
+final quillDefaultSample = [
+ {
+ 'insert': {'image': Assets.images.screenshot1.path},
+ 'attributes': {
+ 'width': '100',
+ 'height': '100',
+ 'style': 'width:500px; height:350px;'
+ }
+ },
+ {'insert': 'Flutter Quill'},
+ {
+ 'attributes': {'header': 1},
+ 'insert': '\n'
+ },
+ {
+ 'insert': {
+ 'video':
+ 'https://www.youtube.com/watch?v=V4hgdKhIqtc&list=PLbhaS_83B97s78HsDTtplRTEhcFsqSqIK&index=1'
+ }
+ },
+ {
+ 'insert': {
+ 'video':
+ 'https://user-images.githubusercontent.com/122956/126238875-22e42501-ad41-4266-b1d6-3f89b5e3b79b.mp4'
+ }
+ },
+ {'insert': '\nRich text editor for Flutter'},
+ {
+ 'attributes': {'header': 2},
+ 'insert': '\n'
+ },
+ {'insert': 'Quill component for Flutter'},
+ {
+ 'attributes': {'header': 3},
+ 'insert': '\n'
+ },
+ {
+ 'attributes': {'link': 'https://bulletjournal.us/home/index.html'},
+ 'insert': 'Bullet Journal'
+ },
+ {
+ 'insert':
+ ':\nTrack personal and group journals (ToDo, Note, Ledger) from multiple views with timely reminders'
+ },
+ {
+ 'attributes': {'list': 'ordered'},
+ 'insert': '\n'
+ },
+ {
+ 'insert':
+ 'Share your tasks and notes with teammates, and see changes as they happen in real-time, across all devices'
+ },
+ {
+ 'attributes': {'list': 'ordered'},
+ 'insert': '\n'
+ },
+ {'insert': 'Check out what you and your teammates are working on each day'},
+ {
+ 'attributes': {'list': 'ordered'},
+ 'insert': '\n'
+ },
+ {'insert': '\nSplitting bills with friends can never be easier.'},
+ {
+ 'attributes': {'list': 'bullet'},
+ 'insert': '\n'
+ },
+ {'insert': 'Start creating a group and invite your friends to join.'},
+ {
+ 'attributes': {'list': 'bullet'},
+ 'insert': '\n'
+ },
+ {'insert': 'Create a BuJo of Ledger type to see expense or balance summary.'},
+ {
+ 'attributes': {'list': 'bullet'},
+ 'insert': '\n'
+ },
+ {
+ 'insert':
+ '\nAttach one or multiple labels to tasks, notes or transactions. Later you can track them just using the label(s).'
+ },
+ {
+ 'attributes': {'blockquote': true},
+ 'insert': '\n'
+ },
+ {'insert': "\nvar BuJo = 'Bullet' + 'Journal'"},
+ {
+ 'attributes': {'code-block': true},
+ 'insert': '\n'
+ },
+ {'insert': '\nStart tracking in your browser'},
+ {
+ 'attributes': {'indent': 1},
+ 'insert': '\n'
+ },
+ {'insert': 'Stop the timer on your phone'},
+ {
+ 'attributes': {'indent': 1},
+ 'insert': '\n'
+ },
+ {'insert': 'All your time entries are synced'},
+ {
+ 'attributes': {'indent': 2},
+ 'insert': '\n'
+ },
+ {'insert': 'between the phone apps'},
+ {
+ 'attributes': {'indent': 2},
+ 'insert': '\n'
+ },
+ {'insert': 'and the website.'},
+ {
+ 'attributes': {'indent': 3},
+ 'insert': '\n'
+ },
+ {'insert': '\n'},
+ {'insert': '\nCenter Align'},
+ {
+ 'attributes': {'align': 'center'},
+ 'insert': '\n'
+ },
+ {'insert': 'Right Align'},
+ {
+ 'attributes': {'align': 'right'},
+ 'insert': '\n'
+ },
+ {'insert': 'Justify Align'},
+ {
+ 'attributes': {'align': 'justify'},
+ 'insert': '\n'
+ },
+ {'insert': 'Have trouble finding things? '},
+ {
+ 'attributes': {'list': 'ordered'},
+ 'insert': '\n'
+ },
+ {'insert': 'Just type in the search bar'},
+ {
+ 'attributes': {'indent': 1, 'list': 'ordered'},
+ 'insert': '\n'
+ },
+ {'insert': 'and easily find contents'},
+ {
+ 'attributes': {'indent': 2, 'list': 'ordered'},
+ 'insert': '\n'
+ },
+ {'insert': 'across projects or folders.'},
+ {
+ 'attributes': {'indent': 2, 'list': 'ordered'},
+ 'insert': '\n'
+ },
+ {'insert': 'It matches text in your note or task.'},
+ {
+ 'attributes': {'indent': 1, 'list': 'ordered'},
+ 'insert': '\n'
+ },
+ {'insert': 'Enable reminders so that you will get notified by'},
+ {
+ 'attributes': {'list': 'ordered'},
+ 'insert': '\n'
+ },
+ {'insert': 'email'},
+ {
+ 'attributes': {'indent': 1, 'list': 'ordered'},
+ 'insert': '\n'
+ },
+ {'insert': 'message on your phone'},
+ {
+ 'attributes': {'indent': 1, 'list': 'ordered'},
+ 'insert': '\n'
+ },
+ {'insert': 'popup on the web site'},
+ {
+ 'attributes': {'indent': 1, 'list': 'ordered'},
+ 'insert': '\n'
+ },
+ {'insert': 'Create a BuJo serving as project or folder'},
+ {
+ 'attributes': {'list': 'bullet'},
+ 'insert': '\n'
+ },
+ {'insert': 'Organize your'},
+ {
+ 'attributes': {'indent': 1, 'list': 'bullet'},
+ 'insert': '\n'
+ },
+ {'insert': 'tasks'},
+ {
+ 'attributes': {'indent': 2, 'list': 'bullet'},
+ 'insert': '\n'
+ },
+ {'insert': 'notes'},
+ {
+ 'attributes': {'indent': 2, 'list': 'bullet'},
+ 'insert': '\n'
+ },
+ {'insert': 'transactions'},
+ {
+ 'attributes': {'indent': 2, 'list': 'bullet'},
+ 'insert': '\n'
+ },
+ {'insert': 'under BuJo '},
+ {
+ 'attributes': {'indent': 3, 'list': 'bullet'},
+ 'insert': '\n'
+ },
+ {'insert': 'See them in Calendar'},
+ {
+ 'attributes': {'list': 'bullet'},
+ 'insert': '\n'
+ },
+ {'insert': 'or hierarchical view'},
+ {
+ 'attributes': {'indent': 1, 'list': 'bullet'},
+ 'insert': '\n'
+ },
+ {'insert': 'this is a check list'},
+ {
+ 'attributes': {'list': 'checked'},
+ 'insert': '\n'
+ },
+ {'insert': 'this is a uncheck list'},
+ {
+ 'attributes': {'list': 'unchecked'},
+ 'insert': '\n'
+ },
+ {'insert': 'Font '},
+ {
+ 'attributes': {'font': 'sans-serif'},
+ 'insert': 'Sans Serif'
+ },
+ {'insert': ' '},
+ {
+ 'attributes': {'font': 'serif'},
+ 'insert': 'Serif'
+ },
+ {'insert': ' '},
+ {
+ 'attributes': {'font': 'monospace'},
+ 'insert': 'Monospace'
+ },
+ {'insert': ' Size '},
+ {
+ 'attributes': {'size': 'small'},
+ 'insert': 'Small'
+ },
+ {'insert': ' '},
+ {
+ 'attributes': {'size': 'large'},
+ 'insert': 'Large'
+ },
+ {'insert': ' '},
+ {
+ 'attributes': {'size': 'huge'},
+ 'insert': 'Huge'
+ },
+ {
+ 'attributes': {'size': '15.0'},
+ 'insert': 'font size 15'
+ },
+ {'insert': ' '},
+ {
+ 'attributes': {'size': '35'},
+ 'insert': 'font size 35'
+ },
+ {'insert': ' '},
+ {
+ 'attributes': {'size': '20'},
+ 'insert': 'font size 20'
+ },
+ {
+ 'attributes': {'token': 'built_in'},
+ 'insert': ' diff'
+ },
+ {
+ 'attributes': {'token': 'operator'},
+ 'insert': '-match'
+ },
+ {
+ 'attributes': {'token': 'literal'},
+ 'insert': '-patch'
+ },
+ {
+ 'insert': {
+ 'image':
+ 'https://user-images.githubusercontent.com/122956/72955931-ccc07900-3d52-11ea-89b1-d468a6e2aa2b.png'
+ },
+ 'attributes': {
+ 'width': '230',
+ 'style': 'display: block; margin: auto; width: 500px;'
+ }
+ },
+ {'insert': '\n'}
+];
diff --git a/example/lib/presentation/quill/samples/quill_images_sample.dart b/example/lib/presentation/quill/samples/quill_images_sample.dart
new file mode 100644
index 00000000..b483e733
--- /dev/null
+++ b/example/lib/presentation/quill/samples/quill_images_sample.dart
@@ -0,0 +1,72 @@
+import '../../../gen/assets.gen.dart';
+
+final quillImagesSample = [
+ {'insert': 'This is an asset image: \n'},
+ {'insert': '\n'},
+ {
+ 'insert': {'image': Assets.images.screenshot1.path},
+ 'attributes': {
+ 'width': '100',
+ 'height': '100',
+ 'style': 'width:500px; height:350px;'
+ }
+ },
+ {'insert': '\n'},
+ {'insert': 'Here is a network image: \n'},
+ {'insert': '\n'},
+ {
+ 'insert': {
+ 'image':
+ 'https://helpx.adobe.com/content/dam/help/en/photoshop/using/convert-color-image-black-white/jcr_content/main-pars/before_and_after/image-before/Landscape-Color.jpg'
+ },
+ 'attributes': {
+ 'width': '100',
+ 'height': '100',
+ 'style': 'width:250px; height:250px;'
+ }
+ },
+ {'insert': '\n'},
+ {'insert': '\n'},
+ {
+ 'insert':
+ '\nThe image above have 250px width and height in the css style attribute which will be used for web, and 100 width and height that is in the attributes which will be used for desktop and mobile\n'
+ },
+ {'insert': '\n'},
+ {
+ 'insert': {
+ 'image':
+ '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'
+ },
+ 'attributes': {
+ 'width': '100',
+ 'height': '100',
+ 'style': 'width:250px; height:250px;'
+ }
+ },
+ {'insert': '\n'},
+ {
+ 'insert':
+ '\nThe source of the above image is image base 64 directly without `data:image/png;base64,` in the start'
+ },
+ {'insert': '\n'},
+ {'insert': '\n'},
+ {'insert': ''},
+ {
+ 'insert': {
+ 'image':
+ 'data:image/png;base64,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'
+ },
+ 'attributes': {
+ 'width': '100',
+ 'height': '100',
+ 'style': 'width:250px; height:250px;'
+ }
+ },
+ {'insert': '\n'},
+ {'insert': '\n'},
+ {
+ 'insert':
+ 'The source of the above image is also image base 64 but this time it start with `data:image/png;base64,`'
+ },
+ {'insert': '\n'},
+];
diff --git a/example/lib/presentation/quill/samples/quill_text_sample.dart b/example/lib/presentation/quill/samples/quill_text_sample.dart
new file mode 100644
index 00000000..4e047db9
--- /dev/null
+++ b/example/lib/presentation/quill/samples/quill_text_sample.dart
@@ -0,0 +1,270 @@
+const quillTextSample = [
+ {'insert': 'Flutter Quill'},
+ {
+ 'attributes': {'header': 1},
+ 'insert': '\n'
+ },
+ {'insert': '\nRich text editor for Flutter'},
+ {
+ 'attributes': {'header': 2},
+ 'insert': '\n'
+ },
+ {'insert': 'Quill component for Flutter'},
+ {
+ 'attributes': {'color': 'rgba(0, 0, 0, 0.847)'},
+ 'insert': ' and '
+ },
+ {
+ 'attributes': {'link': 'https://bulletjournal.us/home/index.html'},
+ 'insert': 'Bullet Journal'
+ },
+ {
+ 'insert':
+ ':\nTrack personal and group journals (ToDo, Note, Ledger) from multiple views with timely reminders'
+ },
+ {
+ 'attributes': {'list': 'ordered'},
+ 'insert': '\n'
+ },
+ {
+ 'insert':
+ 'Share your tasks and notes with teammates, and see changes as they happen in real-time, across all devices'
+ },
+ {
+ 'attributes': {'list': 'ordered'},
+ 'insert': '\n'
+ },
+ {'insert': 'Check out what you and your teammates are working on each day'},
+ {
+ 'attributes': {'list': 'ordered'},
+ 'insert': '\n'
+ },
+ {'insert': '\nSplitting bills with friends can never be easier.'},
+ {
+ 'attributes': {'list': 'bullet'},
+ 'insert': '\n'
+ },
+ {'insert': 'Start creating a group and invite your friends to join.'},
+ {
+ 'attributes': {'list': 'bullet'},
+ 'insert': '\n'
+ },
+ {'insert': 'Create a BuJo of Ledger type to see expense or balance summary.'},
+ {
+ 'attributes': {'list': 'bullet'},
+ 'insert': '\n'
+ },
+ {
+ 'insert':
+ '\nAttach one or multiple labels to tasks, notes or transactions. Later you can track them just using the label(s).'
+ },
+ {
+ 'attributes': {'blockquote': true},
+ 'insert': '\n'
+ },
+ {'insert': "\nvar BuJo = 'Bullet' + 'Journal'"},
+ {
+ 'attributes': {'code-block': true},
+ 'insert': '\n'
+ },
+ {'insert': '\nStart tracking in your browser'},
+ {
+ 'attributes': {'indent': 1},
+ 'insert': '\n'
+ },
+ {'insert': 'Stop the timer on your phone'},
+ {
+ 'attributes': {'indent': 1},
+ 'insert': '\n'
+ },
+ {'insert': 'All your time entries are synced'},
+ {
+ 'attributes': {'indent': 2},
+ 'insert': '\n'
+ },
+ {'insert': 'between the phone apps'},
+ {
+ 'attributes': {'indent': 2},
+ 'insert': '\n'
+ },
+ {'insert': 'and the website.'},
+ {
+ 'attributes': {'indent': 3},
+ 'insert': '\n'
+ },
+ {'insert': '\n'},
+ {'insert': '\nCenter Align'},
+ {
+ 'attributes': {'align': 'center'},
+ 'insert': '\n'
+ },
+ {'insert': 'Right Align'},
+ {
+ 'attributes': {'align': 'right'},
+ 'insert': '\n'
+ },
+ {'insert': 'Justify Align'},
+ {
+ 'attributes': {'align': 'justify'},
+ 'insert': '\n'
+ },
+ {'insert': 'Have trouble finding things? '},
+ {
+ 'attributes': {'list': 'ordered'},
+ 'insert': '\n'
+ },
+ {'insert': 'Just type in the search bar'},
+ {
+ 'attributes': {'indent': 1, 'list': 'ordered'},
+ 'insert': '\n'
+ },
+ {'insert': 'and easily find contents'},
+ {
+ 'attributes': {'indent': 2, 'list': 'ordered'},
+ 'insert': '\n'
+ },
+ {'insert': 'across projects or folders.'},
+ {
+ 'attributes': {'indent': 2, 'list': 'ordered'},
+ 'insert': '\n'
+ },
+ {'insert': 'It matches text in your note or task.'},
+ {
+ 'attributes': {'indent': 1, 'list': 'ordered'},
+ 'insert': '\n'
+ },
+ {'insert': 'Enable reminders so that you will get notified by'},
+ {
+ 'attributes': {'list': 'ordered'},
+ 'insert': '\n'
+ },
+ {'insert': 'email'},
+ {
+ 'attributes': {'indent': 1, 'list': 'ordered'},
+ 'insert': '\n'
+ },
+ {'insert': 'message on your phone'},
+ {
+ 'attributes': {'indent': 1, 'list': 'ordered'},
+ 'insert': '\n'
+ },
+ {'insert': 'popup on the web site'},
+ {
+ 'attributes': {'indent': 1, 'list': 'ordered'},
+ 'insert': '\n'
+ },
+ {'insert': 'Create a BuJo serving as project or folder'},
+ {
+ 'attributes': {'list': 'bullet'},
+ 'insert': '\n'
+ },
+ {'insert': 'Organize your'},
+ {
+ 'attributes': {'indent': 1, 'list': 'bullet'},
+ 'insert': '\n'
+ },
+ {'insert': 'tasks'},
+ {
+ 'attributes': {'indent': 2, 'list': 'bullet'},
+ 'insert': '\n'
+ },
+ {'insert': 'notes'},
+ {
+ 'attributes': {'indent': 2, 'list': 'bullet'},
+ 'insert': '\n'
+ },
+ {'insert': 'transactions'},
+ {
+ 'attributes': {'indent': 2, 'list': 'bullet'},
+ 'insert': '\n'
+ },
+ {'insert': 'under BuJo '},
+ {
+ 'attributes': {'indent': 3, 'list': 'bullet'},
+ 'insert': '\n'
+ },
+ {'insert': 'See them in Calendar'},
+ {
+ 'attributes': {'list': 'bullet'},
+ 'insert': '\n'
+ },
+ {'insert': 'or hierarchical view'},
+ {
+ 'attributes': {'indent': 1, 'list': 'bullet'},
+ 'insert': '\n'
+ },
+ {'insert': 'this is a check list'},
+ {
+ 'attributes': {'list': 'checked'},
+ 'insert': '\n'
+ },
+ {'insert': 'this is a uncheck list'},
+ {
+ 'attributes': {'list': 'unchecked'},
+ 'insert': '\n'
+ },
+ {'insert': 'Font '},
+ {
+ 'attributes': {'font': 'sans-serif'},
+ 'insert': 'Sans Serif'
+ },
+ {'insert': ' '},
+ {
+ 'attributes': {'font': 'serif'},
+ 'insert': 'Serif'
+ },
+ {'insert': ' '},
+ {
+ 'attributes': {'font': 'monospace'},
+ 'insert': 'Monospace'
+ },
+ {'insert': ' Size '},
+ {
+ 'attributes': {'size': 'small'},
+ 'insert': 'Small'
+ },
+ {'insert': ' '},
+ {
+ 'attributes': {'size': 'large'},
+ 'insert': 'Large'
+ },
+ {'insert': ' '},
+ {
+ 'attributes': {'size': 'huge'},
+ 'insert': 'Huge'
+ },
+ {
+ 'attributes': {'size': '15.0'},
+ 'insert': 'font size 15'
+ },
+ {'insert': ' '},
+ {
+ 'attributes': {'size': '35'},
+ 'insert': 'font size 35'
+ },
+ {'insert': ' '},
+ {
+ 'attributes': {'size': '20'},
+ 'insert': 'font size 20'
+ },
+ {
+ 'attributes': {'token': 'built_in'},
+ 'insert': ' diff'
+ },
+ {
+ 'attributes': {'token': 'operator'},
+ 'insert': '-match'
+ },
+ {
+ 'attributes': {'token': 'literal'},
+ 'insert': '-patch'
+ },
+ {
+ 'insert': {
+ 'image':
+ 'https://user-images.githubusercontent.com/122956/72955931-ccc07900-3d52-11ea-89b1-d468a6e2aa2b.png'
+ },
+ 'attributes': {'width': '230', 'style': 'display: block; margin: auto;'}
+ },
+ {'insert': '\n'}
+];
diff --git a/example/lib/presentation/quill/samples/quill_videos_sample.dart b/example/lib/presentation/quill/samples/quill_videos_sample.dart
new file mode 100644
index 00000000..90f0243e
--- /dev/null
+++ b/example/lib/presentation/quill/samples/quill_videos_sample.dart
@@ -0,0 +1,19 @@
+const quillVideosSample = [
+ {'insert': '\n'},
+ {
+ 'insert': {'video': 'https://youtu.be/xz6_AlJkDPA'},
+ 'attributes': {
+ 'width': '300',
+ 'height': '300',
+ 'style': 'width:400px; height:500px;'
+ }
+ },
+ {'insert': '\n'},
+ {'insert': '\n'},
+ {'insert': 'And this is just a youtube video'},
+ {'insert': '\n'},
+ {
+ 'insert': 'This sample is not complete.',
+ },
+ {'insert': '\n'},
+];
diff --git a/example/lib/presentation/settings/cubit/settings_cubit.dart b/example/lib/presentation/settings/cubit/settings_cubit.dart
new file mode 100644
index 00000000..11bd2042
--- /dev/null
+++ b/example/lib/presentation/settings/cubit/settings_cubit.dart
@@ -0,0 +1,26 @@
+import 'package:bloc/bloc.dart';
+import 'package:flutter/material.dart' show ThemeMode;
+import 'package:freezed_annotation/freezed_annotation.dart';
+import 'package:hydrated_bloc/hydrated_bloc.dart' show HydratedMixin;
+
+part 'settings_state.dart';
+part 'settings_cubit.freezed.dart';
+part 'settings_cubit.g.dart';
+
+class SettingsCubit extends Cubit with HydratedMixin {
+ SettingsCubit() : super(const SettingsState());
+
+ void updateSettings(SettingsState newSettingsState) {
+ emit(newSettingsState);
+ }
+
+ @override
+ SettingsState? fromJson(Map json) {
+ return SettingsState.fromJson(json);
+ }
+
+ @override
+ Map? toJson(SettingsState state) {
+ return state.toJson();
+ }
+}
diff --git a/example/lib/presentation/settings/cubit/settings_cubit.freezed.dart b/example/lib/presentation/settings/cubit/settings_cubit.freezed.dart
new file mode 100644
index 00000000..1a0795df
--- /dev/null
+++ b/example/lib/presentation/settings/cubit/settings_cubit.freezed.dart
@@ -0,0 +1,202 @@
+// coverage:ignore-file
+// GENERATED CODE - DO NOT MODIFY BY HAND
+// ignore_for_file: type=lint
+// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
+
+part of 'settings_cubit.dart';
+
+// **************************************************************************
+// FreezedGenerator
+// **************************************************************************
+
+T _$identity(T value) => value;
+
+final _privateConstructorUsedError = UnsupportedError(
+ 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
+
+SettingsState _$SettingsStateFromJson(Map json) {
+ return _SettingsState.fromJson(json);
+}
+
+/// @nodoc
+mixin _$SettingsState {
+ ThemeMode get themeMode => throw _privateConstructorUsedError;
+ DefaultScreen get defaultScreen => throw _privateConstructorUsedError;
+ bool get useCustomQuillToolbar => throw _privateConstructorUsedError;
+
+ Map toJson() => throw _privateConstructorUsedError;
+ @JsonKey(ignore: true)
+ $SettingsStateCopyWith get copyWith =>
+ throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $SettingsStateCopyWith<$Res> {
+ factory $SettingsStateCopyWith(
+ SettingsState value, $Res Function(SettingsState) then) =
+ _$SettingsStateCopyWithImpl<$Res, SettingsState>;
+ @useResult
+ $Res call(
+ {ThemeMode themeMode,
+ DefaultScreen defaultScreen,
+ bool useCustomQuillToolbar});
+}
+
+/// @nodoc
+class _$SettingsStateCopyWithImpl<$Res, $Val extends SettingsState>
+ implements $SettingsStateCopyWith<$Res> {
+ _$SettingsStateCopyWithImpl(this._value, this._then);
+
+ // ignore: unused_field
+ final $Val _value;
+ // ignore: unused_field
+ final $Res Function($Val) _then;
+
+ @pragma('vm:prefer-inline')
+ @override
+ $Res call({
+ Object? themeMode = null,
+ Object? defaultScreen = null,
+ Object? useCustomQuillToolbar = null,
+ }) {
+ return _then(_value.copyWith(
+ themeMode: null == themeMode
+ ? _value.themeMode
+ : themeMode // ignore: cast_nullable_to_non_nullable
+ as ThemeMode,
+ defaultScreen: null == defaultScreen
+ ? _value.defaultScreen
+ : defaultScreen // ignore: cast_nullable_to_non_nullable
+ as DefaultScreen,
+ useCustomQuillToolbar: null == useCustomQuillToolbar
+ ? _value.useCustomQuillToolbar
+ : useCustomQuillToolbar // ignore: cast_nullable_to_non_nullable
+ as bool,
+ ) as $Val);
+ }
+}
+
+/// @nodoc
+abstract class _$$SettingsStateImplCopyWith<$Res>
+ implements $SettingsStateCopyWith<$Res> {
+ factory _$$SettingsStateImplCopyWith(
+ _$SettingsStateImpl value, $Res Function(_$SettingsStateImpl) then) =
+ __$$SettingsStateImplCopyWithImpl<$Res>;
+ @override
+ @useResult
+ $Res call(
+ {ThemeMode themeMode,
+ DefaultScreen defaultScreen,
+ bool useCustomQuillToolbar});
+}
+
+/// @nodoc
+class __$$SettingsStateImplCopyWithImpl<$Res>
+ extends _$SettingsStateCopyWithImpl<$Res, _$SettingsStateImpl>
+ implements _$$SettingsStateImplCopyWith<$Res> {
+ __$$SettingsStateImplCopyWithImpl(
+ _$SettingsStateImpl _value, $Res Function(_$SettingsStateImpl) _then)
+ : super(_value, _then);
+
+ @pragma('vm:prefer-inline')
+ @override
+ $Res call({
+ Object? themeMode = null,
+ Object? defaultScreen = null,
+ Object? useCustomQuillToolbar = null,
+ }) {
+ return _then(_$SettingsStateImpl(
+ themeMode: null == themeMode
+ ? _value.themeMode
+ : themeMode // ignore: cast_nullable_to_non_nullable
+ as ThemeMode,
+ defaultScreen: null == defaultScreen
+ ? _value.defaultScreen
+ : defaultScreen // ignore: cast_nullable_to_non_nullable
+ as DefaultScreen,
+ useCustomQuillToolbar: null == useCustomQuillToolbar
+ ? _value.useCustomQuillToolbar
+ : useCustomQuillToolbar // ignore: cast_nullable_to_non_nullable
+ as bool,
+ ));
+ }
+}
+
+/// @nodoc
+@JsonSerializable()
+class _$SettingsStateImpl implements _SettingsState {
+ const _$SettingsStateImpl(
+ {this.themeMode = ThemeMode.system,
+ this.defaultScreen = DefaultScreen.home,
+ this.useCustomQuillToolbar = false});
+
+ factory _$SettingsStateImpl.fromJson(Map json) =>
+ _$$SettingsStateImplFromJson(json);
+
+ @override
+ @JsonKey()
+ final ThemeMode themeMode;
+ @override
+ @JsonKey()
+ final DefaultScreen defaultScreen;
+ @override
+ @JsonKey()
+ final bool useCustomQuillToolbar;
+
+ @override
+ String toString() {
+ return 'SettingsState(themeMode: $themeMode, defaultScreen: $defaultScreen, useCustomQuillToolbar: $useCustomQuillToolbar)';
+ }
+
+ @override
+ bool operator ==(dynamic other) {
+ return identical(this, other) ||
+ (other.runtimeType == runtimeType &&
+ other is _$SettingsStateImpl &&
+ (identical(other.themeMode, themeMode) ||
+ other.themeMode == themeMode) &&
+ (identical(other.defaultScreen, defaultScreen) ||
+ other.defaultScreen == defaultScreen) &&
+ (identical(other.useCustomQuillToolbar, useCustomQuillToolbar) ||
+ other.useCustomQuillToolbar == useCustomQuillToolbar));
+ }
+
+ @JsonKey(ignore: true)
+ @override
+ int get hashCode =>
+ Object.hash(runtimeType, themeMode, defaultScreen, useCustomQuillToolbar);
+
+ @JsonKey(ignore: true)
+ @override
+ @pragma('vm:prefer-inline')
+ _$$SettingsStateImplCopyWith<_$SettingsStateImpl> get copyWith =>
+ __$$SettingsStateImplCopyWithImpl<_$SettingsStateImpl>(this, _$identity);
+
+ @override
+ Map toJson() {
+ return _$$SettingsStateImplToJson(
+ this,
+ );
+ }
+}
+
+abstract class _SettingsState implements SettingsState {
+ const factory _SettingsState(
+ {final ThemeMode themeMode,
+ final DefaultScreen defaultScreen,
+ final bool useCustomQuillToolbar}) = _$SettingsStateImpl;
+
+ factory _SettingsState.fromJson(Map json) =
+ _$SettingsStateImpl.fromJson;
+
+ @override
+ ThemeMode get themeMode;
+ @override
+ DefaultScreen get defaultScreen;
+ @override
+ bool get useCustomQuillToolbar;
+ @override
+ @JsonKey(ignore: true)
+ _$$SettingsStateImplCopyWith<_$SettingsStateImpl> get copyWith =>
+ throw _privateConstructorUsedError;
+}
diff --git a/example/lib/presentation/settings/cubit/settings_cubit.g.dart b/example/lib/presentation/settings/cubit/settings_cubit.g.dart
new file mode 100644
index 00000000..fe3fd931
--- /dev/null
+++ b/example/lib/presentation/settings/cubit/settings_cubit.g.dart
@@ -0,0 +1,40 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'settings_cubit.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+_$SettingsStateImpl _$$SettingsStateImplFromJson(Map json) =>
+ _$SettingsStateImpl(
+ themeMode: $enumDecodeNullable(_$ThemeModeEnumMap, json['themeMode']) ??
+ ThemeMode.system,
+ defaultScreen:
+ $enumDecodeNullable(_$DefaultScreenEnumMap, json['defaultScreen']) ??
+ DefaultScreen.home,
+ useCustomQuillToolbar: json['useCustomQuillToolbar'] as bool? ?? false,
+ );
+
+Map _$$SettingsStateImplToJson(_$SettingsStateImpl instance) =>
+ {
+ 'themeMode': _$ThemeModeEnumMap[instance.themeMode]!,
+ 'defaultScreen': _$DefaultScreenEnumMap[instance.defaultScreen]!,
+ 'useCustomQuillToolbar': instance.useCustomQuillToolbar,
+ };
+
+const _$ThemeModeEnumMap = {
+ ThemeMode.system: 'system',
+ ThemeMode.light: 'light',
+ ThemeMode.dark: 'dark',
+};
+
+const _$DefaultScreenEnumMap = {
+ DefaultScreen.home: 'home',
+ DefaultScreen.settings: 'settings',
+ DefaultScreen.defaultSample: 'defaultSample',
+ DefaultScreen.imagesSample: 'imagesSample',
+ DefaultScreen.videosSample: 'videosSample',
+ DefaultScreen.textSample: 'textSample',
+ DefaultScreen.emptySample: 'emptySample',
+};
diff --git a/example/lib/presentation/settings/cubit/settings_state.dart b/example/lib/presentation/settings/cubit/settings_state.dart
new file mode 100644
index 00000000..67e5adae
--- /dev/null
+++ b/example/lib/presentation/settings/cubit/settings_state.dart
@@ -0,0 +1,22 @@
+part of 'settings_cubit.dart';
+
+enum DefaultScreen {
+ home,
+ settings,
+ defaultSample,
+ imagesSample,
+ videosSample,
+ textSample,
+ emptySample,
+}
+
+@freezed
+class SettingsState with _$SettingsState {
+ const factory SettingsState({
+ @Default(ThemeMode.system) ThemeMode themeMode,
+ @Default(DefaultScreen.home) DefaultScreen defaultScreen,
+ @Default(false) bool useCustomQuillToolbar,
+ }) = _SettingsState;
+ factory SettingsState.fromJson(Map json) =>
+ _$SettingsStateFromJson(json);
+}
diff --git a/example/lib/presentation/settings/widgets/settings_screen.dart b/example/lib/presentation/settings/widgets/settings_screen.dart
new file mode 100644
index 00000000..49ffa456
--- /dev/null
+++ b/example/lib/presentation/settings/widgets/settings_screen.dart
@@ -0,0 +1,122 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import '../../shared/widgets/dialog_action.dart';
+import '../../shared/widgets/home_screen_button.dart';
+import '../cubit/settings_cubit.dart';
+
+class SettingsScreen extends StatelessWidget {
+ const SettingsScreen({super.key});
+
+ static const routeName = '/settings';
+
+ @override
+ Widget build(BuildContext context) {
+ final materialTheme = Theme.of(context);
+ final isDark = materialTheme.brightness == Brightness.dark;
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text('Settings'),
+ actions: const [
+ HomeScreenButton(),
+ ],
+ ),
+ body: BlocBuilder(
+ builder: (context, state) {
+ return ListView(
+ children: [
+ CheckboxListTile.adaptive(
+ value: isDark,
+ onChanged: (value) {
+ final isNewValueDark = value ?? false;
+ context.read().updateSettings(
+ state.copyWith(
+ themeMode:
+ isNewValueDark ? ThemeMode.dark : ThemeMode.light,
+ ),
+ );
+ },
+ title: const Text('Dark Theme'),
+ subtitle: const Text(
+ 'By default we will use your system theme, but you can set if you want dark or light theme',
+ ),
+ secondary: Icon(isDark ? Icons.nightlight : Icons.sunny),
+ ),
+ ListTile(
+ title: const Text('Default screen'),
+ subtitle: const Text(
+ 'Which screen should be used when the flutter app starts?',
+ ),
+ leading: const Icon(Icons.home),
+ onTap: () async {
+ final settingsBloc = context.read();
+ final newDefaultScreen =
+ await showAdaptiveDialog(
+ context: context,
+ builder: (context) {
+ return AlertDialog.adaptive(
+ title: const Text('Select default screen'),
+ content: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ ...DefaultScreen.values.map(
+ (e) => Material(
+ child: ListTile(
+ onTap: () {
+ Navigator.of(context).pop(e);
+ },
+ title: Text(e.name),
+ leading: CircleAvatar(
+ child: Text((e.index + 1).toString()),
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ actions: [
+ AppDialogAction(
+ onPressed: () => Navigator.of(context).pop(null),
+ options: const DialogActionOptions(
+ cupertinoDialogActionOptions:
+ CupertinoDialogActionOptions(
+ isDefaultAction: true,
+ ),
+ ),
+ child: const Text('Cancel'),
+ ),
+ ],
+ );
+ },
+ );
+ if (newDefaultScreen != null) {
+ settingsBloc.updateSettings(
+ settingsBloc.state
+ .copyWith(defaultScreen: newDefaultScreen),
+ );
+ }
+ },
+ ),
+ CheckboxListTile.adaptive(
+ value: state.useCustomQuillToolbar,
+ onChanged: (value) {
+ final useCustomToolbarNewValue = value ?? false;
+ context.read().updateSettings(
+ state.copyWith(
+ useCustomQuillToolbar: useCustomToolbarNewValue,
+ ),
+ );
+ },
+ title: const Text('Use custom Quill toolbar'),
+ subtitle: const Text(
+ 'By default we will default QuillToolbar, but you can decide if you the built-in or the custom one',
+ ),
+ secondary: const Icon(Icons.dashboard_customize),
+ ),
+ ],
+ );
+ },
+ ),
+ );
+ }
+}
diff --git a/example/lib/presentation/shared/widgets/dialog_action.dart b/example/lib/presentation/shared/widgets/dialog_action.dart
new file mode 100644
index 00000000..0761e066
--- /dev/null
+++ b/example/lib/presentation/shared/widgets/dialog_action.dart
@@ -0,0 +1,63 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_quill/extensions.dart';
+
+@immutable
+final class CupertinoDialogActionOptions {
+ const CupertinoDialogActionOptions({
+ this.isDefaultAction = false,
+ });
+
+ final bool isDefaultAction;
+}
+
+@immutable
+final class MaterialDialogActionOptions {
+ const MaterialDialogActionOptions({
+ this.textStyle,
+ });
+
+ final ButtonStyle? textStyle;
+}
+
+@immutable
+class DialogActionOptions {
+ const DialogActionOptions({
+ this.cupertinoDialogActionOptions,
+ this.materialDialogActionOptions,
+ });
+
+ final CupertinoDialogActionOptions? cupertinoDialogActionOptions;
+ final MaterialDialogActionOptions? materialDialogActionOptions;
+}
+
+class AppDialogAction extends StatelessWidget {
+ const AppDialogAction({
+ required this.child,
+ required this.onPressed,
+ this.options,
+ super.key,
+ });
+
+ final VoidCallback? onPressed;
+ final Widget child;
+
+ final DialogActionOptions? options;
+
+ @override
+ Widget build(BuildContext context) {
+ if (isAppleOS(supportWeb: true)) {
+ return CupertinoDialogAction(
+ onPressed: onPressed,
+ isDefaultAction:
+ options?.cupertinoDialogActionOptions?.isDefaultAction ?? false,
+ child: child,
+ );
+ }
+ return TextButton(
+ onPressed: onPressed,
+ style: options?.materialDialogActionOptions?.textStyle,
+ child: child,
+ );
+ }
+}
diff --git a/example/lib/presentation/shared/widgets/home_screen_button.dart b/example/lib/presentation/shared/widgets/home_screen_button.dart
new file mode 100644
index 00000000..b1ac053a
--- /dev/null
+++ b/example/lib/presentation/shared/widgets/home_screen_button.dart
@@ -0,0 +1,24 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import '../../settings/cubit/settings_cubit.dart';
+
+class HomeScreenButton extends StatelessWidget {
+ const HomeScreenButton({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return IconButton(
+ onPressed: () {
+ final settingsCubit = context.read();
+ settingsCubit.updateSettings(
+ settingsCubit.state.copyWith(
+ defaultScreen: DefaultScreen.home,
+ ),
+ );
+ },
+ icon: const Icon(Icons.home),
+ tooltip: 'Set the default to home screen',
+ );
+ }
+}
diff --git a/example/lib/universal_ui/fake_ui.dart b/example/lib/universal_ui/fake_ui.dart
deleted file mode 100644
index 1711ad5f..00000000
--- a/example/lib/universal_ui/fake_ui.dart
+++ /dev/null
@@ -1,3 +0,0 @@
-class PlatformViewRegistry {
- static void registerViewFactory(String viewId, dynamic cb) {}
-}
diff --git a/example/lib/universal_ui/real_ui.dart b/example/lib/universal_ui/real_ui.dart
deleted file mode 100644
index 6c1072fc..00000000
--- a/example/lib/universal_ui/real_ui.dart
+++ /dev/null
@@ -1,7 +0,0 @@
-import 'dart:ui' if (dart.library.html) 'dart:ui_web' as ui;
-
-class PlatformViewRegistry {
- static void registerViewFactory(String viewId, dynamic cb) {
- ui.platformViewRegistry.registerViewFactory(viewId, cb);
- }
-}
diff --git a/example/lib/universal_ui/universal_ui.dart b/example/lib/universal_ui/universal_ui.dart
deleted file mode 100644
index 421725e2..00000000
--- a/example/lib/universal_ui/universal_ui.dart
+++ /dev/null
@@ -1,114 +0,0 @@
-library universal_ui;
-
-import 'package:flutter/cupertino.dart';
-import 'package:flutter/foundation.dart';
-import 'package:flutter_quill/flutter_quill.dart';
-import 'package:flutter_quill_extensions/flutter_quill_extensions.dart';
-import 'package:universal_html/html.dart' as html;
-import 'package:youtube_player_flutter/youtube_player_flutter.dart';
-
-import '../widgets/responsive_widget.dart';
-import 'fake_ui.dart' if (dart.library.html) 'real_ui.dart' as ui_instance;
-
-class PlatformViewRegistryFix {
- void registerViewFactory(dynamic x, dynamic y) {
- if (kIsWeb) {
- ui_instance.PlatformViewRegistry.registerViewFactory(
- x,
- y,
- );
- }
- }
-}
-
-class UniversalUI {
- PlatformViewRegistryFix platformViewRegistry = PlatformViewRegistryFix();
-}
-
-var ui = UniversalUI();
-
-class ImageEmbedBuilderWeb extends EmbedBuilder {
- @override
- String get key => BlockEmbed.imageType;
-
- @override
- Widget build(
- BuildContext context,
- QuillController controller,
- Embed node,
- bool readOnly,
- bool inline,
- TextStyle textStyle,
- ) {
- final imageUrl = node.value.data;
- if (isImageBase64(imageUrl)) {
- // TODO: handle imageUrl of base64
- return const SizedBox();
- }
- final size = MediaQuery.sizeOf(context);
- UniversalUI().platformViewRegistry.registerViewFactory(imageUrl, (viewId) {
- return html.ImageElement()
- ..src = imageUrl
- ..style.height = 'auto'
- ..style.width = 'auto';
- });
- return Padding(
- padding: EdgeInsets.only(
- right: ResponsiveWidget.isMediumScreen(context)
- ? size.width * 0.5
- : (ResponsiveWidget.isLargeScreen(context))
- ? size.width * 0.75
- : size.width * 0.2,
- ),
- child: SizedBox(
- height: MediaQuery.sizeOf(context).height * 0.45,
- child: HtmlElementView(
- viewType: imageUrl,
- ),
- ),
- );
- }
-}
-
-class VideoEmbedBuilderWeb extends EmbedBuilder {
- @override
- String get key => BlockEmbed.videoType;
-
- @override
- Widget build(
- BuildContext context,
- QuillController controller,
- Embed node,
- bool readOnly,
- bool inline,
- TextStyle textStyle,
- ) {
- var videoUrl = node.value.data;
- if (videoUrl.contains('youtube.com') || videoUrl.contains('youtu.be')) {
- final youtubeID = YoutubePlayer.convertUrlToId(videoUrl);
- if (youtubeID != null) {
- videoUrl = 'https://www.youtube.com/embed/$youtubeID';
- }
- }
-
- UniversalUI().platformViewRegistry.registerViewFactory(
- videoUrl,
- (id) => html.IFrameElement()
- ..width = MediaQuery.sizeOf(context).width.toString()
- ..height = MediaQuery.sizeOf(context).height.toString()
- ..src = videoUrl
- ..style.border = 'none');
-
- return SizedBox(
- height: 500,
- child: HtmlElementView(
- viewType: videoUrl,
- ),
- );
- }
-}
-
-List get defaultEmbedBuildersWeb => [
- ImageEmbedBuilderWeb(),
- VideoEmbedBuilderWeb(),
- ];
diff --git a/example/lib/widgets/demo_scaffold.dart b/example/lib/widgets/demo_scaffold.dart
deleted file mode 100644
index fb96b01c..00000000
--- a/example/lib/widgets/demo_scaffold.dart
+++ /dev/null
@@ -1,140 +0,0 @@
-import 'dart:convert';
-import 'dart:io' show Platform;
-
-import 'package:filesystem_picker/filesystem_picker.dart';
-import 'package:flutter/foundation.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter/services.dart';
-import 'package:flutter_quill/flutter_quill.dart';
-import 'package:flutter_quill_extensions/flutter_quill_extensions.dart';
-import 'package:path_provider/path_provider.dart';
-
-typedef DemoContentBuilder = Widget Function(
- BuildContext context, QuillController? controller);
-
-// Common scaffold for all examples.
-class DemoScaffold extends StatefulWidget {
- const DemoScaffold({
- required this.documentFilename,
- required this.builder,
- this.actions,
- this.showToolbar = true,
- this.floatingActionButton,
- Key? key,
- }) : super(key: key);
-
- /// Filename of the document to load into the editor.
- final String documentFilename;
- final DemoContentBuilder builder;
- final List? actions;
- final Widget? floatingActionButton;
- final bool showToolbar;
-
- @override
- _DemoScaffoldState createState() => _DemoScaffoldState();
-}
-
-class _DemoScaffoldState extends State {
- final _scaffoldKey = GlobalKey();
- QuillController? _controller;
-
- bool _loading = false;
-
- @override
- void didChangeDependencies() {
- super.didChangeDependencies();
- if (_controller == null && !_loading) {
- _loading = true;
- _loadFromAssets();
- }
- }
-
- @override
- void dispose() {
- _controller?.dispose();
- super.dispose();
- }
-
- Future _loadFromAssets() async {
- try {
- final result =
- await rootBundle.loadString('assets/${widget.documentFilename}');
- final doc = Document.fromJson(jsonDecode(result));
- setState(() {
- _controller = QuillController(
- document: doc, selection: const TextSelection.collapsed(offset: 0));
- _loading = false;
- });
- } catch (error) {
- final doc = Document()..insert(0, 'Empty asset');
- setState(() {
- _controller = QuillController(
- document: doc, selection: const TextSelection.collapsed(offset: 0));
- _loading = false;
- });
- }
- }
-
- Future openFileSystemPickerForDesktop(BuildContext context) async {
- return await FilesystemPicker.open(
- context: context,
- rootDirectory: await getApplicationDocumentsDirectory(),
- fsType: FilesystemType.file,
- fileTileSelectMode: FileTileSelectMode.wholeTile,
- );
- }
-
- QuillToolbar get quillToolbar {
- if (_isDesktop()) {
- return QuillToolbar(
- configurations: QuillToolbarConfigurations(
- embedButtons: FlutterQuillEmbeds.buttons(
- filePickImpl: openFileSystemPickerForDesktop,
- ),
- ),
- );
- }
- return QuillToolbar(
- configurations: QuillToolbarConfigurations(
- embedButtons: FlutterQuillEmbeds.buttons(),
- ),
- );
- }
-
- @override
- Widget build(BuildContext context) {
- if (_controller == null) {
- return const Scaffold(body: Center(child: Text('Loading...')));
- }
- final actions = widget.actions ?? [];
-
- return QuillProvider(
- configurations: QuillConfigurations(controller: _controller!),
- child: Scaffold(
- key: _scaffoldKey,
- appBar: AppBar(
- elevation: 0,
- backgroundColor: Theme.of(context).canvasColor,
- centerTitle: false,
- titleSpacing: 0,
- leading: IconButton(
- icon: Icon(
- Icons.chevron_left,
- color: Colors.grey.shade800,
- size: 18,
- ),
- onPressed: () => Navigator.pop(context),
- ),
- title: _loading || !widget.showToolbar ? null : quillToolbar,
- actions: actions,
- ),
- floatingActionButton: widget.floatingActionButton,
- body: _loading
- ? const Center(child: Text('Loading...'))
- : widget.builder(context, _controller),
- ),
- );
- }
-
- bool _isDesktop() => !kIsWeb && !Platform.isAndroid && !Platform.isIOS;
-}
diff --git a/example/lib/widgets/responsive_widget.dart b/example/lib/widgets/responsive_widget.dart
deleted file mode 100644
index f9de4027..00000000
--- a/example/lib/widgets/responsive_widget.dart
+++ /dev/null
@@ -1,43 +0,0 @@
-import 'package:flutter/material.dart';
-
-class ResponsiveWidget extends StatelessWidget {
- const ResponsiveWidget({
- required this.largeScreen,
- this.mediumScreen,
- this.smallScreen,
- Key? key,
- }) : super(key: key);
-
- final Widget largeScreen;
- final Widget? mediumScreen;
- final Widget? smallScreen;
-
- static bool isSmallScreen(BuildContext context) {
- return MediaQuery.sizeOf(context).width < 800;
- }
-
- static bool isLargeScreen(BuildContext context) {
- return MediaQuery.sizeOf(context).width > 1200;
- }
-
- static bool isMediumScreen(BuildContext context) {
- return MediaQuery.sizeOf(context).width >= 800 &&
- MediaQuery.sizeOf(context).width <= 1200;
- }
-
- @override
- Widget build(BuildContext context) {
- return LayoutBuilder(
- builder: (context, constraints) {
- if (constraints.maxWidth > 1200) {
- return largeScreen;
- } else if (constraints.maxWidth <= 1200 &&
- constraints.maxWidth >= 800) {
- return mediumScreen ?? largeScreen;
- } else {
- return smallScreen ?? largeScreen;
- }
- },
- );
- }
-}
diff --git a/example/linux/.gitignore b/example/linux/.gitignore
index c7ea17fc..d3896c98 100644
--- a/example/linux/.gitignore
+++ b/example/linux/.gitignore
@@ -1 +1 @@
-flutter/ephemeral
+flutter/ephemeral
diff --git a/example/linux/CMakeLists.txt b/example/linux/CMakeLists.txt
index 6ec85464..d67bd4e0 100644
--- a/example/linux/CMakeLists.txt
+++ b/example/linux/CMakeLists.txt
@@ -1,106 +1,139 @@
-cmake_minimum_required(VERSION 3.10)
-project(runner LANGUAGES CXX)
-
-set(BINARY_NAME "app")
-set(APPLICATION_ID "com.example.app")
-
-cmake_policy(SET CMP0063 NEW)
-
-set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
-
-# Configure build options.
-if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
- set(CMAKE_BUILD_TYPE "Debug" CACHE
- STRING "Flutter build mode" FORCE)
- set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
- "Debug" "Profile" "Release")
-endif()
-
-# Compilation settings that should be applied to most targets.
-function(APPLY_STANDARD_SETTINGS TARGET)
- target_compile_features(${TARGET} PUBLIC cxx_std_14)
- target_compile_options(${TARGET} PRIVATE -Wall -Werror)
- target_compile_options(${TARGET} PRIVATE "$<$>:-O3>")
- target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>")
-endfunction()
-
-set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
-
-# Flutter library and tool build rules.
-add_subdirectory(${FLUTTER_MANAGED_DIR})
-
-# System-level dependencies.
-find_package(PkgConfig REQUIRED)
-pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
-
-add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
-
-# Application build
-add_executable(${BINARY_NAME}
- "main.cc"
- "my_application.cc"
- "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
-)
-apply_standard_settings(${BINARY_NAME})
-target_link_libraries(${BINARY_NAME} PRIVATE flutter)
-target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
-add_dependencies(${BINARY_NAME} flutter_assemble)
-# Only the install-generated bundle's copy of the executable will launch
-# correctly, since the resources must in the right relative locations. To avoid
-# people trying to run the unbundled copy, put it in a subdirectory instead of
-# the default top-level location.
-set_target_properties(${BINARY_NAME}
- PROPERTIES
- RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
-)
-
-# Generated plugin build rules, which manage building the plugins and adding
-# them to the application.
-include(flutter/generated_plugins.cmake)
-
-
-# === Installation ===
-# By default, "installing" just makes a relocatable bundle in the build
-# directory.
-set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
-if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
- set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
-endif()
-
-# Start with a clean build bundle directory every time.
-install(CODE "
- file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
- " COMPONENT Runtime)
-
-set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
-set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
-
-install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
- COMPONENT Runtime)
-
-install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
- COMPONENT Runtime)
-
-install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
- COMPONENT Runtime)
-
-if(PLUGIN_BUNDLED_LIBRARIES)
- install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
- DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
- COMPONENT Runtime)
-endif()
-
-# Fully re-copy the assets directory on each build to avoid having stale files
-# from a previous install.
-set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
-install(CODE "
- file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
- " COMPONENT Runtime)
-install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
- DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
-
-# Install the AOT library on non-Debug builds only.
-if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
- install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
- COMPONENT Runtime)
-endif()
+# Project-level configuration.
+cmake_minimum_required(VERSION 3.10)
+project(runner LANGUAGES CXX)
+
+# The name of the executable created for the application. Change this to change
+# the on-disk name of your application.
+set(BINARY_NAME "example")
+# The unique GTK application identifier for this application. See:
+# https://wiki.gnome.org/HowDoI/ChooseApplicationID
+set(APPLICATION_ID "com.example.example")
+
+# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
+# versions of CMake.
+cmake_policy(SET CMP0063 NEW)
+
+# Load bundled libraries from the lib/ directory relative to the binary.
+set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
+
+# Root filesystem for cross-building.
+if(FLUTTER_TARGET_PLATFORM_SYSROOT)
+ set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
+ set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
+ set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
+ set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+ set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+endif()
+
+# Define build configuration options.
+if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+ set(CMAKE_BUILD_TYPE "Debug" CACHE
+ STRING "Flutter build mode" FORCE)
+ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
+ "Debug" "Profile" "Release")
+endif()
+
+# Compilation settings that should be applied to most targets.
+#
+# Be cautious about adding new options here, as plugins use this function by
+# default. In most cases, you should add new options to specific targets instead
+# of modifying this function.
+function(APPLY_STANDARD_SETTINGS TARGET)
+ target_compile_features(${TARGET} PUBLIC cxx_std_14)
+ target_compile_options(${TARGET} PRIVATE -Wall -Werror)
+ target_compile_options(${TARGET} PRIVATE "$<$>:-O3>")
+ target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>")
+endfunction()
+
+# Flutter library and tool build rules.
+set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
+add_subdirectory(${FLUTTER_MANAGED_DIR})
+
+# System-level dependencies.
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
+
+add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
+
+# Define the application target. To change its name, change BINARY_NAME above,
+# not the value here, or `flutter run` will no longer work.
+#
+# Any new source files that you add to the application should be added here.
+add_executable(${BINARY_NAME}
+ "main.cc"
+ "my_application.cc"
+ "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
+)
+
+# Apply the standard set of build settings. This can be removed for applications
+# that need different build settings.
+apply_standard_settings(${BINARY_NAME})
+
+# Add dependency libraries. Add any application-specific dependencies here.
+target_link_libraries(${BINARY_NAME} PRIVATE flutter)
+target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
+
+# Run the Flutter tool portions of the build. This must not be removed.
+add_dependencies(${BINARY_NAME} flutter_assemble)
+
+# Only the install-generated bundle's copy of the executable will launch
+# correctly, since the resources must in the right relative locations. To avoid
+# people trying to run the unbundled copy, put it in a subdirectory instead of
+# the default top-level location.
+set_target_properties(${BINARY_NAME}
+ PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
+)
+
+
+# Generated plugin build rules, which manage building the plugins and adding
+# them to the application.
+include(flutter/generated_plugins.cmake)
+
+
+# === Installation ===
+# By default, "installing" just makes a relocatable bundle in the build
+# directory.
+set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
+if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+ set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
+endif()
+
+# Start with a clean build bundle directory every time.
+install(CODE "
+ file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
+ " COMPONENT Runtime)
+
+set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
+set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
+
+install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+
+foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
+ install(FILES "${bundled_library}"
+ DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endforeach(bundled_library)
+
+# Fully re-copy the assets directory on each build to avoid having stale files
+# from a previous install.
+set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
+install(CODE "
+ file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
+ " COMPONENT Runtime)
+install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
+ DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
+
+# Install the AOT library on non-Debug builds only.
+if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
+ install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endif()
diff --git a/example/linux/flutter/CMakeLists.txt b/example/linux/flutter/CMakeLists.txt
index 40425dce..d5bd0164 100644
--- a/example/linux/flutter/CMakeLists.txt
+++ b/example/linux/flutter/CMakeLists.txt
@@ -1,91 +1,88 @@
-cmake_minimum_required(VERSION 3.10)
-
-set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
-
-# Configuration provided via flutter tool.
-include(${EPHEMERAL_DIR}/generated_config.cmake)
-
-# TODO: Move the rest of this into files in ephemeral. See
-# https://github.com/flutter/flutter/issues/57146.
-
-# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
-# which isn't available in 3.10.
-function(list_prepend LIST_NAME PREFIX)
- set(NEW_LIST "")
- foreach(element ${${LIST_NAME}})
- list(APPEND NEW_LIST "${PREFIX}${element}")
- endforeach(element)
- set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
-endfunction()
-
-# === Flutter Library ===
-# System-level dependencies.
-find_package(PkgConfig REQUIRED)
-pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
-pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
-pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
-pkg_check_modules(BLKID REQUIRED IMPORTED_TARGET blkid)
-pkg_check_modules(LZMA REQUIRED IMPORTED_TARGET liblzma)
-
-set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
-
-# Published to parent scope for install step.
-set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
-set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
-set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
-set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
-
-list(APPEND FLUTTER_LIBRARY_HEADERS
- "fl_basic_message_channel.h"
- "fl_binary_codec.h"
- "fl_binary_messenger.h"
- "fl_dart_project.h"
- "fl_engine.h"
- "fl_json_message_codec.h"
- "fl_json_method_codec.h"
- "fl_message_codec.h"
- "fl_method_call.h"
- "fl_method_channel.h"
- "fl_method_codec.h"
- "fl_method_response.h"
- "fl_plugin_registrar.h"
- "fl_plugin_registry.h"
- "fl_standard_message_codec.h"
- "fl_standard_method_codec.h"
- "fl_string_codec.h"
- "fl_value.h"
- "fl_view.h"
- "flutter_linux.h"
-)
-list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
-add_library(flutter INTERFACE)
-target_include_directories(flutter INTERFACE
- "${EPHEMERAL_DIR}"
-)
-target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
-target_link_libraries(flutter INTERFACE
- PkgConfig::GTK
- PkgConfig::GLIB
- PkgConfig::GIO
- PkgConfig::BLKID
- PkgConfig::LZMA
-)
-add_dependencies(flutter flutter_assemble)
-
-# === Flutter tool backend ===
-# _phony_ is a non-existent file to force this command to run every time,
-# since currently there's no way to get a full input/output list from the
-# flutter tool.
-add_custom_command(
- OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
- ${CMAKE_CURRENT_BINARY_DIR}/_phony_
- COMMAND ${CMAKE_COMMAND} -E env
- ${FLUTTER_TOOL_ENVIRONMENT}
- "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
- ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}
- VERBATIM
-)
-add_custom_target(flutter_assemble DEPENDS
- "${FLUTTER_LIBRARY}"
- ${FLUTTER_LIBRARY_HEADERS}
-)
+# This file controls Flutter-level build steps. It should not be edited.
+cmake_minimum_required(VERSION 3.10)
+
+set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
+
+# Configuration provided via flutter tool.
+include(${EPHEMERAL_DIR}/generated_config.cmake)
+
+# TODO: Move the rest of this into files in ephemeral. See
+# https://github.com/flutter/flutter/issues/57146.
+
+# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
+# which isn't available in 3.10.
+function(list_prepend LIST_NAME PREFIX)
+ set(NEW_LIST "")
+ foreach(element ${${LIST_NAME}})
+ list(APPEND NEW_LIST "${PREFIX}${element}")
+ endforeach(element)
+ set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
+endfunction()
+
+# === Flutter Library ===
+# System-level dependencies.
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
+pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
+pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
+
+set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
+
+# Published to parent scope for install step.
+set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
+set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
+set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
+set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
+
+list(APPEND FLUTTER_LIBRARY_HEADERS
+ "fl_basic_message_channel.h"
+ "fl_binary_codec.h"
+ "fl_binary_messenger.h"
+ "fl_dart_project.h"
+ "fl_engine.h"
+ "fl_json_message_codec.h"
+ "fl_json_method_codec.h"
+ "fl_message_codec.h"
+ "fl_method_call.h"
+ "fl_method_channel.h"
+ "fl_method_codec.h"
+ "fl_method_response.h"
+ "fl_plugin_registrar.h"
+ "fl_plugin_registry.h"
+ "fl_standard_message_codec.h"
+ "fl_standard_method_codec.h"
+ "fl_string_codec.h"
+ "fl_value.h"
+ "fl_view.h"
+ "flutter_linux.h"
+)
+list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
+add_library(flutter INTERFACE)
+target_include_directories(flutter INTERFACE
+ "${EPHEMERAL_DIR}"
+)
+target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
+target_link_libraries(flutter INTERFACE
+ PkgConfig::GTK
+ PkgConfig::GLIB
+ PkgConfig::GIO
+)
+add_dependencies(flutter flutter_assemble)
+
+# === Flutter tool backend ===
+# _phony_ is a non-existent file to force this command to run every time,
+# since currently there's no way to get a full input/output list from the
+# flutter tool.
+add_custom_command(
+ OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
+ ${CMAKE_CURRENT_BINARY_DIR}/_phony_
+ COMMAND ${CMAKE_COMMAND} -E env
+ ${FLUTTER_TOOL_ENVIRONMENT}
+ "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
+ ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}
+ VERBATIM
+)
+add_custom_target(flutter_assemble DEPENDS
+ "${FLUTTER_LIBRARY}"
+ ${FLUTTER_LIBRARY_HEADERS}
+)
diff --git a/example/linux/flutter/generated_plugin_registrant.cc b/example/linux/flutter/generated_plugin_registrant.cc
index 158f759a..fe311fa2 100644
--- a/example/linux/flutter/generated_plugin_registrant.cc
+++ b/example/linux/flutter/generated_plugin_registrant.cc
@@ -6,11 +6,15 @@
#include "generated_plugin_registrant.h"
+#include
#include
#include
#include
void fl_register_plugins(FlPluginRegistry* registry) {
+ g_autoptr(FlPluginRegistrar) desktop_drop_registrar =
+ fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopDropPlugin");
+ desktop_drop_plugin_register_with_registrar(desktop_drop_registrar);
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
diff --git a/example/linux/flutter/generated_plugins.cmake b/example/linux/flutter/generated_plugins.cmake
index 93c755ee..3f7f250e 100644
--- a/example/linux/flutter/generated_plugins.cmake
+++ b/example/linux/flutter/generated_plugins.cmake
@@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
+ desktop_drop
file_selector_linux
pasteboard
url_launcher_linux
diff --git a/example/linux/main.cc b/example/linux/main.cc
index 4340ffc1..e7c5c543 100644
--- a/example/linux/main.cc
+++ b/example/linux/main.cc
@@ -1,6 +1,6 @@
-#include "my_application.h"
-
-int main(int argc, char** argv) {
- g_autoptr(MyApplication) app = my_application_new();
- return g_application_run(G_APPLICATION(app), argc, argv);
-}
+#include "my_application.h"
+
+int main(int argc, char** argv) {
+ g_autoptr(MyApplication) app = my_application_new();
+ return g_application_run(G_APPLICATION(app), argc, argv);
+}
diff --git a/example/linux/my_application.cc b/example/linux/my_application.cc
index 5f64f84a..0ba8f430 100644
--- a/example/linux/my_application.cc
+++ b/example/linux/my_application.cc
@@ -1,104 +1,104 @@
-#include "my_application.h"
-
-#include
-#ifdef GDK_WINDOWING_X11
-#include
-#endif
-
-#include "flutter/generated_plugin_registrant.h"
-
-struct _MyApplication {
- GtkApplication parent_instance;
- char** dart_entrypoint_arguments;
-};
-
-G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
-
-// Implements GApplication::activate.
-static void my_application_activate(GApplication* application) {
- MyApplication* self = MY_APPLICATION(application);
- GtkWindow* window =
- GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
-
- // Use a header bar when running in GNOME as this is the common style used
- // by applications and is the setup most users will be using (e.g. Ubuntu
- // desktop).
- // If running on X and not using GNOME then just use a traditional title bar
- // in case the window manager does more exotic layout, e.g. tiling.
- // If running on Wayland assume the header bar will work (may need changing
- // if future cases occur).
- gboolean use_header_bar = FALSE;
-#ifdef GDK_WINDOWING_X11
- GdkScreen *screen = gtk_window_get_screen(window);
- if (GDK_IS_X11_SCREEN(screen)) {
- const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
- if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
- use_header_bar = FALSE;
- }
- }
-#endif
- if (use_header_bar) {
- GtkHeaderBar *header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
- gtk_widget_show(GTK_WIDGET(header_bar));
- gtk_header_bar_set_title(header_bar, "app");
- gtk_header_bar_set_show_close_button(header_bar, TRUE);
- gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
- }
- else {
- gtk_window_set_title(window, "app");
- }
-
- gtk_window_set_default_size(window, 1280, 720);
- gtk_widget_show(GTK_WIDGET(window));
-
- g_autoptr(FlDartProject) project = fl_dart_project_new();
- fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);
-
- FlView* view = fl_view_new(project);
- gtk_widget_show(GTK_WIDGET(view));
- gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
-
- fl_register_plugins(FL_PLUGIN_REGISTRY(view));
-
- gtk_widget_grab_focus(GTK_WIDGET(view));
-}
-
-// Implements GApplication::local_command_line.
-static gboolean my_application_local_command_line(GApplication* application, gchar ***arguments, int *exit_status) {
- MyApplication* self = MY_APPLICATION(application);
- // Strip out the first argument as it is the binary name.
- self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);
-
- g_autoptr(GError) error = nullptr;
- if (!g_application_register(application, nullptr, &error)) {
- g_warning("Failed to register: %s", error->message);
- *exit_status = 1;
- return TRUE;
- }
-
- g_application_activate(application);
- *exit_status = 0;
-
- return TRUE;
-}
-
-// Implements GObject::dispose.
-static void my_application_dispose(GObject *object) {
- MyApplication* self = MY_APPLICATION(object);
- g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
- G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
-}
-
-static void my_application_class_init(MyApplicationClass* klass) {
- G_APPLICATION_CLASS(klass)->activate = my_application_activate;
- G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;
- G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
-}
-
-static void my_application_init(MyApplication* self) {}
-
-MyApplication* my_application_new() {
- return MY_APPLICATION(g_object_new(my_application_get_type(),
- "application-id", APPLICATION_ID,
- nullptr));
-}
+#include "my_application.h"
+
+#include
+#ifdef GDK_WINDOWING_X11
+#include
+#endif
+
+#include "flutter/generated_plugin_registrant.h"
+
+struct _MyApplication {
+ GtkApplication parent_instance;
+ char** dart_entrypoint_arguments;
+};
+
+G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
+
+// Implements GApplication::activate.
+static void my_application_activate(GApplication* application) {
+ MyApplication* self = MY_APPLICATION(application);
+ GtkWindow* window =
+ GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
+
+ // Use a header bar when running in GNOME as this is the common style used
+ // by applications and is the setup most users will be using (e.g. Ubuntu
+ // desktop).
+ // If running on X and not using GNOME then just use a traditional title bar
+ // in case the window manager does more exotic layout, e.g. tiling.
+ // If running on Wayland assume the header bar will work (may need changing
+ // if future cases occur).
+ gboolean use_header_bar = TRUE;
+#ifdef GDK_WINDOWING_X11
+ GdkScreen* screen = gtk_window_get_screen(window);
+ if (GDK_IS_X11_SCREEN(screen)) {
+ const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
+ if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
+ use_header_bar = FALSE;
+ }
+ }
+#endif
+ if (use_header_bar) {
+ GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
+ gtk_widget_show(GTK_WIDGET(header_bar));
+ gtk_header_bar_set_title(header_bar, "example");
+ gtk_header_bar_set_show_close_button(header_bar, TRUE);
+ gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
+ } else {
+ gtk_window_set_title(window, "example");
+ }
+
+ gtk_window_set_default_size(window, 1280, 720);
+ gtk_widget_show(GTK_WIDGET(window));
+
+ g_autoptr(FlDartProject) project = fl_dart_project_new();
+ fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);
+
+ FlView* view = fl_view_new(project);
+ gtk_widget_show(GTK_WIDGET(view));
+ gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
+
+ fl_register_plugins(FL_PLUGIN_REGISTRY(view));
+
+ gtk_widget_grab_focus(GTK_WIDGET(view));
+}
+
+// Implements GApplication::local_command_line.
+static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) {
+ MyApplication* self = MY_APPLICATION(application);
+ // Strip out the first argument as it is the binary name.
+ self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);
+
+ g_autoptr(GError) error = nullptr;
+ if (!g_application_register(application, nullptr, &error)) {
+ g_warning("Failed to register: %s", error->message);
+ *exit_status = 1;
+ return TRUE;
+ }
+
+ g_application_activate(application);
+ *exit_status = 0;
+
+ return TRUE;
+}
+
+// Implements GObject::dispose.
+static void my_application_dispose(GObject* object) {
+ MyApplication* self = MY_APPLICATION(object);
+ g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
+ G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
+}
+
+static void my_application_class_init(MyApplicationClass* klass) {
+ G_APPLICATION_CLASS(klass)->activate = my_application_activate;
+ G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;
+ G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
+}
+
+static void my_application_init(MyApplication* self) {}
+
+MyApplication* my_application_new() {
+ return MY_APPLICATION(g_object_new(my_application_get_type(),
+ "application-id", APPLICATION_ID,
+ "flags", G_APPLICATION_NON_UNIQUE,
+ nullptr));
+}
diff --git a/example/linux/my_application.h b/example/linux/my_application.h
index 8f20fb55..72271d5e 100644
--- a/example/linux/my_application.h
+++ b/example/linux/my_application.h
@@ -1,18 +1,18 @@
-#ifndef FLUTTER_MY_APPLICATION_H_
-#define FLUTTER_MY_APPLICATION_H_
-
-#include
-
-G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
- GtkApplication)
-
-/**
- * my_application_new:
- *
- * Creates a new Flutter-based application.
- *
- * Returns: a new #MyApplication.
- */
-MyApplication* my_application_new();
-
-#endif // FLUTTER_MY_APPLICATION_H_
+#ifndef FLUTTER_MY_APPLICATION_H_
+#define FLUTTER_MY_APPLICATION_H_
+
+#include
+
+G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
+ GtkApplication)
+
+/**
+ * my_application_new:
+ *
+ * Creates a new Flutter-based application.
+ *
+ * Returns: a new #MyApplication.
+ */
+MyApplication* my_application_new();
+
+#endif // FLUTTER_MY_APPLICATION_H_
diff --git a/example/macos/.gitignore b/example/macos/.gitignore
index e72996ef..746adbb6 100644
--- a/example/macos/.gitignore
+++ b/example/macos/.gitignore
@@ -1,7 +1,7 @@
-# Flutter-related
-**/Flutter/ephemeral/
-**/Pods/
-
-# Xcode-related
-**/xcuserdata/
-Podfile.lock
\ No newline at end of file
+# Flutter-related
+**/Flutter/ephemeral/
+**/Pods/
+
+# Xcode-related
+**/dgph
+**/xcuserdata/
diff --git a/example/macos/Flutter/Flutter-Debug.xcconfig b/example/macos/Flutter/Flutter-Debug.xcconfig
index df4c964c..4b81f9b2 100644
--- a/example/macos/Flutter/Flutter-Debug.xcconfig
+++ b/example/macos/Flutter/Flutter-Debug.xcconfig
@@ -1,2 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
-#include "ephemeral/Flutter-Generated.xcconfig"
+#include "ephemeral/Flutter-Generated.xcconfig"
diff --git a/example/macos/Flutter/Flutter-Release.xcconfig b/example/macos/Flutter/Flutter-Release.xcconfig
index e79501e2..5caa9d15 100644
--- a/example/macos/Flutter/Flutter-Release.xcconfig
+++ b/example/macos/Flutter/Flutter-Release.xcconfig
@@ -1,2 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
-#include "ephemeral/Flutter-Generated.xcconfig"
+#include "ephemeral/Flutter-Generated.xcconfig"
diff --git a/example/macos/Flutter/GeneratedPluginRegistrant.swift b/example/macos/Flutter/GeneratedPluginRegistrant.swift
index 9245196c..6b3c06b5 100644
--- a/example/macos/Flutter/GeneratedPluginRegistrant.swift
+++ b/example/macos/Flutter/GeneratedPluginRegistrant.swift
@@ -5,20 +5,26 @@
import FlutterMacOS
import Foundation
+import desktop_drop
import device_info_plus
import file_selector_macos
import gal
import pasteboard
import path_provider_foundation
+import share_plus
+import sqflite
import url_launcher_macos
import video_player_avfoundation
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
+ DesktopDropPlugin.register(with: registry.registrar(forPlugin: "DesktopDropPlugin"))
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
GalPlugin.register(with: registry.registrar(forPlugin: "GalPlugin"))
PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
+ SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
+ SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin"))
}
diff --git a/example/macos/Podfile b/example/macos/Podfile
index 0c76ccf5..dbccf89c 100644
--- a/example/macos/Podfile
+++ b/example/macos/Podfile
@@ -1,4 +1,4 @@
-platform :osx, '12.0'
+platform :osx, '11.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
@@ -31,6 +31,9 @@ target 'Runner' do
use_modular_headers!
flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
+ target 'RunnerTests' do
+ inherit! :search_paths
+ end
end
post_install do |installer|
diff --git a/example/macos/Podfile.lock b/example/macos/Podfile.lock
new file mode 100644
index 00000000..0df3e6fc
--- /dev/null
+++ b/example/macos/Podfile.lock
@@ -0,0 +1,88 @@
+PODS:
+ - desktop_drop (0.0.1):
+ - FlutterMacOS
+ - device_info_plus (0.0.1):
+ - FlutterMacOS
+ - file_selector_macos (0.0.1):
+ - FlutterMacOS
+ - FlutterMacOS (1.0.0)
+ - FMDB (2.7.5):
+ - FMDB/standard (= 2.7.5)
+ - FMDB/standard (2.7.5)
+ - gal (1.0.0):
+ - Flutter
+ - FlutterMacOS
+ - pasteboard (0.0.1):
+ - FlutterMacOS
+ - path_provider_foundation (0.0.1):
+ - Flutter
+ - FlutterMacOS
+ - share_plus (0.0.1):
+ - FlutterMacOS
+ - sqflite (0.0.2):
+ - FlutterMacOS
+ - FMDB (>= 2.7.5)
+ - url_launcher_macos (0.0.1):
+ - FlutterMacOS
+ - video_player_avfoundation (0.0.1):
+ - Flutter
+ - FlutterMacOS
+
+DEPENDENCIES:
+ - desktop_drop (from `Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos`)
+ - device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`)
+ - file_selector_macos (from `Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos`)
+ - FlutterMacOS (from `Flutter/ephemeral`)
+ - gal (from `Flutter/ephemeral/.symlinks/plugins/gal/darwin`)
+ - pasteboard (from `Flutter/ephemeral/.symlinks/plugins/pasteboard/macos`)
+ - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
+ - share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`)
+ - sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/macos`)
+ - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
+ - video_player_avfoundation (from `Flutter/ephemeral/.symlinks/plugins/video_player_avfoundation/darwin`)
+
+SPEC REPOS:
+ trunk:
+ - FMDB
+
+EXTERNAL SOURCES:
+ desktop_drop:
+ :path: Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos
+ device_info_plus:
+ :path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos
+ file_selector_macos:
+ :path: Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos
+ FlutterMacOS:
+ :path: Flutter/ephemeral
+ gal:
+ :path: Flutter/ephemeral/.symlinks/plugins/gal/darwin
+ pasteboard:
+ :path: Flutter/ephemeral/.symlinks/plugins/pasteboard/macos
+ path_provider_foundation:
+ :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
+ share_plus:
+ :path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos
+ sqflite:
+ :path: Flutter/ephemeral/.symlinks/plugins/sqflite/macos
+ url_launcher_macos:
+ :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
+ video_player_avfoundation:
+ :path: Flutter/ephemeral/.symlinks/plugins/video_player_avfoundation/darwin
+
+SPEC CHECKSUMS:
+ desktop_drop: 69eeff437544aa619c8db7f4481b3a65f7696898
+ device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f
+ file_selector_macos: 468fb6b81fac7c0e88d71317f3eec34c3b008ff9
+ FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
+ FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
+ gal: 61e868295d28fe67ffa297fae6dacebf56fd53e1
+ pasteboard: 9b69dba6fedbb04866be632205d532fe2f6b1d99
+ path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
+ share_plus: 76dd39142738f7a68dd57b05093b5e8193f220f7
+ sqflite: a5789cceda41d54d23f31d6de539d65bb14100ea
+ url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95
+ video_player_avfoundation: 8563f13d8fc8b2c29dc2d09e60b660e4e8128837
+
+PODFILE CHECKSUM: c2e95c8c0fe03c5c57e438583cae4cc732296009
+
+COCOAPODS: 1.14.2
diff --git a/example/macos/Runner.xcodeproj/project.pbxproj b/example/macos/Runner.xcodeproj/project.pbxproj
index 29557646..9d12e3f3 100644
--- a/example/macos/Runner.xcodeproj/project.pbxproj
+++ b/example/macos/Runner.xcodeproj/project.pbxproj
@@ -21,7 +21,9 @@
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
- 07D884DE6AB8033C3F60B238 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 48A88899E2BC5FD7AFD2B040 /* Pods_Runner.framework */; };
+ 0819A4118119F0FB90CC792A /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DF9630B171C82D9F157D8A7A /* Pods_RunnerTests.framework */; };
+ 0ECAC09CE6CB433383FE46BA /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F25EE7A42A9A7B340553AFD5 /* Pods_Runner.framework */; };
+ 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; };
335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
@@ -30,6 +32,13 @@
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
+ 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 33CC10EC2044A3C60003C045;
+ remoteInfo = Runner;
+ };
33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
@@ -53,10 +62,14 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
- 17BD0969A552CE47C17FC221 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
+ 08857C82C866FAA72A6112E1 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; };
+ 1AE7B8BE0097CC9BD2CEB71C /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
+ 213343C812B541CBD55EC002 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
+ 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; };
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; };
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; };
- 33CC10ED2044A3C60003C045 /* app.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = app.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; };
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; };
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; };
@@ -68,25 +81,43 @@
33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; };
33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; };
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; };
- 3DB40E993F068140F6DEEA8F /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
- 48A88899E2BC5FD7AFD2B040 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
- 676737A1C184536E1D9D90A1 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
+ 382159709DCCA9047A6B60BF /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; };
+ AD4F0BE8B3C0868B8BCD1802 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; };
+ B6A180C3D11679D4FEC36007 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
+ DF9630B171C82D9F157D8A7A /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ F25EE7A42A9A7B340553AFD5 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
+ 331C80D2294CF70F00263BE5 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 0819A4118119F0FB90CC792A /* Pods_RunnerTests.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
33CC10EA2044A3C60003C045 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- 07D884DE6AB8033C3F60B238 /* Pods_Runner.framework in Frameworks */,
+ 0ECAC09CE6CB433383FE46BA /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
+ 331C80D6294CF71000263BE5 /* RunnerTests */ = {
+ isa = PBXGroup;
+ children = (
+ 331C80D7294CF71000263BE5 /* RunnerTests.swift */,
+ );
+ path = RunnerTests;
+ sourceTree = "";
+ };
33BA886A226E78AF003329D5 /* Configs */ = {
isa = PBXGroup;
children = (
@@ -103,16 +134,18 @@
children = (
33FAB671232836740065AC1E /* Runner */,
33CEB47122A05771004F2AC0 /* Flutter */,
+ 331C80D6294CF71000263BE5 /* RunnerTests */,
33CC10EE2044A3C60003C045 /* Products */,
- E0CAA5D4D3AFCAEB94FF2464 /* Pods */,
- C2525C9EE4B6956CB985C5A2 /* Frameworks */,
+ 50A36B439D603B27B2A10103 /* Pods */,
+ 6BD6849B62426A4BAFC71567 /* Frameworks */,
);
sourceTree = "";
};
33CC10EE2044A3C60003C045 /* Products */ = {
isa = PBXGroup;
children = (
- 33CC10ED2044A3C60003C045 /* app.app */,
+ 33CC10ED2044A3C60003C045 /* example.app */,
+ 331C80D5294CF71000263BE5 /* RunnerTests.xctest */,
);
name = Products;
sourceTree = "";
@@ -152,39 +185,62 @@
path = Runner;
sourceTree = "";
};
- C2525C9EE4B6956CB985C5A2 /* Frameworks */ = {
+ 50A36B439D603B27B2A10103 /* Pods */ = {
isa = PBXGroup;
children = (
- 48A88899E2BC5FD7AFD2B040 /* Pods_Runner.framework */,
+ 213343C812B541CBD55EC002 /* Pods-Runner.debug.xcconfig */,
+ B6A180C3D11679D4FEC36007 /* Pods-Runner.release.xcconfig */,
+ 1AE7B8BE0097CC9BD2CEB71C /* Pods-Runner.profile.xcconfig */,
+ AD4F0BE8B3C0868B8BCD1802 /* Pods-RunnerTests.debug.xcconfig */,
+ 08857C82C866FAA72A6112E1 /* Pods-RunnerTests.release.xcconfig */,
+ 382159709DCCA9047A6B60BF /* Pods-RunnerTests.profile.xcconfig */,
);
- name = Frameworks;
+ name = Pods;
+ path = Pods;
sourceTree = "";
};
- E0CAA5D4D3AFCAEB94FF2464 /* Pods */ = {
+ 6BD6849B62426A4BAFC71567 /* Frameworks */ = {
isa = PBXGroup;
children = (
- 3DB40E993F068140F6DEEA8F /* Pods-Runner.debug.xcconfig */,
- 676737A1C184536E1D9D90A1 /* Pods-Runner.release.xcconfig */,
- 17BD0969A552CE47C17FC221 /* Pods-Runner.profile.xcconfig */,
+ F25EE7A42A9A7B340553AFD5 /* Pods_Runner.framework */,
+ DF9630B171C82D9F157D8A7A /* Pods_RunnerTests.framework */,
);
- name = Pods;
- path = Pods;
+ name = Frameworks;
sourceTree = "";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
+ 331C80D4294CF70F00263BE5 /* RunnerTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
+ buildPhases = (
+ B106B712E930130681A09692 /* [CP] Check Pods Manifest.lock */,
+ 331C80D1294CF70F00263BE5 /* Sources */,
+ 331C80D2294CF70F00263BE5 /* Frameworks */,
+ 331C80D3294CF70F00263BE5 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 331C80DA294CF71000263BE5 /* PBXTargetDependency */,
+ );
+ name = RunnerTests;
+ productName = RunnerTests;
+ productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
33CC10EC2044A3C60003C045 /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
- 8E0B73589C7156B8D6C458C1 /* [CP] Check Pods Manifest.lock */,
+ 1C8E506F11B9603AA202D203 /* [CP] Check Pods Manifest.lock */,
33CC10E92044A3C60003C045 /* Sources */,
33CC10EA2044A3C60003C045 /* Frameworks */,
33CC10EB2044A3C60003C045 /* Resources */,
33CC110E2044A8840003C045 /* Bundle Framework */,
3399D490228B24CF009A79C7 /* ShellScript */,
- 64BF2FA23C00365B5E3F66C0 /* [CP] Embed Pods Frameworks */,
+ CCF759434F90A85A86F6BD32 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@@ -193,7 +249,7 @@
);
name = Runner;
productName = Runner;
- productReference = 33CC10ED2044A3C60003C045 /* app.app */;
+ productReference = 33CC10ED2044A3C60003C045 /* example.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
@@ -206,6 +262,10 @@
LastUpgradeCheck = 1430;
ORGANIZATIONNAME = "";
TargetAttributes = {
+ 331C80D4294CF70F00263BE5 = {
+ CreatedOnToolsVersion = 14.0;
+ TestTargetID = 33CC10EC2044A3C60003C045;
+ };
33CC10EC2044A3C60003C045 = {
CreatedOnToolsVersion = 9.2;
LastSwiftMigration = 1100;
@@ -236,12 +296,20 @@
projectRoot = "";
targets = (
33CC10EC2044A3C60003C045 /* Runner */,
+ 331C80D4294CF70F00263BE5 /* RunnerTests */,
33CC111A2044C6BA0003C045 /* Flutter Assemble */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
+ 331C80D3294CF70F00263BE5 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
33CC10EB2044A3C60003C045 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -254,6 +322,28 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
+ 1C8E506F11B9603AA202D203 /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
3399D490228B24CF009A79C7 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
@@ -292,48 +382,56 @@
shellPath = /bin/sh;
shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
};
- 64BF2FA23C00365B5E3F66C0 /* [CP] Embed Pods Frameworks */ = {
+ B106B712E930130681A09692 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
- "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
- name = "[CP] Embed Pods Frameworks";
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
- "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
- 8E0B73589C7156B8D6C458C1 /* [CP] Check Pods Manifest.lock */ = {
+ CCF759434F90A85A86F6BD32 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
- inputPaths = (
- "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
- "${PODS_ROOT}/Manifest.lock",
- );
- name = "[CP] Check Pods Manifest.lock";
+ name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
- );
- outputPaths = (
- "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
+ 331C80D1294CF70F00263BE5 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
33CC10E92044A3C60003C045 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -347,6 +445,11 @@
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
+ 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 33CC10EC2044A3C60003C045 /* Runner */;
+ targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */;
+ };
33CC11202044C79F0003C045 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;
@@ -367,6 +470,51 @@
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
+ 331C80DB294CF71000263BE5 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = AD4F0BE8B3C0868B8BCD1802 /* Pods-RunnerTests.debug.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/example";
+ };
+ name = Debug;
+ };
+ 331C80DC294CF71000263BE5 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 08857C82C866FAA72A6112E1 /* Pods-RunnerTests.release.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/example";
+ };
+ name = Release;
+ };
+ 331C80DD294CF71000263BE5 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 382159709DCCA9047A6B60BF /* Pods-RunnerTests.profile.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/example";
+ };
+ name = Profile;
+ };
338D0CE9231458BD00FA5F75 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
@@ -405,7 +553,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
+ MACOSX_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;
@@ -427,7 +575,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
- MACOSX_DEPLOYMENT_TARGET = 12.0;
+ MACOSX_DEPLOYMENT_TARGET = 11.0;
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
};
@@ -485,7 +633,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
+ MACOSX_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
@@ -532,7 +680,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)";
+ MACOSX_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;
@@ -554,7 +702,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
- MACOSX_DEPLOYMENT_TARGET = 12.0;
+ MACOSX_DEPLOYMENT_TARGET = 11.0;
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
@@ -575,7 +723,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
- MACOSX_DEPLOYMENT_TARGET = 12.0;
+ MACOSX_DEPLOYMENT_TARGET = 11.0;
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
};
@@ -600,6 +748,16 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
+ 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 331C80DB294CF71000263BE5 /* Debug */,
+ 331C80DC294CF71000263BE5 /* Release */,
+ 331C80DD294CF71000263BE5 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
diff --git a/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
index fc6bf807..18d98100 100644
--- a/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
+++ b/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -1,8 +1,8 @@
-
-
-
-
- IDEDidComputeMac32BitWarning
-
-
-
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
index 8cbaa660..397f3d33 100644
--- a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
+++ b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -15,7 +15,7 @@
@@ -31,13 +31,24 @@
-
-
+
+
+
+
+
+
-
-
diff --git a/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
index fc6bf807..18d98100 100644
--- a/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
+++ b/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -1,8 +1,8 @@
-
-
-
-
- IDEDidComputeMac32BitWarning
-
-
-
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/example/macos/Runner/AppDelegate.swift b/example/macos/Runner/AppDelegate.swift
index 553a135b..d53ef643 100644
--- a/example/macos/Runner/AppDelegate.swift
+++ b/example/macos/Runner/AppDelegate.swift
@@ -1,9 +1,9 @@
-import Cocoa
-import FlutterMacOS
-
-@NSApplicationMain
-class AppDelegate: FlutterAppDelegate {
- override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
- return true
- }
-}
+import Cocoa
+import FlutterMacOS
+
+@NSApplicationMain
+class AppDelegate: FlutterAppDelegate {
+ override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
+ return true
+ }
+}
diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
index 8d4e7cb8..a2ec33f1 100644
--- a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
+++ b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -1,68 +1,68 @@
-{
- "images" : [
- {
- "size" : "16x16",
- "idiom" : "mac",
- "filename" : "app_icon_16.png",
- "scale" : "1x"
- },
- {
- "size" : "16x16",
- "idiom" : "mac",
- "filename" : "app_icon_32.png",
- "scale" : "2x"
- },
- {
- "size" : "32x32",
- "idiom" : "mac",
- "filename" : "app_icon_32.png",
- "scale" : "1x"
- },
- {
- "size" : "32x32",
- "idiom" : "mac",
- "filename" : "app_icon_64.png",
- "scale" : "2x"
- },
- {
- "size" : "128x128",
- "idiom" : "mac",
- "filename" : "app_icon_128.png",
- "scale" : "1x"
- },
- {
- "size" : "128x128",
- "idiom" : "mac",
- "filename" : "app_icon_256.png",
- "scale" : "2x"
- },
- {
- "size" : "256x256",
- "idiom" : "mac",
- "filename" : "app_icon_256.png",
- "scale" : "1x"
- },
- {
- "size" : "256x256",
- "idiom" : "mac",
- "filename" : "app_icon_512.png",
- "scale" : "2x"
- },
- {
- "size" : "512x512",
- "idiom" : "mac",
- "filename" : "app_icon_512.png",
- "scale" : "1x"
- },
- {
- "size" : "512x512",
- "idiom" : "mac",
- "filename" : "app_icon_1024.png",
- "scale" : "2x"
- }
- ],
- "info" : {
- "version" : 1,
- "author" : "xcode"
- }
-}
+{
+ "images" : [
+ {
+ "size" : "16x16",
+ "idiom" : "mac",
+ "filename" : "app_icon_16.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "16x16",
+ "idiom" : "mac",
+ "filename" : "app_icon_32.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "32x32",
+ "idiom" : "mac",
+ "filename" : "app_icon_32.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "32x32",
+ "idiom" : "mac",
+ "filename" : "app_icon_64.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "128x128",
+ "idiom" : "mac",
+ "filename" : "app_icon_128.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "128x128",
+ "idiom" : "mac",
+ "filename" : "app_icon_256.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "256x256",
+ "idiom" : "mac",
+ "filename" : "app_icon_256.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "256x256",
+ "idiom" : "mac",
+ "filename" : "app_icon_512.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "512x512",
+ "idiom" : "mac",
+ "filename" : "app_icon_512.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "512x512",
+ "idiom" : "mac",
+ "filename" : "app_icon_1024.png",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
index 3c4935a7..82b6f9d9 100644
Binary files a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png and b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ
diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png
index ed4cc164..13b35eba 100644
Binary files a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png and b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ
diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png
index 483be613..0a3f5fa4 100644
Binary files a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png and b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ
diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png
index bcbf36df..bdb57226 100644
Binary files a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png and b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ
diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png
index 9c0a6528..f083318e 100644
Binary files a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png and b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ
diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png
index e71a7261..326c0e72 100644
Binary files a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png and b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ
diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png
index 8a31fe2d..2f1632cf 100644
Binary files a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png and b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ
diff --git a/example/macos/Runner/Base.lproj/MainMenu.xib b/example/macos/Runner/Base.lproj/MainMenu.xib
index 030024dc..80e867a4 100644
--- a/example/macos/Runner/Base.lproj/MainMenu.xib
+++ b/example/macos/Runner/Base.lproj/MainMenu.xib
@@ -1,339 +1,343 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/macos/Runner/Configs/AppInfo.xcconfig b/example/macos/Runner/Configs/AppInfo.xcconfig
index 38c93ba6..dda192bc 100644
--- a/example/macos/Runner/Configs/AppInfo.xcconfig
+++ b/example/macos/Runner/Configs/AppInfo.xcconfig
@@ -1,14 +1,14 @@
-// Application-level settings for the Runner target.
-//
-// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the
-// future. If not, the values below would default to using the project name when this becomes a
-// 'flutter create' template.
-
-// The application's name. By default this is also the title of the Flutter window.
-PRODUCT_NAME = app
-
-// The application's bundle identifier
-PRODUCT_BUNDLE_IDENTIFIER = com.example.app
-
-// The copyright displayed in application information
-PRODUCT_COPYRIGHT = Copyright © 2021 com.example. All rights reserved.
+// Application-level settings for the Runner target.
+//
+// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the
+// future. If not, the values below would default to using the project name when this becomes a
+// 'flutter create' template.
+
+// The application's name. By default this is also the title of the Flutter window.
+PRODUCT_NAME = example
+
+// The application's bundle identifier
+PRODUCT_BUNDLE_IDENTIFIER = com.example.example
+
+// The copyright displayed in application information
+PRODUCT_COPYRIGHT = Copyright © 2023 com.example. All rights reserved.
diff --git a/example/macos/Runner/Configs/Debug.xcconfig b/example/macos/Runner/Configs/Debug.xcconfig
index b3988237..36b0fd94 100644
--- a/example/macos/Runner/Configs/Debug.xcconfig
+++ b/example/macos/Runner/Configs/Debug.xcconfig
@@ -1,2 +1,2 @@
-#include "../../Flutter/Flutter-Debug.xcconfig"
-#include "Warnings.xcconfig"
+#include "../../Flutter/Flutter-Debug.xcconfig"
+#include "Warnings.xcconfig"
diff --git a/example/macos/Runner/Configs/Release.xcconfig b/example/macos/Runner/Configs/Release.xcconfig
index d93e5dc4..dff4f495 100644
--- a/example/macos/Runner/Configs/Release.xcconfig
+++ b/example/macos/Runner/Configs/Release.xcconfig
@@ -1,2 +1,2 @@
-#include "../../Flutter/Flutter-Release.xcconfig"
-#include "Warnings.xcconfig"
+#include "../../Flutter/Flutter-Release.xcconfig"
+#include "Warnings.xcconfig"
diff --git a/example/macos/Runner/Configs/Warnings.xcconfig b/example/macos/Runner/Configs/Warnings.xcconfig
index fb4d7d3f..42bcbf47 100644
--- a/example/macos/Runner/Configs/Warnings.xcconfig
+++ b/example/macos/Runner/Configs/Warnings.xcconfig
@@ -1,13 +1,13 @@
-WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings
-GCC_WARN_UNDECLARED_SELECTOR = YES
-CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES
-CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE
-CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
-CLANG_WARN_PRAGMA_PACK = YES
-CLANG_WARN_STRICT_PROTOTYPES = YES
-CLANG_WARN_COMMA = YES
-GCC_WARN_STRICT_SELECTOR_MATCH = YES
-CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES
-CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
-GCC_WARN_SHADOW = YES
-CLANG_WARN_UNREACHABLE_CODE = YES
+WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings
+GCC_WARN_UNDECLARED_SELECTOR = YES
+CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES
+CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE
+CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
+CLANG_WARN_PRAGMA_PACK = YES
+CLANG_WARN_STRICT_PROTOTYPES = YES
+CLANG_WARN_COMMA = YES
+GCC_WARN_STRICT_SELECTOR_MATCH = YES
+CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES
+CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
+GCC_WARN_SHADOW = YES
+CLANG_WARN_UNREACHABLE_CODE = YES
diff --git a/example/macos/Runner/DebugProfile.entitlements b/example/macos/Runner/DebugProfile.entitlements
index d6c8ee0c..b3feca3e 100644
--- a/example/macos/Runner/DebugProfile.entitlements
+++ b/example/macos/Runner/DebugProfile.entitlements
@@ -1,14 +1,18 @@
-
-
-
-
- com.apple.security.app-sandbox
-
- com.apple.security.cs.allow-jit
-
- com.apple.security.network.server
-
- com.apple.security.network.client
-
-
-
+
+
+
+
+ com.apple.security.app-sandbox
+
+ com.apple.security.cs.allow-jit
+
+ com.apple.security.network.server
+
+ com.apple.security.network.client
+
+ com.apple.security.files.user-selected.read-only
+
+
+
+
diff --git a/example/macos/Runner/Info.plist b/example/macos/Runner/Info.plist
index 3733c1a8..0eae602e 100644
--- a/example/macos/Runner/Info.plist
+++ b/example/macos/Runner/Info.plist
@@ -1,32 +1,36 @@
-
-
-
-
- CFBundleDevelopmentRegion
- $(DEVELOPMENT_LANGUAGE)
- CFBundleExecutable
- $(EXECUTABLE_NAME)
- CFBundleIconFile
-
- CFBundleIdentifier
- $(PRODUCT_BUNDLE_IDENTIFIER)
- CFBundleInfoDictionaryVersion
- 6.0
- CFBundleName
- $(PRODUCT_NAME)
- CFBundlePackageType
- APPL
- CFBundleShortVersionString
- $(FLUTTER_BUILD_NAME)
- CFBundleVersion
- $(FLUTTER_BUILD_NUMBER)
- LSMinimumSystemVersion
- $(MACOSX_DEPLOYMENT_TARGET)
- NSHumanReadableCopyright
- $(PRODUCT_COPYRIGHT)
- NSMainNibFile
- MainMenu
- NSPrincipalClass
- NSApplication
-
-
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIconFile
+
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $(FLUTTER_BUILD_NAME)
+ CFBundleVersion
+ $(FLUTTER_BUILD_NUMBER)
+ LSMinimumSystemVersion
+ $(MACOSX_DEPLOYMENT_TARGET)
+ NSHumanReadableCopyright
+ $(PRODUCT_COPYRIGHT)
+ NSMainNibFile
+ MainMenu
+ NSPrincipalClass
+ NSApplication
+ NSPhotoLibraryUsageDescription
+ We need permission to the photo library in order for inserting images in the text editor
+ NSPhotoLibraryAddUsageDescription
+ We need this permission for saving the images in the editor
+
+
diff --git a/example/macos/Runner/MainFlutterWindow.swift b/example/macos/Runner/MainFlutterWindow.swift
index 4cb95dc9..3cc05eb2 100644
--- a/example/macos/Runner/MainFlutterWindow.swift
+++ b/example/macos/Runner/MainFlutterWindow.swift
@@ -1,15 +1,15 @@
-import Cocoa
-import FlutterMacOS
-
-class MainFlutterWindow: NSWindow {
- override func awakeFromNib() {
- let flutterViewController = FlutterViewController.init()
- let windowFrame = self.frame
- self.contentViewController = flutterViewController
- self.setFrame(windowFrame, display: true)
-
- RegisterGeneratedPlugins(registry: flutterViewController)
-
- super.awakeFromNib()
- }
-}
+import Cocoa
+import FlutterMacOS
+
+class MainFlutterWindow: NSWindow {
+ override func awakeFromNib() {
+ let flutterViewController = FlutterViewController()
+ let windowFrame = self.frame
+ self.contentViewController = flutterViewController
+ self.setFrame(windowFrame, display: true)
+
+ RegisterGeneratedPlugins(registry: flutterViewController)
+
+ super.awakeFromNib()
+ }
+}
diff --git a/example/macos/Runner/Release.entitlements b/example/macos/Runner/Release.entitlements
index 04336df3..319280f5 100644
--- a/example/macos/Runner/Release.entitlements
+++ b/example/macos/Runner/Release.entitlements
@@ -1,8 +1,14 @@
-
-
-
-
- com.apple.security.app-sandbox
-
-
-
+
+
+
+
+ com.apple.security.app-sandbox
+
+ com.apple.security.network.client
+
+ com.apple.security.files.user-selected.read-only
+
+
+
+
diff --git a/example/macos/RunnerTests/RunnerTests.swift b/example/macos/RunnerTests/RunnerTests.swift
new file mode 100644
index 00000000..5418c9f5
--- /dev/null
+++ b/example/macos/RunnerTests/RunnerTests.swift
@@ -0,0 +1,12 @@
+import FlutterMacOS
+import Cocoa
+import XCTest
+
+class RunnerTests: XCTestCase {
+
+ func testExample() {
+ // If you add code to the Runner application, consider adding tests here.
+ // See https://developer.apple.com/documentation/xctest for more information about using XCTest.
+ }
+
+}
diff --git a/example/pubspec.yaml b/example/pubspec.yaml
index 4053c2c7..dc0df887 100644
--- a/example/pubspec.yaml
+++ b/example/pubspec.yaml
@@ -1,39 +1,77 @@
-name: app
-description: demo app
+name: example
+description: A demo for the Flutter Quill
publish_to: 'none'
version: 1.0.0+1
environment:
- sdk: '>=2.12.0 <3.0.0'
-
+ sdk: '>=3.1.5 <4.0.0'
dependencies:
flutter:
sdk: flutter
- universal_html: ^2.2.4
-
+ flutter_localizations:
+ sdk: flutter
cupertino_icons: ^1.0.6
+
+ # Flutter Quill Packages
+ flutter_quill: ^8.5.5
+ flutter_quill_extensions: ^0.6.10
+ flutter_quill_test: ^0.0.5
+ quill_html_converter: ^0.0.1-experimental.1
+
+ # Normal Packages
+ path: ^1.8.3
+ equatable: ^2.0.5
+ cross_file: ^0.3.3+6
+ cached_network_image: ^3.3.0
+
+ # Bloc libraries
+ bloc: ^8.1.2
+ flutter_bloc: ^8.1.3
+ hydrated_bloc: ^9.1.2
+
+ # Freezed
+ freezed_annotation: ^2.4.1
+
+ # Json
+ json_annotation: ^4.8.1
+
+ # Plugins
+ image_cropper: ^5.0.0
path_provider: ^2.1.1
- filesystem_picker: ^4.0.0
- file_picker: ^6.0.0
- flutter_quill:
- path: ../
- flutter_quill_extensions:
- path: ../flutter_quill_extensions
+ # For drag and drop feature
+ desktop_drop: ^0.4.4
+ # For picking quill document files
+ file_picker: ^6.1.1
+ # For sharing text
+ share_plus: ^7.2.1
dependency_overrides:
flutter_quill:
path: ../
+ flutter_quill_extensions:
+ path: ../flutter_quill_extensions
+ flutter_quill_test:
+ path: ../flutter_quill_test
+ quill_html_converter:
+ path: ../packages/quill_html_converter
dev_dependencies:
flutter_test:
sdk: flutter
+ flutter_lints: ^3.0.1
+ build_runner: ^2.4.6
+ flutter_gen_runner: ^5.3.2
+ # Freezed
+ freezed: ^2.4.5
+ # Json
+ json_serializable: ^6.7.1
flutter:
-
uses-material-design: true
assets:
- assets/
-
+ - assets/images/
+
fonts:
- family: monospace
fonts:
@@ -61,4 +99,11 @@ flutter:
- asset: assets/fonts/RobotoMono-Regular.ttf
- family: SF-UI-Display
fonts:
- - asset: assets/fonts/SF-Pro-Display-Regular.otf
\ No newline at end of file
+ - asset: assets/fonts/SF-Pro-Display-Regular.otf
+
+flutter_gen:
+ # integrations:
+ # flutter_svg: true
+ # flare_flutter: true
+ # rive: true
+ # lottie: true
diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart
index ca3ecf17..ab73b3a2 100644
--- a/example/test/widget_test.dart
+++ b/example/test/widget_test.dart
@@ -1,29 +1 @@
-// This is a basic Flutter widget test.
-//
-// To perform an interaction with a widget in your test, use the WidgetTester
-// utility that Flutter provides. For example, you can send tap and scroll
-// gestures. You can also use WidgetTester to find child widgets in the widget
-// tree, read text, and verify that the values of widget properties are correct.
-
-import 'package:app/main.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-
-void main() {
- testWidgets('Counter increments smoke test', (tester) async {
- // Build our app and trigger a frame.
- await tester.pumpWidget(MyApp());
-
- // Verify that our counter starts at 0.
- expect(find.text('0'), findsOneWidget);
- expect(find.text('1'), findsNothing);
-
- // Tap the '+' icon and trigger a frame.
- await tester.tap(find.byIcon(Icons.add));
- await tester.pump();
-
- // Verify that our counter has incremented.
- expect(find.text('0'), findsNothing);
- expect(find.text('1'), findsOneWidget);
- });
-}
+void main() {}
diff --git a/example/web/icons/Icon-maskable-192.png b/example/web/icons/Icon-maskable-192.png
new file mode 100644
index 00000000..eb9b4d76
Binary files /dev/null and b/example/web/icons/Icon-maskable-192.png differ
diff --git a/example/web/icons/Icon-maskable-512.png b/example/web/icons/Icon-maskable-512.png
new file mode 100644
index 00000000..d69c5669
Binary files /dev/null and b/example/web/icons/Icon-maskable-512.png differ
diff --git a/example/web/index.html b/example/web/index.html
index 887a139d..f998cef3 100644
--- a/example/web/index.html
+++ b/example/web/index.html
@@ -8,10 +8,13 @@
The path provided below has to start and end with a slash "/" in order for
it to work correctly.
- Fore more details:
+ For more details:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
+
+ This is a placeholder for base href that will be replaced by the value of
+ the `--base-href` argument provided to `flutter build`.
-->
-
+
@@ -20,26 +23,42 @@
-
+
- app
+ example
+
+
+
+
+
+
+
+
+
-
-