diff --git a/.gitignore b/.gitignore
index 841758d2..cee99ed0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -78,4 +78,6 @@ example/ios/Podfile.lock
pubspec.lock
# For local development
-pubspec_overrides.yaml
\ No newline at end of file
+pubspec_overrides.yaml
+
+old_example
\ No newline at end of file
diff --git a/.pubignore b/.pubignore
index 15f51bee..e4e6f2b1 100644
--- a/.pubignore
+++ b/.pubignore
@@ -10,4 +10,4 @@ example/.fvm/
example/build/
example/.dart_tool/
-scripts/
\ No newline at end of file
+scripts/
diff --git a/README.md b/README.md
index a11a815f..9aad0a05 100644
--- a/README.md
+++ b/README.md
@@ -65,10 +65,10 @@ it in GitHub repo instead.
-
-
-
-
+
+
+
+
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/README.md b/example/README.md
index 08534b51..c727691c 100644
--- a/example/README.md
+++ b/example/README.md
@@ -5,7 +5,14 @@ This is just a demo of Flutter Quill
## Screenshots
-
-
-
-
\ No newline at end of file
+
+
+
+
+
+## Development notes
+
+- 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
index b553997f..735e0400 100644
--- a/example/analysis_options.yaml
+++ b/example/analysis_options.yaml
@@ -2,8 +2,8 @@ include: package:flutter_lints/flutter.yaml
analyzer:
errors:
- undefined_prefixed_name: ignore
- unsafe_html: ignore
+ invalid_annotation_target: ignore
+
linter:
rules:
always_declare_return_types: true
@@ -12,7 +12,7 @@ linter:
avoid_empty_else: true
avoid_escaping_inner_quotes: true
avoid_print: false
- avoid_redundant_argument_values: true
+ avoid_redundant_argument_values: false
avoid_types_on_closure_parameters: true
avoid_void_async: true
cascade_invocations: true
@@ -34,4 +34,4 @@ linter:
unnecessary_lambdas: true
unnecessary_parenthesis: true
unnecessary_string_interpolations: true
- library_private_types_in_public_api: false
+ 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 e574d1e8..d2bb9117 100644
--- a/example/android/app/build.gradle
+++ b/example/android/app/build.gradle
@@ -27,15 +27,13 @@ android {
compileSdk flutter.compileSdkVersion
ndkVersion flutter.ndkVersion
- def javaVersion = JavaVersion.VERSION_17
-
compileOptions {
- sourceCompatibility javaVersion
- targetCompatibility javaVersion
+ sourceCompatibility JavaVersion.VERSION_17
+ targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
- jvmTarget = javaVersion.toString()
+ jvmTarget = '1.8'
}
sourceSets {
@@ -44,7 +42,7 @@ android {
defaultConfig {
applicationId "com.example.example"
- minSdkVersion 24
+ minSdkVersion 23
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml
index 6176738c..44cd71db 100644
--- a/example/android/app/src/main/AndroidManifest.xml
+++ b/example/android/app/src/main/AndroidManifest.xml
@@ -1,15 +1,13 @@
-
+
-
+
-
+
@@ -21,7 +19,7 @@
+ android:label="@string/app_name">
-
+
-
+ android:name="com.yalantis.ucrop.UCropActivity"
+ android:screenOrientation="portrait"
+ android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
+
+ Flutter Quill Demo
+
\ No newline at end of file
diff --git a/example/android/build.gradle b/example/android/build.gradle
index 10bc7da8..d73bed24 100644
--- a/example/android/build.gradle
+++ b/example/android/build.gradle
@@ -8,7 +8,7 @@ buildscript {
}
dependencies {
- classpath 'com.android.tools.build:gradle:8.1.2'
+ classpath 'com.android.tools.build:gradle:8.1.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
diff --git a/example/assets/images/1.png b/example/assets/images/screenshot_1.png
similarity index 100%
rename from example/assets/images/1.png
rename to example/assets/images/screenshot_1.png
diff --git a/example/assets/images/2.png b/example/assets/images/screenshot_2.png
similarity index 100%
rename from example/assets/images/2.png
rename to example/assets/images/screenshot_2.png
diff --git a/example/assets/images/3.png b/example/assets/images/screenshot_3.png
similarity index 100%
rename from example/assets/images/3.png
rename to example/assets/images/screenshot_3.png
diff --git a/example/assets/images/4.png b/example/assets/images/screenshot_4.png
similarity index 100%
rename from example/assets/images/4.png
rename to example/assets/images/screenshot_4.png
diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj
index bcaa8216..75c0e507 100644
--- a/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/example/ios/Runner.xcodeproj/project.pbxproj
@@ -7,15 +7,13 @@
objects = {
/* Begin PBXBuildFile section */
- 110D5FB266DE0C87D485C5A1 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CA159BA3A0CB20166BDC7649 /* Pods_RunnerTests.framework */; };
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
- 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
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 */; };
- E0CFF8B0B56159E612BD1BF1 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 56338E24E520D8744CFF5FE2 /* Pods_Runner.framework */; };
+ 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -44,15 +42,10 @@
/* 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 = ""; };
- 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; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
- 56338E24E520D8744CFF5FE2 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
- 6B6E89A47C28537B66DC0FA4 /* 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 = ""; };
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 = ""; };
- 7D464E3E73705EDBCA621B35 /* 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 = ""; };
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; };
@@ -60,64 +53,21 @@
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 = ""; };
- A5625BCCF88D6127AE26675C /* 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 = ""; };
- CA159BA3A0CB20166BDC7649 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
- D0600C43B20FD3C51A3985DD /* 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 = ""; };
- E008F2A10A874EB0C8AB0591 /* 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 = ""; };
- F65D90E41795239C733C3B48 /* 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 = ""; };
+ 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 */
- 729BF1F2E5A6188112D79F62 /* Frameworks */ = {
- isa = PBXFrameworksBuildPhase;
- buildActionMask = 2147483647;
- files = (
- 110D5FB266DE0C87D485C5A1 /* Pods_RunnerTests.framework in Frameworks */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- E0CFF8B0B56159E612BD1BF1 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
- 331C8082294A63A400263BE5 /* RunnerTests */ = {
- isa = PBXGroup;
- children = (
- 331C807B294A618700263BE5 /* RunnerTests.swift */,
- );
- path = RunnerTests;
- sourceTree = "";
- };
- 808219EA7A4ECE8B7D2A26F2 /* Frameworks */ = {
- isa = PBXGroup;
- children = (
- 56338E24E520D8744CFF5FE2 /* Pods_Runner.framework */,
- CA159BA3A0CB20166BDC7649 /* Pods_RunnerTests.framework */,
- );
- name = Frameworks;
- sourceTree = "";
- };
- 839C085E910FF72A20C1DC94 /* Pods */ = {
- isa = PBXGroup;
- children = (
- E008F2A10A874EB0C8AB0591 /* Pods-Runner.debug.xcconfig */,
- D0600C43B20FD3C51A3985DD /* Pods-Runner.release.xcconfig */,
- F65D90E41795239C733C3B48 /* Pods-Runner.profile.xcconfig */,
- 7D464E3E73705EDBCA621B35 /* Pods-RunnerTests.debug.xcconfig */,
- A5625BCCF88D6127AE26675C /* Pods-RunnerTests.release.xcconfig */,
- 6B6E89A47C28537B66DC0FA4 /* Pods-RunnerTests.profile.xcconfig */,
- );
- name = Pods;
- path = Pods;
- sourceTree = "";
- };
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
@@ -129,6 +79,14 @@
name = Flutter;
sourceTree = "";
};
+ 331C8082294A63A400263BE5 /* RunnerTests */ = {
+ isa = PBXGroup;
+ children = (
+ 331C807B294A618700263BE5 /* RunnerTests.swift */,
+ );
+ path = RunnerTests;
+ sourceTree = "";
+ };
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
@@ -136,8 +94,6 @@
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
331C8082294A63A400263BE5 /* RunnerTests */,
- 839C085E910FF72A20C1DC94 /* Pods */,
- 808219EA7A4ECE8B7D2A26F2 /* Frameworks */,
);
sourceTree = "";
};
@@ -172,10 +128,9 @@
isa = PBXNativeTarget;
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
- 81CBE292094CBE166A4095F9 /* [CP] Check Pods Manifest.lock */,
331C807D294A63A400263BE5 /* Sources */,
+ 331C807E294A63A400263BE5 /* Frameworks */,
331C807F294A63A400263BE5 /* Resources */,
- 729BF1F2E5A6188112D79F62 /* Frameworks */,
);
buildRules = (
);
@@ -191,14 +146,12 @@
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
- 06DBD2892C9AFDD9AA47E269 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
- 7C1D0099598B9B03C264FE8E /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@@ -270,28 +223,6 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
- 06DBD2892C9AFDD9AA47E269 /* [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;
- };
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
@@ -308,45 +239,6 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
- 7C1D0099598B9B03C264FE8E /* [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;
- };
- 81CBE292094CBE166A4095F9 /* [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-RunnerTests-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;
- };
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
@@ -485,7 +377,7 @@
};
331C8088294A63A400263BE5 /* Debug */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 7D464E3E73705EDBCA621B35 /* Pods-RunnerTests.debug.xcconfig */;
+ baseConfigurationReference = AE0B7B92F70575B8D7E0D07E /* Pods-RunnerTests.debug.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
@@ -503,7 +395,7 @@
};
331C8089294A63A400263BE5 /* Release */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = A5625BCCF88D6127AE26675C /* Pods-RunnerTests.release.xcconfig */;
+ baseConfigurationReference = 89B67EB44CE7B6631473024E /* Pods-RunnerTests.release.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
@@ -519,7 +411,7 @@
};
331C808A294A63A400263BE5 /* Profile */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 6B6E89A47C28537B66DC0FA4 /* Pods-RunnerTests.profile.xcconfig */;
+ baseConfigurationReference = 640959BDD8F10B91D80A66BE /* Pods-RunnerTests.profile.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
diff --git a/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcworkspace/contents.xcworkspacedata
index 21a3cc14..1d526a16 100644
--- a/example/ios/Runner.xcworkspace/contents.xcworkspacedata
+++ b/example/ios/Runner.xcworkspace/contents.xcworkspacedata
@@ -4,7 +4,4 @@
-
-
diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist
index 8dd062a7..be041944 100644
--- a/example/ios/Runner/Info.plist
+++ b/example/ios/Runner/Info.plist
@@ -46,8 +46,12 @@
UIApplicationSupportsIndirectInputEvents
NSPhotoLibraryUsageDescription
- The app will use it to pick images
+ We need permission to the photo library in order for inserting images in the text editor
NSCameraUsageDescription
- The app will use it to capture a images, record videos.
+ 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/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 b80d1ca5..3b53bca3 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -1,11 +1,34 @@
+import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/material.dart';
-import 'package:flutter_localizations/flutter_localizations.dart';
-import 'package:flutter_quill/translations.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();
+ HydratedBloc.storage = await HydratedStorage.build(
+ storageDirectory: kIsWeb
+ ? HydratedStorage.webStorageDirectory
+ : await getApplicationDocumentsDirectory(),
+ );
runApp(const MyApp());
}
@@ -14,29 +37,119 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
- return MaterialApp(
- debugShowCheckedModeBanner: false,
- title: 'Quill Demo',
- theme: ThemeData(
- primarySwatch: Colors.blue,
- visualDensity: VisualDensity.adaptivePlatformDensity,
- useMaterial3: true,
- brightness: Brightness.light,
- ),
- darkTheme: ThemeData(
- primarySwatch: Colors.blue,
- visualDensity: VisualDensity.adaptivePlatformDensity,
- useMaterial3: true,
- brightness: Brightness.dark,
- ),
- localizationsDelegates: const [
- GlobalMaterialLocalizations.delegate,
- GlobalWidgetsLocalizations.delegate,
- GlobalCupertinoLocalizations.delegate,
- FlutterQuillLocalizations.delegate,
+ return MultiBlocProvider(
+ providers: [
+ BlocProvider(
+ create: (context) => SettingsCubit(),
+ ),
],
- supportedLocales: FlutterQuillLocalizations.supportedLocales,
- home: const 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 6252c09a..00000000
--- a/example/lib/pages/home_page.dart
+++ /dev/null
@@ -1,879 +0,0 @@
-// ignore_for_file: avoid_redundant_argument_values
-
-import 'dart:async' show Timer;
-import 'dart:convert';
-import 'dart:io' show File;
-import 'dart:ui';
-
-import 'package:desktop_drop/desktop_drop.dart';
-import 'package:file_picker/file_picker.dart';
-import 'package:flutter/foundation.dart';
-import 'package:flutter/material.dart';
-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:flutter_quill_extensions/logic/services/image_picker/image_picker.dart';
-import 'package:flutter_quill_extensions/presentation/embeds/widgets/image.dart';
-import 'package:image_cropper/image_cropper.dart';
-import 'package:path/path.dart' as path;
-import 'package:path_provider/path_provider.dart';
-import 'package:quill_html_converter/quill_html_converter.dart';
-
-import '../samples/sample_data.dart';
-import '../samples/sample_data_nomedia.dart';
-import '../samples/sample_data_testing.dart';
-import '../widgets/time_stamp_embed_widget.dart';
-import 'read_only_page.dart';
-
-enum _SelectionType {
- none,
- word,
- // line,
-}
-
-class HomePage extends StatefulWidget {
- const HomePage({super.key});
-
- @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;
- var _isReadOnly = false;
-
- @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 doc = Document.fromJson(sampleDataTesting);
- _controller = QuillController(
- document: doc,
- selection: const TextSelection.collapsed(offset: 0),
- );
- } catch (error) {
- print(error.toString());
- final doc = Document()
- ..insert(0, 'Error while loading the document: ${error.toString()}');
- _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(
- title: const Text(
- 'Flutter Quill',
- ),
- actions: [
- IconButton(
- tooltip: 'Print to log',
- onPressed: () {
- print(
- jsonEncode(_controller.document.toDelta().toJson()),
- );
- ScaffoldMessenger.of(context).showSnackBar(
- const SnackBar(
- content: Text(
- 'The quill delta json has been printed to the log.',
- ),
- ),
- );
- },
- icon: const Icon(
- Icons.print,
- ),
- ),
- IconButton(
- tooltip: 'Toggle read only',
- onPressed: () {
- setState(() => _isReadOnly = !_isReadOnly);
- },
- icon: Icon(
- _isReadOnly ? Icons.lock : Icons.edit,
- ),
- ),
- 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.editorBuilders(),
- TimeStampEmbedBuilderWidget()
- ],
- ),
- ),
- ),
- ),
- icon: const Icon(Icons.text_fields_rounded),
- )
- ],
- ),
- drawer: Drawer(
- child: ListView(
- children: [
- DrawerHeader(
- child: IconButton(
- tooltip: 'Open document by json delta',
- 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();
- _controller.document =
- Document.fromJson(jsonDecode(jsonString));
- } catch (e) {
- print(
- 'Error while loading json delta file: ${e.toString()}',
- );
- scaffoldMessenger.showSnackBar(
- SnackBar(
- content: Text(
- 'Error while loading json delta file: ${e.toString()}',
- ),
- ),
- );
- } finally {
- navigator.pop();
- }
- },
- icon: const Icon(Icons.file_copy),
- ),
- ),
- ListTile(
- title: const Text('Load sample data'),
- onTap: () async {
- final scaffoldMessenger = ScaffoldMessenger.of(context);
- final navigator = Navigator.of(context);
- try {
- _controller.document = Document.fromJson(
- sampleData,
- );
- } catch (e) {
- print(
- 'Error while loading json delta file: ${e.toString()}',
- );
- scaffoldMessenger.showSnackBar(SnackBar(
- content: Text(
- 'Error while loading json delta file: ${e.toString()}',
- ),
- ));
- } finally {
- navigator.pop();
- }
- },
- ),
- ListTile(
- title: const Text('Load sample data with no media'),
- onTap: () async {
- final scaffoldMessenger = ScaffoldMessenger.of(context);
- final navigator = Navigator.of(context);
- try {
- _controller.document = Document.fromJson(
- sampleDataNoMedia,
- );
- } catch (e) {
- print(
- 'Error while loading json delta file: ${e.toString()}',
- );
- scaffoldMessenger.showSnackBar(SnackBar(
- content: Text(
- 'Error while loading json delta file: ${e.toString()}',
- ),
- ));
- } finally {
- navigator.pop();
- }
- },
- ),
- ListTile(
- title: const Text('Load testing sample data '),
- onTap: () async {
- final scaffoldMessenger = ScaffoldMessenger.of(context);
- final navigator = Navigator.of(context);
- try {
- _controller.document = Document.fromJson(
- sampleDataTesting,
- );
- } catch (e) {
- print(
- 'Error while loading json delta file: ${e.toString()}',
- );
- scaffoldMessenger.showSnackBar(SnackBar(
- content: Text(
- 'Error while loading json delta file: ${e.toString()}',
- ),
- ));
- } finally {
- navigator.pop();
- }
- },
- ),
- ListTile(
- title: const Text('Convert to/from HTML'),
- onTap: () async {
- final scaffoldMessenger = ScaffoldMessenger.of(context);
- final navigator = Navigator.of(context);
- try {
- final html = _controller.document.toDelta().toHtml();
- _controller.document =
- Document.fromDelta(DeltaHtmlExt.fromHtml(html));
- } catch (e) {
- scaffoldMessenger.showSnackBar(
- SnackBar(
- content: Text(
- 'Error while convert to/from HTML: ${e.toString()}',
- ),
- ),
- );
- } finally {
- navigator.pop();
- }
- },
- ),
- _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;
- });
- }
-
- OnDragDoneCallback get _onDragDone {
- return (details) {
- final scaffoldMessenger = ScaffoldMessenger.of(context);
- final file = details.files.first;
- final isSupported =
- imageFileExtensions.any((ext) => file.name.endsWith(ext));
- if (!isSupported) {
- scaffoldMessenger.showSnackBar(
- SnackBar(
- content: Text(
- 'Only images are supported right now: ${file.mimeType}, ${file.name}, ${file.path}, $imageFileExtensions',
- ),
- ),
- );
- return;
- }
- _controller.insertImageBlock(
- imageSource: file.path,
- );
- scaffoldMessenger.showSnackBar(
- const SnackBar(
- content: Text('Image is inserted.'),
- ),
- );
- };
- }
-
- QuillEditor get quillEditor {
- if (kIsWeb) {
- return QuillEditor(
- focusNode: _focusNode,
- scrollController: ScrollController(),
- configurations: QuillEditorConfigurations(
- builder: (context, rawEditor) {
- return DropTarget(
- onDragDone: _onDragDone,
- child: rawEditor,
- );
- },
- placeholder: 'Add content',
- readOnly: false,
- scrollable: true,
- autoFocus: false,
- expands: false,
- padding: EdgeInsets.zero,
- onTapUp: (details, p1) {
- return _onTripleClickSelection();
- },
- 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),
- ),
- embedBuilders: [
- ...FlutterQuillEmbeds.editorWebBuilders(),
- TimeStampEmbedBuilderWidget()
- ],
- ),
- );
- }
- return QuillEditor(
- configurations: QuillEditorConfigurations(
- builder: (context, rawEditor) {
- return DropTarget(
- onDragDone: _onDragDone,
- child: rawEditor,
- );
- },
- placeholder: 'Add content',
- readOnly: _isReadOnly,
- autoFocus: false,
- enableSelectionToolbar: isMobile(supportWeb: false),
- expands: false,
- padding: EdgeInsets.zero,
- onImagePaste: _onImagePaste,
- onTapUp: (details, p1) {
- return _onTripleClickSelection();
- },
- 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()],
- ),
- ),
- embedBuilders: [
- ...FlutterQuillEmbeds.editorBuilders(
- imageEmbedConfigurations:
- const QuillEditorImageEmbedConfigurations(),
- ),
- TimeStampEmbedBuilderWidget()
- ],
- ),
- scrollController: ScrollController(),
- focusNode: _focusNode,
- );
- }
-
- /// When inserting an image
- OnImageInsertCallback get onImageInsert {
- return (image, controller) async {
- final croppedFile = await ImageCropper().cropImage(
- sourcePath: image,
- aspectRatioPresets: [
- CropAspectRatioPreset.square,
- CropAspectRatioPreset.ratio3x2,
- CropAspectRatioPreset.original,
- CropAspectRatioPreset.ratio4x3,
- CropAspectRatioPreset.ratio16x9
- ],
- uiSettings: [
- AndroidUiSettings(
- toolbarTitle: 'Cropper',
- toolbarColor: Colors.deepOrange,
- toolbarWidgetColor: Colors.white,
- initAspectRatio: CropAspectRatioPreset.original,
- lockAspectRatio: false,
- ),
- IOSUiSettings(
- title: 'Cropper',
- ),
- WebUiSettings(
- context: context,
- ),
- ],
- );
- final newImage = croppedFile?.path;
- if (newImage == null) {
- return;
- }
- if (isWeb()) {
- controller.insertImageBlock(imageSource: newImage);
- return;
- }
- final newSavedImage = await _onImagePickCallback(File(newImage));
- controller.insertImageBlock(imageSource: newSavedImage);
- };
- }
-
- QuillToolbar get quillToolbar {
- final 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');
- },
- ),
- ];
- if (kIsWeb) {
- return QuillToolbar(
- configurations: QuillToolbarConfigurations(
- customButtons: customButtons,
- embedButtons: FlutterQuillEmbeds.toolbarButtons(
- cameraButtonOptions: const QuillToolbarCameraButtonOptions(),
- imageButtonOptions: QuillToolbarImageButtonOptions(
- imageButtonConfigurations: QuillToolbarImageConfigurations(
- onImageInsertedCallback: (image) async {},
- onImageInsertCallback: onImageInsert,
- ),
- ),
- ),
- buttonOptions: QuillToolbarButtonOptions(
- base: QuillToolbarBaseButtonOptions(
- afterButtonPressed: _focusNode.requestFocus,
- ),
- ),
- ),
- );
- }
- if (isDesktop(supportWeb: false)) {
- return QuillToolbar(
- configurations: QuillToolbarConfigurations(
- customButtons: customButtons,
- embedButtons: FlutterQuillEmbeds.toolbarButtons(
- cameraButtonOptions: const QuillToolbarCameraButtonOptions(),
- imageButtonOptions: QuillToolbarImageButtonOptions(
- imageButtonConfigurations: QuillToolbarImageConfigurations(
- onImageInsertedCallback: (image) async {
- ScaffoldMessenger.of(context).showSnackBar(
- const SnackBar(
- content: Text('Image inserted'),
- ),
- );
- },
- ),
- ),
- ),
- showAlignmentButtons: true,
- buttonOptions: QuillToolbarButtonOptions(
- base: QuillToolbarBaseButtonOptions(
- afterButtonPressed: _focusNode.requestFocus,
- ),
- ),
- ),
- );
- }
- return QuillToolbar(
- configurations: QuillToolbarConfigurations(
- customButtons: customButtons,
- embedButtons: FlutterQuillEmbeds.toolbarButtons(
- cameraButtonOptions: const QuillToolbarCameraButtonOptions(),
- videoButtonOptions: QuillToolbarVideoButtonOptions(
- videoConfigurations: QuillToolbarVideoConfigurations(
- onVideoInsertedCallback: (video) =>
- _onVideoPickCallback(File(video)),
- ),
- ),
- imageButtonOptions: QuillToolbarImageButtonOptions(
- imageButtonConfigurations: QuillToolbarImageConfigurations(
- onImageInsertCallback: onImageInsert,
- onImageInsertedCallback: (image) async {
- ScaffoldMessenger.of(context).showSnackBar(
- const SnackBar(
- content: Text('Image inserted'),
- ),
- );
- },
- ),
- // 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,
- // uncomment to provide a custom "pick from" dialog.
- // mediaPickSettingSelector: _selectMediaPickSetting,
- // uncomment to provide a custom "pick from" dialog.
- // cameraPickSettingSelector: _selectCameraPickSetting,
- ),
- // videoButtonOptions: QuillToolbarVideoButtonOptions(
- // onVideoPickCallback: _onVideoPickCallback,
- // ),
- ),
- 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,
- sharedConfigurations: QuillSharedConfigurations(
- animationConfigurations: QuillAnimationConfigurations.enableAll(),
- locale: const Locale(
- 'de',
- ), // won't take affect since we defined FlutterQuillLocalizations.delegate
- ),
- ),
- child: Column(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Expanded(
- flex: 15,
- child: Container(
- padding: const EdgeInsets.only(left: 16, right: 16),
- child: quillEditor,
- ),
- ),
- if (!_isReadOnly)
- kIsWeb
- ? Expanded(
- child: Container(
- padding: const EdgeInsets.symmetric(
- vertical: 16, horizontal: 8),
- child: quillToolbar,
- ))
- : Container(
- child: quillToolbar,
- )
- ],
- ),
- ),
- );
- }
-
- // 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)}');
- final copiedFile = await file.copy(path.join(
- appDocDir.path,
- '${DateTime.now().toIso8601String()}${path.extension(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);
- return Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- Divider(
- thickness: 2,
- indent: size.width * 0.1,
- endIndent: size.width * 0.1,
- ),
- ListTile(
- title: const Center(
- child: Text(
- 'Read only demo',
- )),
- dense: true,
- visualDensity: VisualDensity.compact,
- onTap: _openReadOnlyPage,
- ),
- Divider(
- thickness: 2,
- indent: size.width * 0.1,
- endIndent: size.width * 0.1,
- ),
- ],
- );
- }
-
- void _openReadOnlyPage() {
- Navigator.pop(super.context);
- Navigator.push(
- super.context,
- MaterialPageRoute(
- builder: (context) => const 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 5d4ff051..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 '../widgets/demo_scaffold.dart';
-
-class ReadOnlyPage extends StatefulWidget {
- const ReadOnlyPage({super.key});
-
- @override
- _ReadOnlyPageState createState() => _ReadOnlyPageState();
-}
-
-class _ReadOnlyPageState extends State {
- final FocusNode _focusNode = FocusNode();
-
- bool _edit = false;
-
- @override
- Widget build(BuildContext context) {
- return DemoScaffold(
- documentFilename: isDesktop(supportWeb: false)
- ? '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: kIsWeb
- ? FlutterQuillEmbeds.editorWebBuilders()
- : FlutterQuillEmbeds.editorBuilders(),
- scrollable: true,
- autoFocus: true,
- ),
- scrollController: ScrollController(),
- focusNode: _focusNode,
- // readOnly: !_edit,
- );
- if (kIsWeb) {
- quillEditor = QuillEditor(
- configurations: QuillEditorConfigurations(
- autoFocus: true,
- expands: false,
- padding: EdgeInsets.zero,
- embedBuilders: FlutterQuillEmbeds.editorWebBuilders(),
- scrollable: true,
- ),
- scrollController: ScrollController(),
- focusNode: _focusNode,
- );
- }
- return Container(
- decoration: BoxDecoration(
- border: Border.all(color: Colors.grey.shade200),
- ),
- padding: const EdgeInsets.all(8),
- child: quillEditor,
- );
- }
-
- void _toggleEdit() {
- setState(() {
- _edit = !_edit;
- });
- }
-}
diff --git a/example/lib/pages/testing_home_page.dart b/example/lib/pages/testing_home_page.dart
deleted file mode 100644
index 03dc4bba..00000000
--- a/example/lib/pages/testing_home_page.dart
+++ /dev/null
@@ -1,13 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:flutter_quill/flutter_quill.dart';
-
-class TestingHomePage extends StatelessWidget {
- const TestingHomePage({super.key});
-
- @override
- Widget build(BuildContext context) {
- return const Scaffold(
- appBar: QuillToolbar(),
- );
- }
-}
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 86%
rename from example/lib/widgets/time_stamp_embed_widget.dart
rename to example/lib/presentation/quill/embeds/timestamp_embed.dart
index d78457f0..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 {
diff --git a/example/lib/presentation/quill/quill_editor.dart b/example/lib/presentation/quill/quill_editor.dart
new file mode 100644
index 00000000..e7f67732
--- /dev/null
+++ b/example/lib/presentation/quill/quill_editor.dart
@@ -0,0 +1,116 @@
+import 'dart:io' as io show Directory, File;
+
+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(
+ 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..c091beb5
--- /dev/null
+++ b/example/lib/presentation/quill/quill_screen.dart
@@ -0,0 +1,141 @@
+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) const MyQuillToolbar(),
+ 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..a0f16677
--- /dev/null
+++ b/example/lib/presentation/quill/quill_toolbar.dart
@@ -0,0 +1,289 @@
+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({super.key});
+
+ 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(
+ 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/samples/sample_data.dart b/example/lib/presentation/quill/samples/quill_default_sample.dart
similarity index 98%
rename from example/lib/samples/sample_data.dart
rename to example/lib/presentation/quill/samples/quill_default_sample.dart
index 5c940ddb..618775cf 100644
--- a/example/lib/samples/sample_data.dart
+++ b/example/lib/presentation/quill/samples/quill_default_sample.dart
@@ -1,6 +1,8 @@
-const sampleData = [
+import '../../../gen/assets.gen.dart';
+
+final quillDefaultSample = [
{
- 'insert': {'image': 'assets/images/1.png'},
+ 'insert': {'image': Assets.images.screenshot1.path},
'attributes': {
'width': '100',
'height': '100',
diff --git a/example/lib/samples/sample_data_testing.dart b/example/lib/presentation/quill/samples/quill_images_sample.dart
similarity index 99%
rename from example/lib/samples/sample_data_testing.dart
rename to example/lib/presentation/quill/samples/quill_images_sample.dart
index 05f81ab4..b483e733 100644
--- a/example/lib/samples/sample_data_testing.dart
+++ b/example/lib/presentation/quill/samples/quill_images_sample.dart
@@ -1,8 +1,10 @@
-const sampleDataTesting = [
+import '../../../gen/assets.gen.dart';
+
+final quillImagesSample = [
{'insert': 'This is an asset image: \n'},
{'insert': '\n'},
{
- 'insert': {'image': 'assets/images/1.png'},
+ 'insert': {'image': Assets.images.screenshot1.path},
'attributes': {
'width': '100',
'height': '100',
@@ -67,17 +69,4 @@ const sampleDataTesting = [
'The source of the above image is also image base 64 but this time it start with `data:image/png;base64,`'
},
{'insert': '\n'},
- {'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'}
];
diff --git a/example/lib/samples/sample_data_nomedia.dart b/example/lib/presentation/quill/samples/quill_text_sample.dart
similarity index 99%
rename from example/lib/samples/sample_data_nomedia.dart
rename to example/lib/presentation/quill/samples/quill_text_sample.dart
index 360ab187..4e047db9 100644
--- a/example/lib/samples/sample_data_nomedia.dart
+++ b/example/lib/presentation/quill/samples/quill_text_sample.dart
@@ -1,4 +1,4 @@
-const sampleDataNoMedia = [
+const quillTextSample = [
{'insert': 'Flutter Quill'},
{
'attributes': {'header': 1},
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 334e022c..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 2ac0d8fe..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 aa276dbc..00000000
--- a/example/lib/universal_ui/universal_ui.dart
+++ /dev/null
@@ -1,115 +0,0 @@
-library universal_ui;
-
-// import 'package:flutter_quill/flutter_quill.dart';
-// import 'package:flutter_quill_extensions/flutter_quill_extensions.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: size.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';
-// }
-// }
-
-// final size = MediaQuery.sizeOf(context);
-
-// UniversalUI().platformViewRegistry.registerViewFactory(
-// videoUrl,
-// (id) => html.IFrameElement()
-// ..width = size.width.toString()
-// ..height = size.height.toString()
-// ..src = videoUrl
-// ..style.border = 'none',
-// );
-
-// return SizedBox(
-// height: 500,
-// child: HtmlElementView(
-// viewType: videoUrl,
-// ),
-// );
-// }
-// }
-
-// List get defaultEmbedBuildersWeb => [
-// ...FlutterQuillEmbeds.editorsWebBuilders(),
-// // ImageEmbedBuilderWeb(),
-// // VideoEmbedBuilderWeb(),
-// ];
diff --git a/example/lib/widgets/demo_scaffold.dart b/example/lib/widgets/demo_scaffold.dart
deleted file mode 100644
index 85311a85..00000000
--- a/example/lib/widgets/demo_scaffold.dart
+++ /dev/null
@@ -1,142 +0,0 @@
-import 'dart:convert';
-import 'dart:io' show Platform;
-
-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';
-
-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,
- super.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.toolbarButtons(
- // ignore: avoid_redundant_argument_values
- imageButtonOptions: const QuillToolbarImageButtonOptions(
- // filePickImpl: openFileSystemPickerForDesktop,
- ),
- ),
- ),
- );
- }
- return QuillToolbar(
- configurations: QuillToolbarConfigurations(
- embedButtons: FlutterQuillEmbeds.toolbarButtons(),
- ),
- );
- }
-
- @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 d815f734..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,
- super.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/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 ac927005..6b3c06b5 100644
--- a/example/macos/Flutter/GeneratedPluginRegistrant.swift
+++ b/example/macos/Flutter/GeneratedPluginRegistrant.swift
@@ -11,6 +11,8 @@ 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
@@ -21,6 +23,8 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
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 2302f9b8..b3feca3e 100644
--- a/example/macos/Runner/DebugProfile.entitlements
+++ b/example/macos/Runner/DebugProfile.entitlements
@@ -1,16 +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.files.user-selected.read-only
-
-
-
+
+
+
+
+ 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 7370dc0b..319280f5 100644
--- a/example/macos/Runner/Release.entitlements
+++ b/example/macos/Runner/Release.entitlements
@@ -1,12 +1,14 @@
-
-
-
-
- com.apple.security.app-sandbox
-
- com.apple.security.network.client
-
- com.apple.security.files.user-selected.read-only
-
-
-
+
+
+
+
+ com.apple.security.app-sandbox
+
+ com.apple.security.network.client
+
+ com.apple.security.files.user-selected.read-only
+
+
+
+
diff --git a/example/pubspec.yaml b/example/pubspec.yaml
index 2c462ebf..18e40f9b 100644
--- a/example/pubspec.yaml
+++ b/example/pubspec.yaml
@@ -1,29 +1,49 @@
-name: app
-description: demo app
+name: example
+description: A demo for the Flutter Quill
publish_to: 'none'
version: 1.0.0+1
environment:
- sdk: '>=3.1.3 <4.0.0'
-
+ sdk: '>=3.1.5 <4.0.0'
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
- universal_html: ^2.2.4
-
cupertino_icons: ^1.0.6
- path_provider: ^2.1.1
- # filesystem_picker: ^4.0.0
- file_picker: ^6.1.1
- flutter_quill: ^8.5.1
- flutter_quill_extensions: ^0.6.7
- quill_html_converter:
- path: ../packages/quill_html_converter
+
+ # Flutter Quill Packages
+ flutter_quill: ^8.5.3
+ flutter_quill_extensions: ^0.6.9
+ flutter_quill_test: ^0.0.5
+ quill_html_converter: ^0.0.1-experimental.1
+
+ # Normal Packages
path: ^1.8.3
- desktop_drop: ^0.4.4
+ 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
+ # 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:
@@ -32,20 +52,26 @@ dependency_overrides:
path: ../flutter_quill_extensions
flutter_quill_test:
path: ../flutter_quill_test
+ quill_html_converter:
+ path: ../packages/quill_html_converter
dev_dependencies:
- flutter_lints: ^3.0.1
- flutter_quill_test: ^0.0.4
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:
@@ -73,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 5f46835d..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(const 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/index.html b/example/web/index.html
index 1029dffd..f998cef3 100644
--- a/example/web/index.html
+++ b/example/web/index.html
@@ -32,7 +32,7 @@
example
-
+
diff --git a/example/windows/flutter/generated_plugin_registrant.cc b/example/windows/flutter/generated_plugin_registrant.cc
index 9e89e3c8..ef6dc906 100644
--- a/example/windows/flutter/generated_plugin_registrant.cc
+++ b/example/windows/flutter/generated_plugin_registrant.cc
@@ -10,6 +10,7 @@
#include
#include
#include
+#include
#include
void RegisterPlugins(flutter::PluginRegistry* registry) {
@@ -21,6 +22,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("GalPluginCApi"));
PasteboardPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PasteboardPlugin"));
+ SharePlusWindowsPluginCApiRegisterWithRegistrar(
+ registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi"));
UrlLauncherWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
}
diff --git a/example/windows/flutter/generated_plugins.cmake b/example/windows/flutter/generated_plugins.cmake
index f3449e49..abc6f627 100644
--- a/example/windows/flutter/generated_plugins.cmake
+++ b/example/windows/flutter/generated_plugins.cmake
@@ -7,6 +7,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
file_selector_windows
gal
pasteboard
+ share_plus
url_launcher_windows
)
diff --git a/flutter_quill_extensions/lib/presentation/embeds/editor/image/image_menu.dart b/flutter_quill_extensions/lib/presentation/embeds/editor/image/image_menu.dart
index ea519af8..3b7f34bb 100644
--- a/flutter_quill_extensions/lib/presentation/embeds/editor/image/image_menu.dart
+++ b/flutter_quill_extensions/lib/presentation/embeds/editor/image/image_menu.dart
@@ -1,6 +1,6 @@
import 'package:flutter/cupertino.dart' show showCupertinoModalPopup;
import 'package:flutter/material.dart';
-import 'package:flutter_quill/extensions.dart' show isDesktop, isMobile;
+import 'package:flutter_quill/extensions.dart' show isMobile;
import 'package:flutter_quill/flutter_quill.dart'
show ImageUrl, QuillController, StyleAttribute, getEmbedNode;
import 'package:flutter_quill/translations.dart';
@@ -133,7 +133,6 @@ class ImageOptionsMenu extends StatelessWidget {
ListTile(
leading: const Icon(Icons.save),
title: Text(context.loc.save),
- enabled: !isDesktop(supportWeb: false),
onTap: () async {
final messenger = ScaffoldMessenger.of(context);
final localizations = context.loc;
@@ -143,7 +142,7 @@ class ImageOptionsMenu extends StatelessWidget {
imageUrl: imageSource,
imageSaverService: imageSaverService,
);
- final imageSavedSuccessfully = saveImageResult.isSuccess;
+ final imageSavedSuccessfully = saveImageResult.error == null;
messenger.clearSnackBars();
diff --git a/flutter_quill_extensions/lib/presentation/models/config/editor/image/image.dart b/flutter_quill_extensions/lib/presentation/models/config/editor/image/image.dart
index ed7e5abe..8d6646f0 100644
--- a/flutter_quill_extensions/lib/presentation/models/config/editor/image/image.dart
+++ b/flutter_quill_extensions/lib/presentation/models/config/editor/image/image.dart
@@ -101,6 +101,9 @@ class QuillEditorImageEmbedConfigurations {
///
final ImageEmbedBuilderErrorWidgetBuilder? imageErrorWidgetBuilder;
+ /// What should happen when the image is pressed?
+ ///
+ /// By default will show `ImageOptionsMenu` dialog
final VoidCallback? onImageClicked;
static ImageEmbedBuilderOnRemovedCallback get defaultOnImageRemovedCallback {
diff --git a/flutter_quill_extensions/lib/presentation/utils/utils.dart b/flutter_quill_extensions/lib/presentation/utils/utils.dart
index fe25b7c4..00e986fd 100644
--- a/flutter_quill_extensions/lib/presentation/utils/utils.dart
+++ b/flutter_quill_extensions/lib/presentation/utils/utils.dart
@@ -45,9 +45,9 @@ enum SaveImageResultMethod { network, localStorage }
@immutable
class SaveImageResult {
- const SaveImageResult({required this.isSuccess, required this.method});
+ const SaveImageResult({required this.error, required this.method});
- final bool isSuccess;
+ final String? error;
final SaveImageResultMethod method;
}
@@ -67,12 +67,12 @@ Future saveImage({
Uri.parse(appendFileExtensionToImageUrl(imageUrl)),
);
return const SaveImageResult(
- isSuccess: true,
+ error: null,
method: SaveImageResultMethod.network,
);
} catch (e) {
- return const SaveImageResult(
- isSuccess: false,
+ return SaveImageResult(
+ error: e.toString(),
method: SaveImageResultMethod.network,
);
}
@@ -80,12 +80,12 @@ Future saveImage({
try {
await imageSaverService.saveLocalImage(imageUrl);
return const SaveImageResult(
- isSuccess: true,
+ error: null,
method: SaveImageResultMethod.localStorage,
);
} catch (e) {
- return const SaveImageResult(
- isSuccess: false,
+ return SaveImageResult(
+ error: e.toString(),
method: SaveImageResultMethod.localStorage,
);
}
diff --git a/lib/src/models/documents/nodes/embeddable.dart b/lib/src/models/documents/nodes/embeddable.dart
index 1db18da8..24903315 100644
--- a/lib/src/models/documents/nodes/embeddable.dart
+++ b/lib/src/models/documents/nodes/embeddable.dart
@@ -1,4 +1,4 @@
-import 'dart:convert';
+import 'dart:convert' show jsonDecode, jsonEncode;
/// An object which can be embedded into a Quill document.
///
diff --git a/lib/src/utils/platform.dart b/lib/src/utils/platform.dart
index 020cfae3..a06db05c 100644
--- a/lib/src/utils/platform.dart
+++ b/lib/src/utils/platform.dart
@@ -2,10 +2,12 @@ import 'dart:io' show Platform;
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart'
- show kIsWeb, TargetPlatform, defaultTargetPlatform;
+ show TargetPlatform, defaultTargetPlatform, kIsWeb, visibleForTesting;
/// If you want to override the [kIsWeb] use [overrideIsWeb]
-bool isWeb({bool? overrideIsWeb}) {
+bool isWeb({
+ @visibleForTesting bool? overrideIsWeb,
+}) {
return overrideIsWeb ?? kIsWeb;
}
diff --git a/pubspec.yaml b/pubspec.yaml
index c120d033..cb3ea9e7 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -13,13 +13,13 @@ topics:
screenshots:
- description: 'Screenshot 1'
- path: example/assets/images/1.png
+ path: example/assets/images/screenshot_1.png
- description: 'Screenshot 2'
- path: example/assets/images/2.png
+ path: example/assets/images/screenshot_2.png
- description: 'Screenshot 3'
- path: example/assets/images/3.png
+ path: example/assets/images/screenshot_3.png
- description: 'Screenshot 4'
- path: example/assets/images/4.png
+ path: example/assets/images/screenshot_4.png
platforms:
android: