diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9a9d41a0..f2142c66 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,13 @@
+## 8.0.1
+
+* Dart sdk: '>=2.18.0 <4.0.0'
+
+## 8.0.0
+
+* Migrate to Flutter 3.10.0 and Dart 3.0.0 (#557,#563,#570,#572,#573)
+* Cherry Pick https://github.com/flutter/flutter/pull/110131
+* Cherry Pick https://github.com/flutter/flutter/pull/119495
+
## 7.0.2
* publish v6.4.1 for flutter 3.3.0 and v6.2.2 for flutter 3.0.5
diff --git a/analysis_options.yaml b/analysis_options.yaml
index d8dbf79f..52b92671 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -19,9 +19,6 @@
# Android Studio, and the `flutter analyze` command.
analyzer:
- strong-mode:
- implicit-casts: false
- implicit-dynamic: false
errors:
# treat missing required parameters as a warning (not a hint)
missing_required_param: warning
@@ -139,10 +136,8 @@ linter:
# - prefer_constructors_over_static_methods # not yet tested
- prefer_contains
# - prefer_double_quotes # opposite of prefer_single_quotes
- - prefer_equal_for_default_values
# - prefer_expression_function_bodies # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods
- prefer_final_fields
- - prefer_final_in_for_each
- prefer_final_locals
- prefer_for_elements_to_map_fromIterable
- prefer_foreach
diff --git a/example/.metadata b/example/.metadata
index f06a1f48..e94891d5 100644
--- a/example/.metadata
+++ b/example/.metadata
@@ -1,10 +1,45 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
-# This file should be version controlled and should not be manually edited.
+# This file should be version controlled.
version:
- revision: 63b2daff7f91afeaac47f3646f59eefd59210c41
- channel: unknown
+ revision: 84a1e904f44f9b0e9c4510138010edcc653163f8
+ channel: stable
project_type: app
+
+# Tracks metadata for the flutter migrate command
+migration:
+ platforms:
+ - platform: root
+ create_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8
+ base_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8
+ - platform: android
+ create_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8
+ base_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8
+ - platform: ios
+ create_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8
+ base_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8
+ - platform: linux
+ create_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8
+ base_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8
+ - platform: macos
+ create_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8
+ base_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8
+ - platform: web
+ create_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8
+ base_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8
+ - platform: windows
+ create_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8
+ base_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8
+
+ # User provided section
+
+ # List of Local paths (relative to this file) that should be
+ # ignored by the migrate tool.
+ #
+ # Files that are not part of the templates will be ignored by default.
+ unmanaged_files:
+ - 'lib/main.dart'
+ - 'ios/Runner.xcodeproj/project.pbxproj'
diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml
index 7bafee68..f438fcd6 100644
--- a/example/analysis_options.yaml
+++ b/example/analysis_options.yaml
@@ -19,9 +19,6 @@
# Android Studio, and the `flutter analyze` command.
analyzer:
- strong-mode:
- implicit-casts: false
- implicit-dynamic: false
errors:
# treat missing required parameters as a warning (not a hint)
missing_required_param: warning
@@ -141,7 +138,7 @@ linter:
# - prefer_constructors_over_static_methods # not yet tested
- prefer_contains
# - prefer_double_quotes # opposite of prefer_single_quotes
- - prefer_equal_for_default_values
+ # - prefer_equal_for_default_values
# - prefer_expression_function_bodies # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods
- prefer_final_fields
- prefer_final_in_for_each
diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml
new file mode 100644
index 00000000..399f6981
--- /dev/null
+++ b/example/android/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/example/android/app/src/main/kotlin/com/fluttercandies/example/MainActivity.kt b/example/android/app/src/main/kotlin/com/fluttercandies/example/MainActivity.kt
new file mode 100644
index 00000000..00b9cec2
--- /dev/null
+++ b/example/android/app/src/main/kotlin/com/fluttercandies/example/MainActivity.kt
@@ -0,0 +1,6 @@
+package com.fluttercandies.example
+
+import io.flutter.embedding.android.FlutterActivity
+
+class MainActivity: FlutterActivity() {
+}
diff --git a/example/android/app/src/profile/AndroidManifest.xml b/example/android/app/src/profile/AndroidManifest.xml
new file mode 100644
index 00000000..399f6981
--- /dev/null
+++ b/example/android/app/src/profile/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/example/android/build.gradle b/example/android/build.gradle
index ea1fb828..5480a15b 100644
--- a/example/android/build.gradle
+++ b/example/android/build.gradle
@@ -27,6 +27,6 @@ subprojects {
project.evaluationDependsOn(':app')
}
-task clean(type: Delete) {
+tasks.register("clean", Delete) {
delete rootProject.buildDir
}
diff --git a/example/ios/.gitignore b/example/ios/.gitignore
index e96ef602..7a7f9873 100644
--- a/example/ios/.gitignore
+++ b/example/ios/.gitignore
@@ -1,3 +1,4 @@
+**/dgph
*.mode1v3
*.mode2v3
*.moved-aside
@@ -18,6 +19,7 @@ Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
+Flutter/ephemeral/
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
diff --git a/example/ios/Flutter/.last_build_id b/example/ios/Flutter/.last_build_id
deleted file mode 100644
index 9a7880a9..00000000
--- a/example/ios/Flutter/.last_build_id
+++ /dev/null
@@ -1 +0,0 @@
-1ee731e3db0f1684eabd1576c09e1bd5
\ No newline at end of file
diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist
index 4f8d4d24..9625e105 100644
--- a/example/ios/Flutter/AppFrameworkInfo.plist
+++ b/example/ios/Flutter/AppFrameworkInfo.plist
@@ -3,7 +3,7 @@
CFBundleDevelopmentRegion
- $(DEVELOPMENT_LANGUAGE)
+ en
CFBundleExecutable
App
CFBundleIdentifier
diff --git a/example/ios/Flutter/Debug.xcconfig b/example/ios/Flutter/Debug.xcconfig
index e8efba11..ec97fc6f 100644
--- a/example/ios/Flutter/Debug.xcconfig
+++ b/example/ios/Flutter/Debug.xcconfig
@@ -1,2 +1,2 @@
-#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
diff --git a/example/ios/Flutter/Release.xcconfig b/example/ios/Flutter/Release.xcconfig
index 399e9340..c4855bfe 100644
--- a/example/ios/Flutter/Release.xcconfig
+++ b/example/ios/Flutter/Release.xcconfig
@@ -1,2 +1,2 @@
-#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
diff --git a/example/ios/Podfile b/example/ios/Podfile
index 093ca345..2c6a9b82 100644
--- a/example/ios/Podfile
+++ b/example/ios/Podfile
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
- platform :ios, '9.1'
+platform :ios, '11.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
@@ -32,6 +32,9 @@ target 'Runner' do
use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
+ target 'RunnerTests' do
+ inherit! :search_paths
+ end
end
post_install do |installer|
diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj
index a1ac90e8..e1947927 100644
--- a/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/example/ios/Runner.xcodeproj/project.pbxproj
@@ -7,15 +7,27 @@
objects = {
/* Begin PBXBuildFile section */
+ 00CE350D9CDC39AF135D10C4 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1C1A4391310D5B80A044529A /* Pods_RunnerTests.framework */; };
+ 0A741DABE762BFBDBFD15B60 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A89B8B9AFAA42CACE4FDEBBD /* Pods_Runner.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 */; };
- 9B6B99532F6B6B0D644D8510 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 04A9794512BCC575968B0FE8 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
+/* Begin PBXContainerItemProxy section */
+ 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 97C146E61CF9000F007C117D /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 97C146ED1CF9000F007C117D;
+ remoteInfo = Runner;
+ };
+/* End PBXContainerItemProxy section */
+
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
@@ -30,10 +42,15 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
- 04A9794512BCC575968B0FE8 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 0913CD44C8612EAEA341C3BF /* 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 = ""; };
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 = ""; };
+ 1C1A4391310D5B80A044529A /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; };
+ 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ 33398257447959A9F2747E92 /* 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 = ""; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
+ 6B3F736569180CF18CEDD993 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
@@ -44,9 +61,10 @@
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 = ""; };
- A2E3B06F8BD464073FBD3A85 /* 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 = ""; };
- AEBFBC047BBB84F0F7577710 /* 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 = ""; };
- F5353890E7DA0DEC049ED2A8 /* 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 = ""; };
+ 9A48409AB147FF74A0553EC9 /* 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 = ""; };
+ A89B8B9AFAA42CACE4FDEBBD /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ BDE2EADABA0BA7EF1ED4AA8E /* 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 = ""; };
+ BE9D07E9BA17D31381DD6952 /* 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 = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -54,29 +72,41 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- 9B6B99532F6B6B0D644D8510 /* Pods_Runner.framework in Frameworks */,
+ 0A741DABE762BFBDBFD15B60 /* Pods_Runner.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ FF289DFC2412B6C24DBA6AC1 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 00CE350D9CDC39AF135D10C4 /* Pods_RunnerTests.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
- 3CDA07FC6A06044EBF54B3B2 /* Pods */ = {
+ 171C9DBC427CED8F7C2A845D /* Pods */ = {
isa = PBXGroup;
children = (
- A2E3B06F8BD464073FBD3A85 /* Pods-Runner.debug.xcconfig */,
- AEBFBC047BBB84F0F7577710 /* Pods-Runner.release.xcconfig */,
- F5353890E7DA0DEC049ED2A8 /* Pods-Runner.profile.xcconfig */,
- );
+ 0913CD44C8612EAEA341C3BF /* Pods-Runner.debug.xcconfig */,
+ 6B3F736569180CF18CEDD993 /* Pods-Runner.release.xcconfig */,
+ 33398257447959A9F2747E92 /* Pods-Runner.profile.xcconfig */,
+ BDE2EADABA0BA7EF1ED4AA8E /* Pods-RunnerTests.debug.xcconfig */,
+ BE9D07E9BA17D31381DD6952 /* Pods-RunnerTests.release.xcconfig */,
+ 9A48409AB147FF74A0553EC9 /* Pods-RunnerTests.profile.xcconfig */,
+ );
+ name = Pods;
path = Pods;
sourceTree = "";
};
- 6130C524CBAA69E77E76D577 /* Frameworks */ = {
+ 331C8082294A63A400263BE5 /* RunnerTests */ = {
isa = PBXGroup;
children = (
- 04A9794512BCC575968B0FE8 /* Pods_Runner.framework */,
+ 331C807B294A618700263BE5 /* RunnerTests.swift */,
);
- name = Frameworks;
+ path = RunnerTests;
sourceTree = "";
};
9740EEB11CF90186004384FC /* Flutter */ = {
@@ -96,8 +126,9 @@
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
- 3CDA07FC6A06044EBF54B3B2 /* Pods */,
- 6130C524CBAA69E77E76D577 /* Frameworks */,
+ 331C8082294A63A400263BE5 /* RunnerTests */,
+ 171C9DBC427CED8F7C2A845D /* Pods */,
+ F9BCA3697581ED7B1106B078 /* Frameworks */,
);
sourceTree = "";
};
@@ -105,6 +136,7 @@
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
+ 331C8081294A63A400263BE5 /* RunnerTests.xctest */,
);
name = Products;
sourceTree = "";
@@ -124,21 +156,49 @@
path = Runner;
sourceTree = "";
};
+ F9BCA3697581ED7B1106B078 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ A89B8B9AFAA42CACE4FDEBBD /* Pods_Runner.framework */,
+ 1C1A4391310D5B80A044529A /* Pods_RunnerTests.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
+ 331C8080294A63A400263BE5 /* RunnerTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
+ buildPhases = (
+ 965F58E44DAB1CAB34993261 /* [CP] Check Pods Manifest.lock */,
+ 331C807D294A63A400263BE5 /* Sources */,
+ 331C807F294A63A400263BE5 /* Resources */,
+ FF289DFC2412B6C24DBA6AC1 /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 331C8086294A63A400263BE5 /* PBXTargetDependency */,
+ );
+ name = RunnerTests;
+ productName = RunnerTests;
+ productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
- 0BF4781BAA765831BD6AA06E /* [CP] Check Pods Manifest.lock */,
+ 2812CC32DBD154B055629B43 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
- AB33336E8A077DE07115D6D1 /* [CP] Embed Pods Frameworks */,
+ 3775EC5A021C67D963D3D265 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@@ -158,6 +218,10 @@
LastUpgradeCheck = 1300;
ORGANIZATIONNAME = "";
TargetAttributes = {
+ 331C8080294A63A400263BE5 = {
+ CreatedOnToolsVersion = 14.0;
+ TestTargetID = 97C146ED1CF9000F007C117D;
+ };
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100;
@@ -178,11 +242,19 @@
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
+ 331C8080294A63A400263BE5 /* RunnerTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
+ 331C807F294A63A400263BE5 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -197,7 +269,7 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
- 0BF4781BAA765831BD6AA06E /* [CP] Check Pods Manifest.lock */ = {
+ 2812CC32DBD154B055629B43 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -219,6 +291,23 @@
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;
};
+ 3775EC5A021C67D963D3D265 /* [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;
+ };
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
@@ -226,6 +315,7 @@
files = (
);
inputPaths = (
+ "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (
@@ -234,41 +324,54 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
- 9740EEB61CF901F6004384FC /* Run Script */ = {
+ 965F58E44DAB1CAB34993261 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
- alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
+ inputFileListPaths = (
+ );
inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
);
- name = "Run Script";
outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
+ 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;
};
- AB33336E8A077DE07115D6D1 /* [CP] Embed Pods Frameworks */ = {
+ 9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
- inputFileListPaths = (
- "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
+ inputPaths = (
);
- name = "[CP] Embed Pods Frameworks";
- outputFileListPaths = (
- "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
+ name = "Run Script";
+ outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
- showEnvVarsInLog = 0;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
+ 331C807D294A63A400263BE5 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -280,6 +383,14 @@
};
/* End PBXSourcesBuildPhase section */
+/* Begin PBXTargetDependency section */
+ 331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 97C146ED1CF9000F007C117D /* Runner */;
+ targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
@@ -357,22 +468,13 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
- DEVELOPMENT_TEAM = 3CJ9HMC222;
ENABLE_BITCODE = NO;
- FRAMEWORK_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
- LIBRARY_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
- PRODUCT_BUNDLE_IDENTIFIER = com.fluttercandies.extendedImageExample;
+ PRODUCT_BUNDLE_IDENTIFIER = com.fluttercandies.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
@@ -380,6 +482,56 @@
};
name = Profile;
};
+ 331C8088294A63A400263BE5 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = BDE2EADABA0BA7EF1ED4AA8E /* Pods-RunnerTests.debug.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.fluttercandies.example.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
+ };
+ name = Debug;
+ };
+ 331C8089294A63A400263BE5 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = BE9D07E9BA17D31381DD6952 /* Pods-RunnerTests.release.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.fluttercandies.example.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
+ };
+ name = Release;
+ };
+ 331C808A294A63A400263BE5 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9A48409AB147FF74A0553EC9 /* Pods-RunnerTests.profile.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.fluttercandies.example.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
+ };
+ name = Profile;
+ };
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -494,22 +646,13 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
- DEVELOPMENT_TEAM = 3CJ9HMC222;
ENABLE_BITCODE = NO;
- FRAMEWORK_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
- LIBRARY_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
- PRODUCT_BUNDLE_IDENTIFIER = com.fluttercandies.extendedImageExample;
+ PRODUCT_BUNDLE_IDENTIFIER = com.fluttercandies.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -525,22 +668,13 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
- DEVELOPMENT_TEAM = 3CJ9HMC222;
ENABLE_BITCODE = NO;
- FRAMEWORK_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
- LIBRARY_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
- PRODUCT_BUNDLE_IDENTIFIER = com.fluttercandies.extendedImageExample;
+ PRODUCT_BUNDLE_IDENTIFIER = com.fluttercandies.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
@@ -551,6 +685,16 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
+ 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 331C8088294A63A400263BE5 /* Debug */,
+ 331C8089294A63A400263BE5 /* Release */,
+ 331C808A294A63A400263BE5 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
index 3db53b6e..e42adcb3 100644
--- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
+++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -27,8 +27,6 @@
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
-
-
-
-
+
+
+
+
+
+
-
-
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
+ CFBundleDisplayName
+ Example
CFBundleExecutable
$(EXECUTABLE_NAME)
CFBundleIdentifier
@@ -11,7 +13,7 @@
CFBundleInfoDictionaryVersion
6.0
CFBundleName
- extended_image_example
+ example
CFBundlePackageType
APPL
CFBundleShortVersionString
@@ -41,15 +43,11 @@
UIViewControllerBasedStatusBarAppearance
- NSPhotoLibraryUsageDescription
- Read your photos for display
- NSCameraUsageDescription
- Take a photo for display
- NSMicrophoneUsageDescription
- Take a video for display
CADisableMinimumFrameDurationOnPhone
UIApplicationSupportsIndirectInputEvents
+ FLTEnableImpeller
+
diff --git a/example/ios/RunnerTests/RunnerTests.swift b/example/ios/RunnerTests/RunnerTests.swift
new file mode 100644
index 00000000..86a7c3b1
--- /dev/null
+++ b/example/ios/RunnerTests/RunnerTests.swift
@@ -0,0 +1,12 @@
+import Flutter
+import UIKit
+import XCTest
+
+class RunnerTests: XCTestCase {
+
+ func testExample() {
+ // If you add code to the Runner application, consider adding tests here.
+ // See https://developer.apple.com/documentation/xctest for more information about using XCTest.
+ }
+
+}
diff --git a/example/lib/common/text/at_text.dart b/example/lib/common/text/at_text.dart
index d80ab3e3..4382be05 100644
--- a/example/lib/common/text/at_text.dart
+++ b/example/lib/common/text/at_text.dart
@@ -1,62 +1,62 @@
-import 'package:extended_text_library/extended_text_library.dart';
-import 'package:flutter/gestures.dart';
-import 'package:flutter/material.dart';
+// import 'package:extended_text_library/extended_text_library.dart';
+// import 'package:flutter/gestures.dart';
+// import 'package:flutter/material.dart';
-class AtText extends SpecialText {
- AtText(TextStyle? textStyle, SpecialTextGestureTapCallback? onTap,
- {this.showAtBackground = false, this.start})
- : super(flag, ' ', textStyle, onTap: onTap);
- static const String flag = '@';
- final int? start;
+// class AtText extends SpecialText {
+// AtText(TextStyle? textStyle, SpecialTextGestureTapCallback? onTap,
+// {this.showAtBackground = false, this.start})
+// : super(flag, ' ', textStyle, onTap: onTap);
+// static const String flag = '@';
+// final int? start;
- /// whether show background for @somebody
- final bool showAtBackground;
+// /// whether show background for @somebody
+// final bool showAtBackground;
- @override
- InlineSpan finishText() {
- final TextStyle? textStyle =
- this.textStyle?.copyWith(color: Colors.blue, fontSize: 16.0);
+// @override
+// InlineSpan finishText() {
+// final TextStyle? textStyle =
+// this.textStyle?.copyWith(color: Colors.blue, fontSize: 16.0);
- final String atText = toString();
+// final String atText = toString();
- return showAtBackground
- ? BackgroundTextSpan(
- background: Paint()..color = Colors.blue.withOpacity(0.15),
- text: atText,
- actualText: atText,
- start: start!,
+// return showAtBackground
+// ? BackgroundTextSpan(
+// background: Paint()..color = Colors.blue.withOpacity(0.15),
+// text: atText,
+// actualText: atText,
+// start: start!,
- ///caret can move into special text
- deleteAll: true,
- style: textStyle,
- recognizer: (TapGestureRecognizer()
- ..onTap = () {
- if (onTap != null) {
- onTap!(atText);
- }
- }))
- : SpecialTextSpan(
- text: atText,
- actualText: atText,
- start: start!,
- style: textStyle,
- recognizer: (TapGestureRecognizer()
- ..onTap = () {
- if (onTap != null) {
- onTap!(atText);
- }
- }));
- }
-}
+// ///caret can move into special text
+// deleteAll: true,
+// style: textStyle,
+// recognizer: (TapGestureRecognizer()
+// ..onTap = () {
+// if (onTap != null) {
+// onTap!(atText);
+// }
+// }))
+// : SpecialTextSpan(
+// text: atText,
+// actualText: atText,
+// start: start!,
+// style: textStyle,
+// recognizer: (TapGestureRecognizer()
+// ..onTap = () {
+// if (onTap != null) {
+// onTap!(atText);
+// }
+// }));
+// }
+// }
-List atList = [
- '@Nevermore ',
- '@Dota2 ',
- '@Biglao ',
- '@艾莉亚·史塔克 ',
- '@丹妮莉丝 ',
- '@HandPulledNoodles ',
- '@Zmtzawqlp ',
- '@FaDeKongJian ',
- '@CaiJingLongDaLao ',
-];
+// List atList = [
+// '@Nevermore ',
+// '@Dota2 ',
+// '@Biglao ',
+// '@艾莉亚·史塔克 ',
+// '@丹妮莉丝 ',
+// '@HandPulledNoodles ',
+// '@Zmtzawqlp ',
+// '@FaDeKongJian ',
+// '@CaiJingLongDaLao ',
+// ];
diff --git a/example/lib/common/text/dollar_text.dart b/example/lib/common/text/dollar_text.dart
index 989feaee..362e898b 100644
--- a/example/lib/common/text/dollar_text.dart
+++ b/example/lib/common/text/dollar_text.dart
@@ -1,43 +1,43 @@
-import 'package:extended_text_library/extended_text_library.dart';
-import 'package:flutter/gestures.dart';
-import 'package:flutter/material.dart';
+// import 'package:extended_text_library/extended_text_library.dart';
+// import 'package:flutter/gestures.dart';
+// import 'package:flutter/material.dart';
-class DollarText extends SpecialText {
- DollarText(TextStyle? textStyle, SpecialTextGestureTapCallback? onTap,
- {this.start})
- : super(flag, flag, textStyle, onTap: onTap);
- static const String flag = '\$';
- final int? start;
- @override
- InlineSpan finishText() {
- final String text = getContent();
+// class DollarText extends SpecialText {
+// DollarText(TextStyle? textStyle, SpecialTextGestureTapCallback? onTap,
+// {this.start})
+// : super(flag, flag, textStyle, onTap: onTap);
+// static const String flag = '\$';
+// final int? start;
+// @override
+// InlineSpan finishText() {
+// final String text = getContent();
- return SpecialTextSpan(
- text: text,
- actualText: toString(),
- start: start!,
+// return SpecialTextSpan(
+// text: text,
+// actualText: toString(),
+// start: start!,
- ///caret can move into special text
- deleteAll: true,
- style: textStyle?.copyWith(color: Colors.orange),
- recognizer: TapGestureRecognizer()
- ..onTap = () {
- if (onTap != null) {
- onTap!(toString());
- }
- });
- }
-}
+// ///caret can move into special text
+// deleteAll: true,
+// style: textStyle?.copyWith(color: Colors.orange),
+// recognizer: TapGestureRecognizer()
+// ..onTap = () {
+// if (onTap != null) {
+// onTap!(toString());
+// }
+// });
+// }
+// }
-List dollarList = [
- '\$Dota2\$',
- '\$Dota2 Ti9\$',
- '\$CN dota best dota\$',
- '\$Flutter\$',
- '\$CN dev best dev\$',
- '\$UWP\$',
- '\$Nevermore\$',
- '\$FlutterCandies\$',
- '\$ExtendedImage\$',
- '\$ExtendedText\$',
-];
+// List dollarList = [
+// '\$Dota2\$',
+// '\$Dota2 Ti9\$',
+// '\$CN dota best dota\$',
+// '\$Flutter\$',
+// '\$CN dev best dev\$',
+// '\$UWP\$',
+// '\$Nevermore\$',
+// '\$FlutterCandies\$',
+// '\$ExtendedImage\$',
+// '\$ExtendedText\$',
+// ];
diff --git a/example/lib/common/text/emoji_text.dart b/example/lib/common/text/emoji_text.dart
index eb79a939..705a78e1 100644
--- a/example/lib/common/text/emoji_text.dart
+++ b/example/lib/common/text/emoji_text.dart
@@ -1,53 +1,53 @@
-import 'package:extended_text_library/extended_text_library.dart';
-import 'package:flutter/material.dart';
-
-///emoji/image text
-class EmojiText extends SpecialText {
- EmojiText(TextStyle textStyle, {this.start})
- : super(EmojiText.flag, ']', textStyle);
- static const String flag = '[';
- final int? start;
- @override
- InlineSpan finishText() {
- final String key = toString();
-
- ///https://github.com/flutter/flutter/issues/42086
- /// widget span is not working on web
- if (EmojiUitl.instance.emojiMap.containsKey(key)) {
- //fontsize id define image height
- //size = 30.0/26.0 * fontSize
- const double size = 20.0;
-
- ///fontSize 26 and text height =30.0
- //final double fontSize = 26.0;
- return ImageSpan(
- AssetImage(
- EmojiUitl.instance.emojiMap[key]!,
- ),
- actualText: key,
- imageWidth: size,
- imageHeight: size,
- start: start!,
- fit: BoxFit.fill,
- margin: const EdgeInsets.only(left: 2.0, top: 2.0, right: 2.0));
- }
-
- return TextSpan(text: toString(), style: textStyle);
- }
-}
-
-class EmojiUitl {
- EmojiUitl._() {
- _emojiMap['[love]'] = '$_emojiFilePath/love.png';
- _emojiMap['[sun_glasses]'] = '$_emojiFilePath/sun_glasses.png';
- }
-
- final Map _emojiMap = {};
-
- Map get emojiMap => _emojiMap;
-
- final String _emojiFilePath = 'assets';
-
- static EmojiUitl? _instance;
- static EmojiUitl get instance => _instance ??= EmojiUitl._();
-}
+// import 'package:extended_text_library/extended_text_library.dart';
+// import 'package:flutter/material.dart';
+
+// ///emoji/image text
+// class EmojiText extends SpecialText {
+// EmojiText(TextStyle textStyle, {this.start})
+// : super(EmojiText.flag, ']', textStyle);
+// static const String flag = '[';
+// final int? start;
+// @override
+// InlineSpan finishText() {
+// final String key = toString();
+
+// ///https://github.com/flutter/flutter/issues/42086
+// /// widget span is not working on web
+// if (EmojiUitl.instance.emojiMap.containsKey(key)) {
+// //fontsize id define image height
+// //size = 30.0/26.0 * fontSize
+// const double size = 20.0;
+
+// ///fontSize 26 and text height =30.0
+// //final double fontSize = 26.0;
+// return ImageSpan(
+// AssetImage(
+// EmojiUitl.instance.emojiMap[key]!,
+// ),
+// actualText: key,
+// imageWidth: size,
+// imageHeight: size,
+// start: start!,
+// fit: BoxFit.fill,
+// margin: const EdgeInsets.only(left: 2.0, top: 2.0, right: 2.0));
+// }
+
+// return TextSpan(text: toString(), style: textStyle);
+// }
+// }
+
+// class EmojiUitl {
+// EmojiUitl._() {
+// _emojiMap['[love]'] = '$_emojiFilePath/love.png';
+// _emojiMap['[sun_glasses]'] = '$_emojiFilePath/sun_glasses.png';
+// }
+
+// final Map _emojiMap = {};
+
+// Map get emojiMap => _emojiMap;
+
+// final String _emojiFilePath = 'assets';
+
+// static EmojiUitl? _instance;
+// static EmojiUitl get instance => _instance ??= EmojiUitl._();
+// }
diff --git a/example/lib/common/text/my_extended_text_selection_controls.dart b/example/lib/common/text/my_extended_text_selection_controls.dart
index 95c91691..72c7bc85 100644
--- a/example/lib/common/text/my_extended_text_selection_controls.dart
+++ b/example/lib/common/text/my_extended_text_selection_controls.dart
@@ -1,313 +1,313 @@
-import 'dart:math' as math;
-import 'package:extended_text_library/extended_text_library.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter/services.dart';
-import 'package:url_launcher/url_launcher.dart';
+// import 'dart:math' as math;
+// import 'package:extended_text_library/extended_text_library.dart';
+// import 'package:flutter/material.dart';
+// import 'package:flutter/services.dart';
+// import 'package:url_launcher/url_launcher.dart';
-///
-/// create by zmtzawqlp on 2019/8/3
-///
+// ///
+// /// create by zmtzawqlp on 2019/8/3
+// ///
-const double _kHandleSize = 22.0;
+// const double _kHandleSize = 22.0;
-// Padding between the toolbar and the anchor.
-const double _kToolbarContentDistanceBelow = _kHandleSize - 2.0;
-const double _kToolbarContentDistance = 8.0;
+// // Padding between the toolbar and the anchor.
+// const double _kToolbarContentDistanceBelow = _kHandleSize - 2.0;
+// const double _kToolbarContentDistance = 8.0;
-/// Android Material styled text selection controls.
-class MyTextSelectionControls extends TextSelectionControls {
- MyTextSelectionControls({this.joinZeroWidthSpace = false});
- final bool joinZeroWidthSpace;
+// /// Android Material styled text selection controls.
+// class MyTextSelectionControls extends TextSelectionControls {
+// MyTextSelectionControls({this.joinZeroWidthSpace = false});
+// final bool joinZeroWidthSpace;
- @override
- void handleCopy(TextSelectionDelegate delegate,
- [ClipboardStatusNotifier? clipboardStatus]) {
- final TextEditingValue value = delegate.textEditingValue;
+// @override
+// void handleCopy(TextSelectionDelegate delegate,
+// [ClipboardStatusNotifier? clipboardStatus]) {
+// final TextEditingValue value = delegate.textEditingValue;
- String data = value.selection.textInside(value.text);
- // remove zeroWidthSpace
- if (joinZeroWidthSpace) {
- data = data.replaceAll(zeroWidthSpace, '');
- }
- Clipboard.setData(ClipboardData(
- text: value.selection.textInside(value.text),
- ));
- clipboardStatus?.update();
- delegate.userUpdateTextEditingValue(
- TextEditingValue(
- text: value.text,
- selection: TextSelection.collapsed(offset: value.selection.end),
- ),
- SelectionChangedCause.toolbar,
- );
- delegate.bringIntoView(delegate.textEditingValue.selection.extent);
- delegate.hideToolbar();
- }
+// String data = value.selection.textInside(value.text);
+// // remove zeroWidthSpace
+// if (joinZeroWidthSpace) {
+// data = data.replaceAll(zeroWidthSpace, '');
+// }
+// Clipboard.setData(ClipboardData(
+// text: value.selection.textInside(value.text),
+// ));
+// clipboardStatus?.update();
+// delegate.userUpdateTextEditingValue(
+// TextEditingValue(
+// text: value.text,
+// selection: TextSelection.collapsed(offset: value.selection.end),
+// ),
+// SelectionChangedCause.toolbar,
+// );
+// delegate.bringIntoView(delegate.textEditingValue.selection.extent);
+// delegate.hideToolbar();
+// }
- /// Returns the size of the Material handle.
- @override
- Size getHandleSize(double textLineHeight) =>
- const Size(_kHandleSize, _kHandleSize);
+// /// Returns the size of the Material handle.
+// @override
+// Size getHandleSize(double textLineHeight) =>
+// const Size(_kHandleSize, _kHandleSize);
- /// Builder for material-style copy/paste text selection toolbar.
- @override
- Widget buildToolbar(
- BuildContext context,
- Rect globalEditableRegion,
- double textLineHeight,
- Offset selectionMidpoint,
- List endpoints,
- TextSelectionDelegate delegate,
- ClipboardStatusNotifier? clipboardStatus,
- Offset? lastSecondaryTapDownPosition,
- ) {
- return _TextSelectionControlsToolbar(
- globalEditableRegion: globalEditableRegion,
- textLineHeight: textLineHeight,
- selectionMidpoint: selectionMidpoint,
- endpoints: endpoints,
- delegate: delegate,
- clipboardStatus: clipboardStatus,
- handleCut: canCut(delegate) ? () => handleCut(delegate, null) : null,
- handleCopy: canCopy(delegate) ? () => handleCopy(delegate) : null,
- handlePaste: canPaste(delegate) ? () => handlePaste(delegate) : null,
- handleSelectAll:
- canSelectAll(delegate) ? () => handleSelectAll(delegate) : null,
- handleLike: () {
- launchUrl(Uri.parse(
- 'mailto:zmtzawqlp@live.com?subject=extended_text_share&body=${delegate.textEditingValue.text}'));
- delegate.hideToolbar();
- delegate.userUpdateTextEditingValue(
- delegate.textEditingValue
- .copyWith(selection: const TextSelection.collapsed(offset: 0)),
- SelectionChangedCause.toolbar,
- );
- },
- );
- }
+// /// Builder for material-style copy/paste text selection toolbar.
+// @override
+// Widget buildToolbar(
+// BuildContext context,
+// Rect globalEditableRegion,
+// double textLineHeight,
+// Offset selectionMidpoint,
+// List endpoints,
+// TextSelectionDelegate delegate,
+// ClipboardStatusNotifier? clipboardStatus,
+// Offset? lastSecondaryTapDownPosition,
+// ) {
+// return _TextSelectionControlsToolbar(
+// globalEditableRegion: globalEditableRegion,
+// textLineHeight: textLineHeight,
+// selectionMidpoint: selectionMidpoint,
+// endpoints: endpoints,
+// delegate: delegate,
+// clipboardStatus: clipboardStatus,
+// handleCut: canCut(delegate) ? () => handleCut(delegate, null) : null,
+// handleCopy: canCopy(delegate) ? () => handleCopy(delegate) : null,
+// handlePaste: canPaste(delegate) ? () => handlePaste(delegate) : null,
+// handleSelectAll:
+// canSelectAll(delegate) ? () => handleSelectAll(delegate) : null,
+// handleLike: () {
+// launchUrl(Uri.parse(
+// 'mailto:zmtzawqlp@live.com?subject=extended_text_share&body=${delegate.textEditingValue.text}'));
+// delegate.hideToolbar();
+// delegate.userUpdateTextEditingValue(
+// delegate.textEditingValue
+// .copyWith(selection: const TextSelection.collapsed(offset: 0)),
+// SelectionChangedCause.toolbar,
+// );
+// },
+// );
+// }
- /// Builder for material-style text selection handles.
- @override
- Widget buildHandle(
- BuildContext context, TextSelectionHandleType type, double textLineHeight,
- [VoidCallback? onTap, double? startGlyphHeight, double? endGlyphHeight]) {
- final Widget handle = SizedBox(
- width: _kHandleSize,
- height: _kHandleSize,
- child: Image.asset(
- 'assets/40.png',
- ),
- );
+// /// Builder for material-style text selection handles.
+// @override
+// Widget buildHandle(
+// BuildContext context, TextSelectionHandleType type, double textLineHeight,
+// [VoidCallback? onTap, double? startGlyphHeight, double? endGlyphHeight]) {
+// final Widget handle = SizedBox(
+// width: _kHandleSize,
+// height: _kHandleSize,
+// child: Image.asset(
+// 'assets/40.png',
+// ),
+// );
- // [handle] is a circle, with a rectangle in the top left quadrant of that
- // circle (an onion pointing to 10:30). We rotate [handle] to point
- // straight up or up-right depending on the handle type.
- switch (type) {
- case TextSelectionHandleType.left: // points up-right
- return Transform.rotate(
- angle: math.pi / 4.0,
- child: handle,
- );
- case TextSelectionHandleType.right: // points up-left
- return Transform.rotate(
- angle: -math.pi / 4.0,
- child: handle,
- );
- case TextSelectionHandleType.collapsed: // points up
- return handle;
- }
- }
+// // [handle] is a circle, with a rectangle in the top left quadrant of that
+// // circle (an onion pointing to 10:30). We rotate [handle] to point
+// // straight up or up-right depending on the handle type.
+// switch (type) {
+// case TextSelectionHandleType.left: // points up-right
+// return Transform.rotate(
+// angle: math.pi / 4.0,
+// child: handle,
+// );
+// case TextSelectionHandleType.right: // points up-left
+// return Transform.rotate(
+// angle: -math.pi / 4.0,
+// child: handle,
+// );
+// case TextSelectionHandleType.collapsed: // points up
+// return handle;
+// }
+// }
- /// Gets anchor for material-style text selection handles.
- ///
- /// See [TextSelectionControls.getHandleAnchor].
- @override
- Offset getHandleAnchor(TextSelectionHandleType type, double textLineHeight,
- [double? startGlyphHeight, double? endGlyphHeight]) {
- switch (type) {
- case TextSelectionHandleType.left:
- return const Offset(_kHandleSize, 0);
- case TextSelectionHandleType.right:
- return Offset.zero;
- default:
- return const Offset(_kHandleSize / 2, -4);
- }
- }
+// /// Gets anchor for material-style text selection handles.
+// ///
+// /// See [TextSelectionControls.getHandleAnchor].
+// @override
+// Offset getHandleAnchor(TextSelectionHandleType type, double textLineHeight,
+// [double? startGlyphHeight, double? endGlyphHeight]) {
+// switch (type) {
+// case TextSelectionHandleType.left:
+// return const Offset(_kHandleSize, 0);
+// case TextSelectionHandleType.right:
+// return Offset.zero;
+// default:
+// return const Offset(_kHandleSize / 2, -4);
+// }
+// }
- @override
- bool canSelectAll(TextSelectionDelegate delegate) {
- // Android allows SelectAll when selection is not collapsed, unless
- // everything has already been selected.
- final TextEditingValue value = delegate.textEditingValue;
- return delegate.selectAllEnabled &&
- value.text.isNotEmpty &&
- !(value.selection.start == 0 &&
- value.selection.end == value.text.length);
- }
-}
+// @override
+// bool canSelectAll(TextSelectionDelegate delegate) {
+// // Android allows SelectAll when selection is not collapsed, unless
+// // everything has already been selected.
+// final TextEditingValue value = delegate.textEditingValue;
+// return delegate.selectAllEnabled &&
+// value.text.isNotEmpty &&
+// !(value.selection.start == 0 &&
+// value.selection.end == value.text.length);
+// }
+// }
-// The label and callback for the available default text selection menu buttons.
-class _TextSelectionToolbarItemData {
- const _TextSelectionToolbarItemData({
- required this.label,
- required this.onPressed,
- });
+// // The label and callback for the available default text selection menu buttons.
+// class _TextSelectionToolbarItemData {
+// const _TextSelectionToolbarItemData({
+// required this.label,
+// required this.onPressed,
+// });
- final String label;
- final VoidCallback? onPressed;
-}
+// final String label;
+// final VoidCallback? onPressed;
+// }
-// The highest level toolbar widget, built directly by buildToolbar.
-class _TextSelectionControlsToolbar extends StatefulWidget {
- const _TextSelectionControlsToolbar({
- required this.clipboardStatus,
- required this.delegate,
- required this.endpoints,
- required this.globalEditableRegion,
- required this.handleCut,
- required this.handleCopy,
- required this.handlePaste,
- required this.handleSelectAll,
- required this.selectionMidpoint,
- required this.textLineHeight,
- required this.handleLike,
- });
+// // The highest level toolbar widget, built directly by buildToolbar.
+// class _TextSelectionControlsToolbar extends StatefulWidget {
+// const _TextSelectionControlsToolbar({
+// required this.clipboardStatus,
+// required this.delegate,
+// required this.endpoints,
+// required this.globalEditableRegion,
+// required this.handleCut,
+// required this.handleCopy,
+// required this.handlePaste,
+// required this.handleSelectAll,
+// required this.selectionMidpoint,
+// required this.textLineHeight,
+// required this.handleLike,
+// });
- final ClipboardStatusNotifier? clipboardStatus;
- final TextSelectionDelegate delegate;
- final List endpoints;
- final Rect globalEditableRegion;
- final VoidCallback? handleCut;
- final VoidCallback? handleCopy;
- final VoidCallback? handlePaste;
- final VoidCallback? handleSelectAll;
- final VoidCallback? handleLike;
- final Offset selectionMidpoint;
- final double textLineHeight;
+// final ClipboardStatusNotifier? clipboardStatus;
+// final TextSelectionDelegate delegate;
+// final List endpoints;
+// final Rect globalEditableRegion;
+// final VoidCallback? handleCut;
+// final VoidCallback? handleCopy;
+// final VoidCallback? handlePaste;
+// final VoidCallback? handleSelectAll;
+// final VoidCallback? handleLike;
+// final Offset selectionMidpoint;
+// final double textLineHeight;
- @override
- _TextSelectionControlsToolbarState createState() =>
- _TextSelectionControlsToolbarState();
-}
+// @override
+// _TextSelectionControlsToolbarState createState() =>
+// _TextSelectionControlsToolbarState();
+// }
-class _TextSelectionControlsToolbarState
- extends State<_TextSelectionControlsToolbar> with TickerProviderStateMixin {
- void _onChangedClipboardStatus() {
- setState(() {
- // Inform the widget that the value of clipboardStatus has changed.
- });
- }
+// class _TextSelectionControlsToolbarState
+// extends State<_TextSelectionControlsToolbar> with TickerProviderStateMixin {
+// void _onChangedClipboardStatus() {
+// setState(() {
+// // Inform the widget that the value of clipboardStatus has changed.
+// });
+// }
- @override
- void initState() {
- super.initState();
- widget.clipboardStatus?.addListener(_onChangedClipboardStatus);
- }
+// @override
+// void initState() {
+// super.initState();
+// widget.clipboardStatus?.addListener(_onChangedClipboardStatus);
+// }
- @override
- void didUpdateWidget(_TextSelectionControlsToolbar oldWidget) {
- super.didUpdateWidget(oldWidget);
- if (widget.clipboardStatus != oldWidget.clipboardStatus) {
- widget.clipboardStatus?.addListener(_onChangedClipboardStatus);
- oldWidget.clipboardStatus?.removeListener(_onChangedClipboardStatus);
- }
- }
+// @override
+// void didUpdateWidget(_TextSelectionControlsToolbar oldWidget) {
+// super.didUpdateWidget(oldWidget);
+// if (widget.clipboardStatus != oldWidget.clipboardStatus) {
+// widget.clipboardStatus?.addListener(_onChangedClipboardStatus);
+// oldWidget.clipboardStatus?.removeListener(_onChangedClipboardStatus);
+// }
+// }
- @override
- void dispose() {
- super.dispose();
- widget.clipboardStatus?.removeListener(_onChangedClipboardStatus);
- }
+// @override
+// void dispose() {
+// super.dispose();
+// widget.clipboardStatus?.removeListener(_onChangedClipboardStatus);
+// }
- @override
- Widget build(BuildContext context) {
- // If there are no buttons to be shown, don't render anything.
- if (widget.handleCut == null &&
- widget.handleCopy == null &&
- widget.handlePaste == null &&
- widget.handleSelectAll == null) {
- return const SizedBox.shrink();
- }
- // If the paste button is desired, don't render anything until the state of
- // the clipboard is known, since it's used to determine if paste is shown.
- if (widget.handlePaste != null &&
- widget.clipboardStatus?.value == ClipboardStatus.unknown) {
- return const SizedBox.shrink();
- }
+// @override
+// Widget build(BuildContext context) {
+// // If there are no buttons to be shown, don't render anything.
+// if (widget.handleCut == null &&
+// widget.handleCopy == null &&
+// widget.handlePaste == null &&
+// widget.handleSelectAll == null) {
+// return const SizedBox.shrink();
+// }
+// // If the paste button is desired, don't render anything until the state of
+// // the clipboard is known, since it's used to determine if paste is shown.
+// if (widget.handlePaste != null &&
+// widget.clipboardStatus?.value == ClipboardStatus.unknown) {
+// return const SizedBox.shrink();
+// }
- // Calculate the positioning of the menu. It is placed above the selection
- // if there is enough room, or otherwise below.
- final TextSelectionPoint startTextSelectionPoint = widget.endpoints[0];
- final TextSelectionPoint endTextSelectionPoint =
- widget.endpoints.length > 1 ? widget.endpoints[1] : widget.endpoints[0];
- final Offset anchorAbove = Offset(
- widget.globalEditableRegion.left + widget.selectionMidpoint.dx,
- widget.globalEditableRegion.top +
- startTextSelectionPoint.point.dy -
- widget.textLineHeight -
- _kToolbarContentDistance,
- );
- final Offset anchorBelow = Offset(
- widget.globalEditableRegion.left + widget.selectionMidpoint.dx,
- widget.globalEditableRegion.top +
- endTextSelectionPoint.point.dy +
- _kToolbarContentDistanceBelow,
- );
+// // Calculate the positioning of the menu. It is placed above the selection
+// // if there is enough room, or otherwise below.
+// final TextSelectionPoint startTextSelectionPoint = widget.endpoints[0];
+// final TextSelectionPoint endTextSelectionPoint =
+// widget.endpoints.length > 1 ? widget.endpoints[1] : widget.endpoints[0];
+// final Offset anchorAbove = Offset(
+// widget.globalEditableRegion.left + widget.selectionMidpoint.dx,
+// widget.globalEditableRegion.top +
+// startTextSelectionPoint.point.dy -
+// widget.textLineHeight -
+// _kToolbarContentDistance,
+// );
+// final Offset anchorBelow = Offset(
+// widget.globalEditableRegion.left + widget.selectionMidpoint.dx,
+// widget.globalEditableRegion.top +
+// endTextSelectionPoint.point.dy +
+// _kToolbarContentDistanceBelow,
+// );
- // Determine which buttons will appear so that the order and total number is
- // known. A button's position in the menu can slightly affect its
- // appearance.
- assert(debugCheckHasMaterialLocalizations(context));
- final MaterialLocalizations localizations =
- MaterialLocalizations.of(context);
- final List<_TextSelectionToolbarItemData> itemDatas =
- <_TextSelectionToolbarItemData>[
- if (widget.handleCut != null)
- _TextSelectionToolbarItemData(
- label: localizations.cutButtonLabel,
- onPressed: widget.handleCut!,
- ),
- if (widget.handleCopy != null)
- _TextSelectionToolbarItemData(
- label: localizations.copyButtonLabel,
- onPressed: widget.handleCopy!,
- ),
- if (widget.handlePaste != null &&
- widget.clipboardStatus?.value == ClipboardStatus.pasteable)
- _TextSelectionToolbarItemData(
- label: localizations.pasteButtonLabel,
- onPressed: widget.handlePaste!,
- ),
- if (widget.handleSelectAll != null)
- _TextSelectionToolbarItemData(
- label: localizations.selectAllButtonLabel,
- onPressed: widget.handleSelectAll!,
- ),
- _TextSelectionToolbarItemData(
- label: 'like',
- onPressed: widget.handleLike,
- ),
- ];
+// // Determine which buttons will appear so that the order and total number is
+// // known. A button's position in the menu can slightly affect its
+// // appearance.
+// assert(debugCheckHasMaterialLocalizations(context));
+// final MaterialLocalizations localizations =
+// MaterialLocalizations.of(context);
+// final List<_TextSelectionToolbarItemData> itemDatas =
+// <_TextSelectionToolbarItemData>[
+// if (widget.handleCut != null)
+// _TextSelectionToolbarItemData(
+// label: localizations.cutButtonLabel,
+// onPressed: widget.handleCut!,
+// ),
+// if (widget.handleCopy != null)
+// _TextSelectionToolbarItemData(
+// label: localizations.copyButtonLabel,
+// onPressed: widget.handleCopy!,
+// ),
+// if (widget.handlePaste != null &&
+// widget.clipboardStatus?.value == ClipboardStatus.pasteable)
+// _TextSelectionToolbarItemData(
+// label: localizations.pasteButtonLabel,
+// onPressed: widget.handlePaste!,
+// ),
+// if (widget.handleSelectAll != null)
+// _TextSelectionToolbarItemData(
+// label: localizations.selectAllButtonLabel,
+// onPressed: widget.handleSelectAll!,
+// ),
+// _TextSelectionToolbarItemData(
+// label: 'like',
+// onPressed: widget.handleLike,
+// ),
+// ];
- // If there is no option available, build an empty widget.
- if (itemDatas.isEmpty) {
- return const SizedBox(width: 0.0, height: 0.0);
- }
+// // If there is no option available, build an empty widget.
+// if (itemDatas.isEmpty) {
+// return const SizedBox(width: 0.0, height: 0.0);
+// }
- return TextSelectionToolbar(
- anchorAbove: anchorAbove,
- anchorBelow: anchorBelow,
- children: itemDatas
- .asMap()
- .entries
- .map((MapEntry entry) {
- return TextSelectionToolbarTextButton(
- padding: TextSelectionToolbarTextButton.getPadding(
- entry.key, itemDatas.length),
- onPressed: entry.value.onPressed,
- child: Text(entry.value.label),
- );
- }).toList(),
- );
- }
-}
+// return TextSelectionToolbar(
+// anchorAbove: anchorAbove,
+// anchorBelow: anchorBelow,
+// children: itemDatas
+// .asMap()
+// .entries
+// .map((MapEntry entry) {
+// return TextSelectionToolbarTextButton(
+// padding: TextSelectionToolbarTextButton.getPadding(
+// entry.key, itemDatas.length),
+// onPressed: entry.value.onPressed,
+// child: Text(entry.value.label),
+// );
+// }).toList(),
+// );
+// }
+// }
diff --git a/example/lib/common/text/my_special_text_span_builder.dart b/example/lib/common/text/my_special_text_span_builder.dart
index db2c8705..c7e93eae 100644
--- a/example/lib/common/text/my_special_text_span_builder.dart
+++ b/example/lib/common/text/my_special_text_span_builder.dart
@@ -1,39 +1,39 @@
-import 'package:extended_text_library/extended_text_library.dart';
-import 'package:flutter/material.dart';
+// import 'package:extended_text_library/extended_text_library.dart';
+// import 'package:flutter/material.dart';
-import 'at_text.dart';
-import 'dollar_text.dart';
-import 'emoji_text.dart';
+// import 'at_text.dart';
+// import 'dollar_text.dart';
+// import 'emoji_text.dart';
-class MySpecialTextSpanBuilder extends SpecialTextSpanBuilder {
- MySpecialTextSpanBuilder({this.showAtBackground = false});
+// class MySpecialTextSpanBuilder extends SpecialTextSpanBuilder {
+// MySpecialTextSpanBuilder({this.showAtBackground = false});
- /// whether show background for @somebody
- final bool showAtBackground;
+// /// whether show background for @somebody
+// final bool showAtBackground;
- @override
- SpecialText? createSpecialText(String flag,
- {TextStyle? textStyle,
- SpecialTextGestureTapCallback? onTap,
- int? index}) {
- if (flag == '') {
- return null;
- }
+// @override
+// SpecialText? createSpecialText(String flag,
+// {TextStyle? textStyle,
+// SpecialTextGestureTapCallback? onTap,
+// int? index}) {
+// if (flag == '') {
+// return null;
+// }
- ///index is end index of start flag, so text start index should be index-(flag.length-1)
- if (isStart(flag, AtText.flag)) {
- return AtText(
- textStyle!,
- onTap,
- start: index! - (AtText.flag.length - 1),
- showAtBackground: showAtBackground,
- );
- } else if (isStart(flag, EmojiText.flag)) {
- return EmojiText(textStyle!, start: index! - (EmojiText.flag.length - 1));
- } else if (isStart(flag, DollarText.flag)) {
- return DollarText(textStyle!, onTap,
- start: index! - (DollarText.flag.length - 1));
- }
- return null;
- }
-}
+// ///index is end index of start flag, so text start index should be index-(flag.length-1)
+// if (isStart(flag, AtText.flag)) {
+// return AtText(
+// textStyle!,
+// onTap,
+// start: index! - (AtText.flag.length - 1),
+// showAtBackground: showAtBackground,
+// );
+// } else if (isStart(flag, EmojiText.flag)) {
+// return EmojiText(textStyle!, start: index! - (EmojiText.flag.length - 1));
+// } else if (isStart(flag, DollarText.flag)) {
+// return DollarText(textStyle!, onTap,
+// start: index! - (DollarText.flag.length - 1));
+// }
+// return null;
+// }
+// }
diff --git a/example/lib/common/widget/memory_usage_chart.dart b/example/lib/common/widget/memory_usage_chart.dart
index add727e6..4ae2bb28 100644
--- a/example/lib/common/widget/memory_usage_chart.dart
+++ b/example/lib/common/widget/memory_usage_chart.dart
@@ -1,5 +1,4 @@
import 'dart:math';
-import 'dart:ui';
import 'package:example/common/utils/vm_helper.dart';
import 'package:fl_chart/fl_chart.dart';
@@ -37,7 +36,7 @@ class _MemoryUsageChartState extends State {
}
return Container(
padding: const EdgeInsets.only(left: 30, right: 30, top: 20, bottom: 5),
- width: window.physicalSize.width,
+ width: View.of(context).physicalSize.width,
height: 150,
child: LineChart(
getData(),
diff --git a/example/lib/common/widget/memory_usage_view.dart b/example/lib/common/widget/memory_usage_view.dart
index bfb916be..00a8f33d 100644
--- a/example/lib/common/widget/memory_usage_view.dart
+++ b/example/lib/common/widget/memory_usage_view.dart
@@ -16,9 +16,14 @@ class _MemoryUsageViewState extends State {
void initState() {
super.initState();
VMHelper().addListener(_updateMemoryUsage);
+ }
- _top = window.physicalSize.height / window.devicePixelRatio / 2 - 80;
- _left = window.physicalSize.width / window.devicePixelRatio / 2 - 40;
+ @override
+ void didChangeDependencies() {
+ final FlutterView view = View.of(context);
+ _top = view.physicalSize.height / view.devicePixelRatio / 2 - 80;
+ _left = view.physicalSize.width / view.devicePixelRatio / 2 - 40;
+ super.didChangeDependencies();
}
void _updateMemoryUsage() {
diff --git a/example/lib/common/widget/pic_swiper.dart b/example/lib/common/widget/pic_swiper.dart
index 13c6f7e8..d2a5e764 100644
--- a/example/lib/common/widget/pic_swiper.dart
+++ b/example/lib/common/widget/pic_swiper.dart
@@ -5,16 +5,16 @@ import 'dart:math';
import 'package:example/common/data/tu_chong_source.dart' hide asT;
@FFArgumentImport()
import 'package:example/common/model/pic_swiper_item.dart';
-import 'package:example/common/text/my_extended_text_selection_controls.dart';
-import 'package:example/common/text/my_special_text_span_builder.dart';
+// import 'package:example/common/text/my_extended_text_selection_controls.dart';
+// import 'package:example/common/text/my_special_text_span_builder.dart';
import 'package:example/common/utils/util.dart';
import 'package:extended_image/extended_image.dart';
-import 'package:extended_text/extended_text.dart';
+//import 'package:extended_text/extended_text.dart';
import 'package:ff_annotation_route_library/ff_annotation_route_library.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart' hide Image;
import 'package:oktoast/oktoast.dart';
-import 'package:url_launcher/url_launcher.dart';
+// import 'package:url_launcher/url_launcher.dart';
import 'hero.dart';
import 'item_builder.dart';
@@ -83,43 +83,43 @@ class ImageDetail extends StatelessWidget {
const SizedBox(
height: 15.0,
),
- ExtendedText(
+ Text(
content,
- onSpecialTextTap: (dynamic parameter) {
- if (parameter.toString().startsWith('\$')) {
- launchUrl(Uri.parse('https://github.com/fluttercandies'));
- } else if (parameter.toString().startsWith('@')) {
- launchUrl(Uri.parse('mailto:zmtzawqlp@live.com'));
- }
- },
- specialTextSpanBuilder: MySpecialTextSpanBuilder(),
+ // onSpecialTextTap: (dynamic parameter) {
+ // if (parameter.toString().startsWith('\$')) {
+ // launchUrl(Uri.parse('https://github.com/fluttercandies'));
+ // } else if (parameter.toString().startsWith('@')) {
+ // launchUrl(Uri.parse('mailto:zmtzawqlp@live.com'));
+ // }
+ // },
+ // specialTextSpanBuilder: MySpecialTextSpanBuilder(),
//overflow: ExtendedTextOverflow.ellipsis,
style: const TextStyle(fontSize: 14, color: Colors.grey),
maxLines: 10,
- overflowWidget: TextOverflowWidget(
- //maxHeight: double.infinity,
- //align: TextOverflowAlign.right,
- //fixedOffset: Offset.zero,
- //debugOverflowRectColor: Colors.red,
- child: DefaultTextStyle(
- style: const TextStyle(fontSize: 12, color: Colors.blue),
- child: Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- const Text('\u2026 '),
- GestureDetector(
- child: const Text('more'),
- onTap: () {
- launchUrl(Uri.parse(
- 'https://github.com/fluttercandies/extended_text'));
- },
- )
- ],
- ),
- ),
- ),
- selectionEnabled: true,
- selectionControls: MyTextSelectionControls(),
+ // overflowWidget: TextOverflowWidget(
+ // //maxHeight: double.infinity,
+ // //align: TextOverflowAlign.right,
+ // //fixedOffset: Offset.zero,
+ // //debugOverflowRectColor: Colors.red,
+ // child: DefaultTextStyle(
+ // style: const TextStyle(fontSize: 12, color: Colors.blue),
+ // child: Row(
+ // mainAxisSize: MainAxisSize.min,
+ // children: [
+ // const Text('\u2026 '),
+ // GestureDetector(
+ // child: const Text('more'),
+ // onTap: () {
+ // launchUrl(Uri.parse(
+ // 'https://github.com/fluttercandies/extended_text'));
+ // },
+ // )
+ // ],
+ // ),
+ // ),
+ // ),
+ // selectionEnabled: true,
+ // selectionControls: MyTextSelectionControls(),
),
const SizedBox(
height: 20.0,
@@ -149,7 +149,7 @@ class ImageDetail extends StatelessWidget {
'${tuChongItem?.imageSize.width.toInt()} * ${tuChongItem?.imageSize.height.toInt()}',
),
),
- Positioned(
+ const Positioned(
top: -33.0,
right: 0,
left: 0,
@@ -157,7 +157,7 @@ class ImageDetail extends StatelessWidget {
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
- children: const [
+ children: [
Icon(
Icons.star,
color: Colors.yellow,
@@ -197,37 +197,37 @@ class ImageDetail extends StatelessWidget {
BoxShadow(color: Colors.grey, blurRadius: 15.0, spreadRadius: 20.0),
]),
);
-
- return ExtendedTextSelectionPointerHandler(
- //default behavior
- // child: result,
- //custom your behavior
- builder: (List states) {
- return GestureDetector(
- onTap: () {
- //do not pop page
- },
- child: Listener(
- child: result,
- behavior: HitTestBehavior.translucent,
- onPointerDown: (PointerDownEvent value) {
- for (final ExtendedTextSelectionState state in states) {
- if (!state.containsPosition(value.position)) {
- //clear other selection
- state.clearSelection();
- }
- }
- },
- onPointerMove: (PointerMoveEvent value) {
- //clear other selection
- for (final ExtendedTextSelectionState state in states) {
- state.clearSelection();
- }
- },
- ),
- );
- },
- );
+ return result;
+ // return ExtendedTextSelectionPointerHandler(
+ // //default behavior
+ // // child: result,
+ // //custom your behavior
+ // builder: (List states) {
+ // return GestureDetector(
+ // onTap: () {
+ // //do not pop page
+ // },
+ // child: Listener(
+ // child: result,
+ // behavior: HitTestBehavior.translucent,
+ // onPointerDown: (PointerDownEvent value) {
+ // for (final ExtendedTextSelectionState state in states) {
+ // if (!state.containsPosition(value.position)) {
+ // //clear other selection
+ // state.clearSelection();
+ // }
+ // }
+ // },
+ // onPointerMove: (PointerMoveEvent value) {
+ // //clear other selection
+ // for (final ExtendedTextSelectionState state in states) {
+ // state.clearSelection();
+ // }
+ // },
+ // ),
+ // );
+ // },
+ // );
}
}
diff --git a/example/lib/main.dart b/example/lib/main.dart
index f4a7041a..5264f63d 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -49,17 +49,6 @@ class MyApp extends StatelessWidget {
return onGenerateRoute(
settings: settings,
getRouteSettings: getRouteSettings,
- // routeSettingsWrapper: (FFRouteSettings ffRouteSettings) {
- // if (ffRouteSettings.name == Routes.fluttercandiesMainpage ||
- // ffRouteSettings.name == Routes.fluttercandiesDemogrouppage) {
- // return ffRouteSettings;
- // }
- // return ffRouteSettings.copyWith(
- // widget: CommonWidget(
- // child: ffRouteSettings.widget,
- // title: ffRouteSettings.routeName,
- // ));
- // },
);
},
));
diff --git a/example/lib/pages/complex/image_editor_demo.dart b/example/lib/pages/complex/image_editor_demo.dart
index 6564cfab..58521829 100644
--- a/example/lib/pages/complex/image_editor_demo.dart
+++ b/example/lib/pages/complex/image_editor_demo.dart
@@ -221,9 +221,9 @@ class _ImageEditorDemoState extends State {
initialValue: _cropLayerPainter,
itemBuilder: (BuildContext context) {
return >[
- PopupMenuItem(
+ const PopupMenuItem(
child: Row(
- children: const [
+ children: [
Icon(
Icons.rounded_corner_sharp,
color: Colors.blue,
@@ -234,12 +234,12 @@ class _ImageEditorDemoState extends State {
Text('Default'),
],
),
- value: const EditorCropLayerPainter(),
+ value: EditorCropLayerPainter(),
),
const PopupMenuDivider(),
- PopupMenuItem(
+ const PopupMenuItem(
child: Row(
- children: const [
+ children: [
Icon(
Icons.circle,
color: Colors.blue,
@@ -250,12 +250,12 @@ class _ImageEditorDemoState extends State {
Text('Custom'),
],
),
- value: const CustomEditorCropLayerPainter(),
+ value: CustomEditorCropLayerPainter(),
),
const PopupMenuDivider(),
- PopupMenuItem(
+ const PopupMenuItem(
child: Row(
- children: const [
+ children: [
Icon(
CupertinoIcons.circle,
color: Colors.blue,
@@ -266,7 +266,7 @@ class _ImageEditorDemoState extends State {
Text('Circle'),
],
),
- value: const CircleEditorCropLayerPainter(),
+ value: CircleEditorCropLayerPainter(),
),
];
},
diff --git a/example/lib/pages/complex/photo_view_demo.dart b/example/lib/pages/complex/photo_view_demo.dart
index 63456131..bd8e4b43 100644
--- a/example/lib/pages/complex/photo_view_demo.dart
+++ b/example/lib/pages/complex/photo_view_demo.dart
@@ -6,19 +6,16 @@ import 'dart:async';
import 'package:example/common/data/tu_chong_repository.dart';
import 'package:example/common/data/tu_chong_source.dart';
-import 'package:example/common/text/my_extended_text_selection_controls.dart';
-import 'package:example/common/text/my_special_text_span_builder.dart';
import 'package:example/common/utils/vm_helper.dart';
import 'package:example/common/widget/item_builder.dart';
import 'package:example/common/widget/pic_grid_view.dart';
import 'package:example/common/widget/push_to_refresh_header.dart';
import 'package:extended_image/extended_image.dart';
-import 'package:extended_text/extended_text.dart';
+// import 'package:extended_text/extended_text.dart';
import 'package:ff_annotation_route_core/ff_annotation_route_core.dart';
import 'package:flutter/material.dart' hide CircularProgressIndicator;
import 'package:loading_more_list/loading_more_list.dart';
import 'package:pull_to_refresh_notification/pull_to_refresh_notification.dart';
-import 'package:url_launcher/url_launcher.dart';
@FFRoute(
name: 'fluttercandies://photoview',
@@ -35,7 +32,7 @@ class PhotoViewDemo extends StatefulWidget {
}
class _PhotoViewDemoState extends State {
- MyTextSelectionControls? _myExtendedMaterialTextSelectionControls;
+ // MyTextSelectionControls? _myExtendedMaterialTextSelectionControls;
final String _attachContent =
'[love]Extended text help you to build rich text quickly. any special text you will have with extended text.It\'s my pleasure to invite you to join \$FlutterCandies\$ if you want to improve flutter .[love] if you meet any problem, please let me konw @zmtzawqlp .[sun_glasses]';
TuChongRepository listSourceRepository = TuChongRepository();
@@ -137,43 +134,43 @@ class _PhotoViewDemoState extends State {
),
),
Padding(
- child: ExtendedText(
+ child: Text(
content,
- onSpecialTextTap: (dynamic parameter) {
- if (parameter.toString().startsWith('\$')) {
- launchUrl(Uri.parse(
- 'https://github.com/fluttercandies'));
- } else if (parameter
- .toString()
- .startsWith('@')) {
- launchUrl(Uri.parse(
- 'mailto:zmtzawqlp@live.com'));
- }
- },
- specialTextSpanBuilder:
- MySpecialTextSpanBuilder(),
+ // onSpecialTextTap: (dynamic parameter) {
+ // if (parameter.toString().startsWith('\$')) {
+ // launchUrl(Uri.parse(
+ // 'https://github.com/fluttercandies'));
+ // } else if (parameter
+ // .toString()
+ // .startsWith('@')) {
+ // launchUrl(Uri.parse(
+ // 'mailto:zmtzawqlp@live.com'));
+ // }
+ // },
+ // specialTextSpanBuilder:
+ // MySpecialTextSpanBuilder(),
//overflow: ExtendedTextOverflow.ellipsis,
style: const TextStyle(
fontSize: 14, color: Colors.grey),
maxLines: 5,
- overflowWidget: TextOverflowWidget(
- child: Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- const Text('\u2026 '),
- InkWell(
- child: const Text('more'),
- onTap: () {
- launchUrl(Uri.parse(
- 'https://github.com/fluttercandies/extended_text'));
- },
- )
- ],
- ),
- ),
- selectionEnabled: true,
- selectionControls:
- _myExtendedMaterialTextSelectionControls,
+ // overflowWidget: TextOverflowWidget(
+ // child: Row(
+ // mainAxisSize: MainAxisSize.min,
+ // children: [
+ // const Text('\u2026 '),
+ // InkWell(
+ // child: const Text('more'),
+ // onTap: () {
+ // launchUrl(Uri.parse(
+ // 'https://github.com/fluttercandies/extended_text'));
+ // },
+ // )
+ // ],
+ // ),
+ // ),
+ // selectionEnabled: true,
+ // selectionControls:
+ // _myExtendedMaterialTextSelectionControls,
),
padding: const EdgeInsets.only(
left: margin,
@@ -212,32 +209,32 @@ class _PhotoViewDemoState extends State {
],
),
);
-
- return ExtendedTextSelectionPointerHandler(
- //default behavior
- // child: result,
- //custom your behavior
- builder: (List states) {
- return Listener(
- child: result,
- behavior: HitTestBehavior.translucent,
- onPointerDown: (PointerDownEvent value) {
- for (final ExtendedTextSelectionState state in states) {
- if (!state.containsPosition(value.position)) {
- //clear other selection
- state.clearSelection();
- }
- }
- },
- onPointerMove: (PointerMoveEvent value) {
- //clear other selection
- for (final ExtendedTextSelectionState state in states) {
- state.clearSelection();
- }
- },
- );
- },
- );
+ return result;
+ // return ExtendedTextSelectionPointerHandler(
+ // //default behavior
+ // // child: result,
+ // //custom your behavior
+ // builder: (List states) {
+ // return Listener(
+ // child: result,
+ // behavior: HitTestBehavior.translucent,
+ // onPointerDown: (PointerDownEvent value) {
+ // for (final ExtendedTextSelectionState state in states) {
+ // if (!state.containsPosition(value.position)) {
+ // //clear other selection
+ // state.clearSelection();
+ // }
+ // }
+ // },
+ // onPointerMove: (PointerMoveEvent value) {
+ // //clear other selection
+ // for (final ExtendedTextSelectionState state in states) {
+ // state.clearSelection();
+ // }
+ // },
+ // );
+ // },
+ // );
}
@override
@@ -251,7 +248,7 @@ class _PhotoViewDemoState extends State {
@override
void initState() {
- _myExtendedMaterialTextSelectionControls = MyTextSelectionControls();
+ // _myExtendedMaterialTextSelectionControls = MyTextSelectionControls();
super.initState();
}
diff --git a/example/pubspec.yaml b/example/pubspec.yaml
index 5b758efd..f93eaee7 100644
--- a/example/pubspec.yaml
+++ b/example/pubspec.yaml
@@ -4,33 +4,34 @@ publish_to: 'none'
version: 1.0.0+1
environment:
- sdk: '>=2.18.0 <3.0.0'
- flutter: '>=3.7.0'
+ sdk: '>=2.18.0 <4.0.0'
+ flutter: '>=3.10.0'
dependencies:
- extended_sliver: ^2.0.1
- extended_text: ^9.0.0
- ff_annotation_route_library: ^3.0.0
- fl_chart: ^0.55.1
+ extended_sliver: any
+ ff_annotation_route_library: any
+ fl_chart: any
flutter:
sdk: flutter
- http_client_helper: ^2.0.2
- image: ^3.1.3
- image_editor: ^1.0.2
- intl: ^0.17.0
- js: ^0.6.3
- like_button: ^2.0.4
- loading_more_list: ^5.0.2
- oktoast: ^3.1.4
- photo_manager: ^2.0.1
- pull_to_refresh_notification: ^2.3.0
- url_launcher: ^6.0.20
- vm_service: ^9.3.0
- wechat_assets_picker: ^8.0.2
+ http_client_helper: any
+ image: any
+ image_editor: any
+ intl: any
+ js: any
+ like_button: any
+ loading_more_list: any
+ oktoast: any
+ photo_manager: any
+ pull_to_refresh_notification: any
+ url_launcher: any
+ vm_service: any
+ wechat_assets_picker: any
dependency_overrides:
extended_image:
path: ../
+ path_provider: ^2.0.15
+
flutter:
uses-material-design: true
diff --git a/example/web/icons/Icon-maskable-192.png b/example/web/icons/Icon-maskable-192.png
new file mode 100644
index 00000000..eb9b4d76
Binary files /dev/null and b/example/web/icons/Icon-maskable-192.png differ
diff --git a/example/web/icons/Icon-maskable-512.png b/example/web/icons/Icon-maskable-512.png
new file mode 100644
index 00000000..d69c5669
Binary files /dev/null and b/example/web/icons/Icon-maskable-512.png differ
diff --git a/lib/src/extended_image.dart b/lib/src/extended_image.dart
index 9a2333fb..4fd71d09 100644
--- a/lib/src/extended_image.dart
+++ b/lib/src/extended_image.dart
@@ -7,6 +7,7 @@ import 'package:extended_image_library/extended_image_library.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart' hide Image;
+import 'package:flutter/scheduler.dart';
import 'package:flutter/semantics.dart';
import 'editor/editor.dart';
@@ -1239,7 +1240,9 @@ class _ExtendedImageState extends State
}
void _replaceImage({required ImageInfo? info}) {
- _imageInfo?.dispose();
+ final ImageInfo? oldImageInfo = _imageInfo;
+ SchedulerBinding.instance
+ .addPostFrameCallback((_) => oldImageInfo?.dispose());
_imageInfo = info;
}
diff --git a/lib/src/gesture/gesture.dart b/lib/src/gesture/gesture.dart
index 58e20a2d..102d9622 100644
--- a/lib/src/gesture/gesture.dart
+++ b/lib/src/gesture/gesture.dart
@@ -249,13 +249,21 @@ class ExtendedImageGestureState extends State
}
if (_pageViewState != null && _pageViewState!.isDraging) {
- _pageViewState!.onDragEnd(DragEndDetails(
- velocity: details.velocity,
- primaryVelocity:
- _pageViewState!.widget.scrollDirection == Axis.horizontal
- ? details.velocity.pixelsPerSecond.dx
- : details.velocity.pixelsPerSecond.dy,
- ));
+ _pageViewState!.onDragEnd(
+ DragEndDetails(
+ velocity: _pageViewState!.widget.scrollDirection == Axis.horizontal
+ ? Velocity(
+ pixelsPerSecond:
+ Offset(details.velocity.pixelsPerSecond.dx, 0))
+ : Velocity(
+ pixelsPerSecond:
+ Offset(0, details.velocity.pixelsPerSecond.dy)),
+ primaryVelocity:
+ _pageViewState!.widget.scrollDirection == Axis.horizontal
+ ? details.velocity.pixelsPerSecond.dx
+ : details.velocity.pixelsPerSecond.dy,
+ ),
+ );
return;
}
diff --git a/lib/src/gesture/page_view/gesture_page_view.dart b/lib/src/gesture/page_view/gesture_page_view.dart
index 999e39f7..654a4de3 100644
--- a/lib/src/gesture/page_view/gesture_page_view.dart
+++ b/lib/src/gesture/page_view/gesture_page_view.dart
@@ -1,11 +1,13 @@
import 'package:extended_image/extended_image.dart';
-import 'package:extended_image/src/gesture_detector/drag.dart';
+
+import 'package:extended_image/src/gesture_detector/official.dart';
+
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
+export 'page_controller/official.dart';
export 'rendering/sliver_fill.dart';
-export 'widgets/page_controller.dart';
export 'widgets/sliver_fill.dart';
part 'widgets/page_view.dart';
@@ -18,14 +20,17 @@ final ExtendedPageController _defaultPageController = ExtendedPageController();
const PageScrollPhysics _kPagePhysics = PageScrollPhysics();
const ScrollPhysics _defaultScrollPhysics = NeverScrollableScrollPhysics();
-final PageMetrics _testPageMetrics = PageMetrics(
- axisDirection: AxisDirection.down,
- minScrollExtent: 0,
- maxScrollExtent: 10,
- pixels: 5,
- viewportDimension: 10,
- viewportFraction: 1.0,
-);
+PageMetrics _getTestPageMetrics(BuildContext context) {
+ return PageMetrics(
+ axisDirection: AxisDirection.down,
+ minScrollExtent: 0,
+ maxScrollExtent: 10,
+ pixels: 5,
+ viewportDimension: 10,
+ viewportFraction: 1.0,
+ devicePixelRatio: View.of(context).devicePixelRatio,
+ );
+}
/// whether should scoll page
bool _defaultCanScrollPage(GestureDetails? gestureDetails) => true;
@@ -189,6 +194,7 @@ class ExtendedImageGesturePageViewState
@override
void initState() {
super.initState();
+
_gestureAnimation = GestureAnimation(this, offsetCallBack: (Offset value) {
final GestureDetails? gestureDetails =
extendedImageGestureState?.gestureDetails;
@@ -209,10 +215,10 @@ class ExtendedImageGesturePageViewState
widget.controller.shouldIgnorePointerWhenScrolling) {
bool canMove = true;
- ///user's physics
+ // user's physics
if (widget.physics.parent != null) {
- canMove =
- widget.physics.parent!.shouldAcceptUserOffset(_testPageMetrics);
+ canMove = widget.physics.parent!
+ .shouldAcceptUserOffset(_getTestPageMetrics(context));
}
if (canMove) {
switch (widget.scrollDirection) {
@@ -325,7 +331,8 @@ class ExtendedImageGesturePageViewState
);
if (widget.physics.parent == null ||
- widget.physics.parent!.shouldAcceptUserOffset(_testPageMetrics)) {
+ widget.physics.parent!
+ .shouldAcceptUserOffset(_getTestPageMetrics(context))) {
result = RawGestureDetector(
gestures: _gestureRecognizers,
behavior: HitTestBehavior.opaque,
diff --git a/lib/src/gesture/page_view/page_controller/official.dart b/lib/src/gesture/page_view/page_controller/official.dart
new file mode 100644
index 00000000..73e800ea
--- /dev/null
+++ b/lib/src/gesture/page_view/page_controller/official.dart
@@ -0,0 +1,369 @@
+import 'dart:math' as math;
+import 'package:flutter/foundation.dart';
+import 'package:flutter/widgets.dart';
+
+part 'page_position.dart';
+part 'page_controller.dart';
+
+class _PageController extends ScrollController {
+ /// Creates a page controller.
+ ///
+ /// The [initialPage], [keepPage], and [viewportFraction] arguments must not be null.
+ _PageController({
+ this.initialPage = 0,
+ this.keepPage = true,
+ this.viewportFraction = 1.0,
+ }) : assert(viewportFraction > 0.0);
+
+ /// The page to show when first creating the [PageView].
+ final int initialPage;
+
+ /// Save the current [page] with [PageStorage] and restore it if
+ /// this controller's scrollable is recreated.
+ ///
+ /// If this property is set to false, the current [page] is never saved
+ /// and [initialPage] is always used to initialize the scroll offset.
+ /// If true (the default), the initial page is used the first time the
+ /// controller's scrollable is created, since there's isn't a page to
+ /// restore yet. Subsequently the saved page is restored and
+ /// [initialPage] is ignored.
+ ///
+ /// See also:
+ ///
+ /// * [PageStorageKey], which should be used when more than one
+ /// scrollable appears in the same route, to distinguish the [PageStorage]
+ /// locations used to save scroll offsets.
+ final bool keepPage;
+
+ /// {@template flutter.widgets.pageview.viewportFraction}
+ /// The fraction of the viewport that each page should occupy.
+ ///
+ /// Defaults to 1.0, which means each page fills the viewport in the scrolling
+ /// direction.
+ /// {@endtemplate}
+ final double viewportFraction;
+
+ /// The current page displayed in the controlled [PageView].
+ ///
+ /// There are circumstances that this [_PageController] can't know the current
+ /// page. Reading [page] will throw an [AssertionError] in the following cases:
+ ///
+ /// 1. No [PageView] is currently using this [_PageController]. Once a
+ /// [PageView] starts using this [_PageController], the new [page]
+ /// position will be derived:
+ ///
+ /// * First, based on the attached [PageView]'s [BuildContext] and the
+ /// position saved at that context's [PageStorage] if [keepPage] is true.
+ /// * Second, from the [_PageController]'s [initialPage].
+ ///
+ /// 2. More than one [PageView] using the same [_PageController].
+ ///
+ /// The [hasClients] property can be used to check if a [PageView] is attached
+ /// prior to accessing [page].
+ double? get page {
+ assert(
+ positions.isNotEmpty,
+ 'PageController.page cannot be accessed before a PageView is built with it.',
+ );
+ assert(
+ positions.length == 1,
+ 'The page property cannot be read when multiple PageViews are attached to '
+ 'the same PageController.',
+ );
+ final _PagePosition position = this.position as _PagePosition;
+ return position.page;
+ }
+
+ /// Animates the controlled [PageView] from the current page to the given page.
+ ///
+ /// The animation lasts for the given duration and follows the given curve.
+ /// The returned [Future] resolves when the animation completes.
+ ///
+ /// The `duration` and `curve` arguments must not be null.
+ Future animateToPage(
+ int page, {
+ required Duration duration,
+ required Curve curve,
+ }) {
+ final _PagePosition position = this.position as _PagePosition;
+ if (position._cachedPage != null) {
+ position._cachedPage = page.toDouble();
+ return Future.value();
+ }
+
+ return position.animateTo(
+ position.getPixelsFromPage(page.toDouble()),
+ duration: duration,
+ curve: curve,
+ );
+ }
+
+ /// Changes which page is displayed in the controlled [PageView].
+ ///
+ /// Jumps the page position from its current value to the given value,
+ /// without animation, and without checking if the new value is in range.
+ void jumpToPage(int page) {
+ final _PagePosition position = this.position as _PagePosition;
+ if (position._cachedPage != null) {
+ position._cachedPage = page.toDouble();
+ return;
+ }
+
+ position.jumpTo(position.getPixelsFromPage(page.toDouble()));
+ }
+
+ /// Animates the controlled [PageView] to the next page.
+ ///
+ /// The animation lasts for the given duration and follows the given curve.
+ /// The returned [Future] resolves when the animation completes.
+ ///
+ /// The `duration` and `curve` arguments must not be null.
+ Future nextPage({required Duration duration, required Curve curve}) {
+ return animateToPage(page!.round() + 1, duration: duration, curve: curve);
+ }
+
+ /// Animates the controlled [PageView] to the previous page.
+ ///
+ /// The animation lasts for the given duration and follows the given curve.
+ /// The returned [Future] resolves when the animation completes.
+ ///
+ /// The `duration` and `curve` arguments must not be null.
+ Future previousPage(
+ {required Duration duration, required Curve curve}) {
+ return animateToPage(page!.round() - 1, duration: duration, curve: curve);
+ }
+
+ @override
+ ScrollPosition createScrollPosition(ScrollPhysics physics,
+ ScrollContext context, ScrollPosition? oldPosition) {
+ return _PagePosition(
+ physics: physics,
+ context: context,
+ initialPage: initialPage,
+ keepPage: keepPage,
+ viewportFraction: viewportFraction,
+ oldPosition: oldPosition,
+ );
+ }
+
+ @override
+ void attach(ScrollPosition position) {
+ super.attach(position);
+ final _PagePosition pagePosition = position as _PagePosition;
+ pagePosition.viewportFraction = viewportFraction;
+ }
+}
+
+class _PagePosition extends ScrollPositionWithSingleContext
+ implements PageMetrics {
+ _PagePosition({
+ required super.physics,
+ required super.context,
+ this.initialPage = 0,
+ bool keepPage = true,
+ double viewportFraction = 1.0,
+ super.oldPosition,
+ }) : assert(viewportFraction > 0.0),
+ _viewportFraction = viewportFraction,
+ _pageToUseOnStartup = initialPage.toDouble(),
+ super(
+ initialPixels: null,
+ keepScrollOffset: keepPage,
+ );
+
+ final int initialPage;
+ double _pageToUseOnStartup;
+ // When the viewport has a zero-size, the `page` can not
+ // be retrieved by `getPageFromPixels`, so we need to cache the page
+ // for use when resizing the viewport to non-zero next time.
+ double? _cachedPage;
+
+ @override
+ Future ensureVisible(
+ RenderObject object, {
+ double alignment = 0.0,
+ Duration duration = Duration.zero,
+ Curve curve = Curves.ease,
+ ScrollPositionAlignmentPolicy alignmentPolicy =
+ ScrollPositionAlignmentPolicy.explicit,
+ RenderObject? targetRenderObject,
+ }) {
+ // Since the _PagePosition is intended to cover the available space within
+ // its viewport, stop trying to move the target render object to the center
+ // - otherwise, could end up changing which page is visible and moving the
+ // targetRenderObject out of the viewport.
+ return super.ensureVisible(
+ object,
+ alignment: alignment,
+ duration: duration,
+ curve: curve,
+ alignmentPolicy: alignmentPolicy,
+ );
+ }
+
+ @override
+ double get viewportFraction => _viewportFraction;
+ double _viewportFraction;
+ set viewportFraction(double value) {
+ if (_viewportFraction == value) {
+ return;
+ }
+ final double? oldPage = page;
+ _viewportFraction = value;
+ if (oldPage != null) {
+ forcePixels(getPixelsFromPage(oldPage));
+ }
+ }
+
+ // The amount of offset that will be added to [minScrollExtent] and subtracted
+ // from [maxScrollExtent], such that every page will properly snap to the center
+ // of the viewport when viewportFraction is greater than 1.
+ //
+ // The value is 0 if viewportFraction is less than or equal to 1, larger than 0
+ // otherwise.
+ double get _initialPageOffset =>
+ math.max(0, viewportDimension * (viewportFraction - 1) / 2);
+
+ double getPageFromPixels(double pixels, double viewportDimension) {
+ assert(viewportDimension > 0.0);
+ final double actual = math.max(0.0, pixels - _initialPageOffset) /
+ (viewportDimension * viewportFraction);
+ final double round = actual.roundToDouble();
+ if ((actual - round).abs() < precisionErrorTolerance) {
+ return round;
+ }
+ return actual;
+ }
+
+ double getPixelsFromPage(double page) {
+ return page * viewportDimension * viewportFraction + _initialPageOffset;
+ }
+
+ @override
+ double? get page {
+ assert(
+ !hasPixels || hasContentDimensions,
+ 'Page value is only available after content dimensions are established.',
+ );
+ return !hasPixels || !hasContentDimensions
+ ? null
+ : _cachedPage ??
+ getPageFromPixels(
+ clampDouble(pixels, minScrollExtent, maxScrollExtent),
+ viewportDimension);
+ }
+
+ @override
+ void saveScrollOffset() {
+ PageStorage.maybeOf(context.storageContext)?.writeState(
+ context.storageContext,
+ _cachedPage ?? getPageFromPixels(pixels, viewportDimension));
+ }
+
+ @override
+ void restoreScrollOffset() {
+ if (!hasPixels) {
+ final double? value = PageStorage.maybeOf(context.storageContext)
+ ?.readState(context.storageContext) as double?;
+ if (value != null) {
+ _pageToUseOnStartup = value;
+ }
+ }
+ }
+
+ @override
+ void saveOffset() {
+ context.saveOffset(
+ _cachedPage ?? getPageFromPixels(pixels, viewportDimension));
+ }
+
+ @override
+ void restoreOffset(double offset, {bool initialRestore = false}) {
+ if (initialRestore) {
+ _pageToUseOnStartup = offset;
+ } else {
+ jumpTo(getPixelsFromPage(offset));
+ }
+ }
+
+ // [_ExtendedPagePosition] has override this method
+ // We can't call super.super.applyViewportDimension in _ExtendedPagePosition
+ // so commont
+ // @override
+ // bool applyViewportDimension(double viewportDimension) {
+ // final double? oldViewportDimensions =
+ // hasViewportDimension ? this.viewportDimension : null;
+ // if (viewportDimension == oldViewportDimensions) {
+ // return true;
+ // }
+ // final bool result = super.applyViewportDimension(viewportDimension);
+ // final double? oldPixels = hasPixels ? pixels : null;
+ // double page;
+ // if (oldPixels == null) {
+ // page = _pageToUseOnStartup;
+ // } else if (oldViewportDimensions == 0.0) {
+ // // If resize from zero, we should use the _cachedPage to recover the state.
+ // page = _cachedPage!;
+ // } else {
+ // page = getPageFromPixels(oldPixels, oldViewportDimensions!);
+ // }
+ // final double newPixels = getPixelsFromPage(page);
+
+ // // If the viewportDimension is zero, cache the page
+ // // in case the viewport is resized to be non-zero.
+ // _cachedPage = (viewportDimension == 0.0) ? page : null;
+
+ // if (newPixels != oldPixels) {
+ // correctPixels(newPixels);
+ // return false;
+ // }
+ // return result;
+ // }
+
+ @override
+ void absorb(ScrollPosition other) {
+ super.absorb(other);
+ assert(_cachedPage == null);
+
+ if (other is! _PagePosition) {
+ return;
+ }
+
+ if (other._cachedPage != null) {
+ _cachedPage = other._cachedPage;
+ }
+ }
+
+ @override
+ bool applyContentDimensions(double minScrollExtent, double maxScrollExtent) {
+ final double newMinScrollExtent = minScrollExtent + _initialPageOffset;
+ return super.applyContentDimensions(
+ newMinScrollExtent,
+ math.max(newMinScrollExtent, maxScrollExtent - _initialPageOffset),
+ );
+ }
+
+ @override
+ PageMetrics copyWith({
+ double? minScrollExtent,
+ double? maxScrollExtent,
+ double? pixels,
+ double? viewportDimension,
+ AxisDirection? axisDirection,
+ double? viewportFraction,
+ double? devicePixelRatio,
+ }) {
+ return PageMetrics(
+ minScrollExtent: minScrollExtent ??
+ (hasContentDimensions ? this.minScrollExtent : null),
+ maxScrollExtent: maxScrollExtent ??
+ (hasContentDimensions ? this.maxScrollExtent : null),
+ pixels: pixels ?? (hasPixels ? this.pixels : null),
+ viewportDimension: viewportDimension ??
+ (hasViewportDimension ? this.viewportDimension : null),
+ axisDirection: axisDirection ?? this.axisDirection,
+ viewportFraction: viewportFraction ?? this.viewportFraction,
+ devicePixelRatio: devicePixelRatio ?? this.devicePixelRatio,
+ );
+ }
+}
diff --git a/lib/src/gesture/page_view/page_controller/page_controller.dart b/lib/src/gesture/page_view/page_controller/page_controller.dart
new file mode 100644
index 00000000..463a86e2
--- /dev/null
+++ b/lib/src/gesture/page_view/page_controller/page_controller.dart
@@ -0,0 +1,65 @@
+part of 'official.dart';
+
+class ExtendedPageController extends _PageController {
+ ExtendedPageController({
+ super.initialPage = 0,
+ super.keepPage = true,
+ super.viewportFraction = 1.0,
+ this.shouldIgnorePointerWhenScrolling = false,
+ this.pageSpacing = 0.0,
+ });
+
+ /// Whether the contents of the widget should ignore [PointerEvent] inputs.
+ ///
+ /// Setting this value to true prevents the use from interacting with the
+ /// contents of the widget with pointer events. The widget itself is still
+ /// interactive.
+ ///
+ /// For example, if the scroll position is being driven by an animation, it
+ /// might be appropriate to set this value to ignore pointer events to
+ /// prevent the user from accidentally interacting with the contents of the
+ /// widget as it animates. The user will still be able to touch the widget,
+ /// potentially stopping the animation.
+ ///
+ ///
+ /// if true, we should handle scale event in [ExtendedImageGesturePageView] before [ExtendedImageGesturePageView] stop scroll.
+ /// notice: there is one issue that we may be zoom two image at the same time, because we can't find out which one should be zoomed.
+ ///
+ ///
+ /// if false, Image can accept scale event before [ExtendedImageGesturePageView] stop scroll.
+ /// notice: we don't know there are any issues if we don't ignore [PointerEvent] inputs when it's scrolling.
+ ///
+ ///
+ /// Two way to solve issue that we can's zoom image before [PageView] stop scroll.
+ ///
+ ///
+ /// default is false.
+
+ final bool shouldIgnorePointerWhenScrolling;
+
+ /// The number of logical pixels between each page.
+
+ final double pageSpacing;
+
+ @override
+ ScrollPosition createScrollPosition(ScrollPhysics physics,
+ ScrollContext context, ScrollPosition? oldPosition) {
+ return _ExtendedPagePosition(
+ physics: physics,
+ context: context,
+ initialPage: initialPage,
+ keepPage: keepPage,
+ viewportFraction: viewportFraction,
+ oldPosition: oldPosition,
+ pageSpacing: pageSpacing,
+ );
+ }
+
+ @override
+ void attach(ScrollPosition position) {
+ super.attach(position);
+ final _ExtendedPagePosition pagePosition =
+ position as _ExtendedPagePosition;
+ pagePosition.pageSpacing = pageSpacing;
+ }
+}
diff --git a/lib/src/gesture/page_view/page_controller/page_position.dart b/lib/src/gesture/page_view/page_controller/page_position.dart
new file mode 100644
index 00000000..54822d34
--- /dev/null
+++ b/lib/src/gesture/page_view/page_controller/page_position.dart
@@ -0,0 +1,61 @@
+// ignore_for_file: prefer_final_fields, overridden_fields, always_put_control_body_on_new_line
+
+part of 'official.dart';
+
+class _ExtendedPagePosition extends _PagePosition {
+ _ExtendedPagePosition({
+ required super.physics,
+ required super.context,
+ super.initialPage = 0,
+ super.keepPage = true,
+ super.viewportFraction = 1.0,
+ super.oldPosition,
+ double pageSpacing = 0.0,
+ }) : _pageSpacing = pageSpacing;
+ double _pageSpacing;
+ double get pageSpacing => _pageSpacing;
+ set pageSpacing(double value) {
+ if (_pageSpacing != value) {
+ final double? oldPage = page;
+ _pageSpacing = value;
+ if (oldPage != null) forcePixels(getPixelsFromPage(oldPage));
+ }
+ }
+
+ // fix viewportDimension
+ @override
+ double get viewportDimension => super.viewportDimension + pageSpacing;
+
+ @override
+ bool applyViewportDimension(double viewportDimension) {
+ final double? oldViewportDimensions =
+ // fix viewportDimension
+ hasViewportDimension ? this.viewportDimension - pageSpacing : null;
+
+ if (viewportDimension == oldViewportDimensions) {
+ return true;
+ }
+
+ final bool result = super.applyViewportDimension(viewportDimension);
+ final double? oldPixels = hasPixels ? pixels : null;
+ double page;
+ if (oldPixels == null) {
+ page = _pageToUseOnStartup;
+ } else if (oldViewportDimensions == 0.0) {
+ // If resize from zero, we should use the _cachedPage to recover the state.
+ page = _cachedPage!;
+ } else {
+ page = getPageFromPixels(oldPixels, oldViewportDimensions!);
+ }
+ final double newPixels = getPixelsFromPage(page);
+
+ // If the viewportDimension is zero, cache the page
+ // in case the viewport is resized to be non-zero.
+ _cachedPage = (viewportDimension == 0.0) ? page : null;
+ if (newPixels != oldPixels) {
+ correctPixels(newPixels);
+ return false;
+ }
+ return result;
+ }
+}
diff --git a/lib/src/gesture/page_view/widgets/page_controller.dart b/lib/src/gesture/page_view/widgets/page_controller.dart
deleted file mode 100644
index 17ea2d38..00000000
--- a/lib/src/gesture/page_view/widgets/page_controller.dart
+++ /dev/null
@@ -1,189 +0,0 @@
-// ignore_for_file: unnecessary_null_comparison, always_put_control_body_on_new_line
-import 'dart:math' as math;
-import 'package:flutter/foundation.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter/widgets.dart';
-
-part 'scroll_position.dart';
-
-class ExtendedPageController extends ScrollController {
- /// Creates a page controller.
- ///
- /// The [initialPage], [keepPage], and [viewportFraction] arguments must not be null.
- ExtendedPageController({
- this.initialPage = 0,
- this.keepPage = true,
- this.viewportFraction = 1.0,
- this.pageSpacing = 0.0,
- this.shouldIgnorePointerWhenScrolling = false,
- }) : assert(initialPage != null),
- assert(keepPage != null),
- assert(viewportFraction != null),
- assert(viewportFraction > 0.0);
-
- /// Whether the contents of the widget should ignore [PointerEvent] inputs.
- ///
- /// Setting this value to true prevents the use from interacting with the
- /// contents of the widget with pointer events. The widget itself is still
- /// interactive.
- ///
- /// For example, if the scroll position is being driven by an animation, it
- /// might be appropriate to set this value to ignore pointer events to
- /// prevent the user from accidentally interacting with the contents of the
- /// widget as it animates. The user will still be able to touch the widget,
- /// potentially stopping the animation.
- ///
- ///
- /// if true, we should handle scale event in [ExtendedImageGesturePageView] before [ExtendedImageGesturePageView] stop scroll.
- /// notice: there is one issue that we may be zoom two image at the same time, because we can't find out which one should be zoomed.
- ///
- ///
- /// if false, Image can accept scale event before [ExtendedImageGesturePageView] stop scroll.
- /// notice: we don't know there are any issues if we don't ignore [PointerEvent] inputs when it's scrolling.
- ///
- ///
- /// Two way to solve issue that we can's zoom image before [PageView] stop scroll.
- ///
- ///
- /// default is false.
- final bool shouldIgnorePointerWhenScrolling;
-
- /// The page to show when first creating the [PageView].
- final int initialPage;
-
- /// Save the current [page] with [PageStorage] and restore it if
- /// this controller's scrollable is recreated.
- ///
- /// If this property is set to false, the current [page] is never saved
- /// and [initialPage] is always used to initialize the scroll offset.
- /// If true (the default), the initial page is used the first time the
- /// controller's scrollable is created, since there's isn't a page to
- /// restore yet. Subsequently the saved page is restored and
- /// [initialPage] is ignored.
- ///
- /// See also:
- ///
- /// * [PageStorageKey], which should be used when more than one
- /// scrollable appears in the same route, to distinguish the [PageStorage]
- /// locations used to save scroll offsets.
- final bool keepPage;
-
- /// The fraction of the viewport that each page should occupy.
- ///
- /// Defaults to 1.0, which means each page fills the viewport in the scrolling
- /// direction.
- final double viewportFraction;
-
- /// The number of logical pixels between each page.
- final double pageSpacing;
-
- /// The current page displayed in the controlled [PageView].
- ///
- /// There are circumstances that this [ExtendedPageController] can't know the current
- /// page. Reading [page] will throw an [AssertionError] in the following cases:
- ///
- /// 1. No [PageView] is currently using this [ExtendedPageController]. Once a
- /// [PageView] starts using this [ExtendedPageController], the new [page]
- /// position will be derived:
- ///
- /// * First, based on the attached [PageView]'s [BuildContext] and the
- /// position saved at that context's [PageStorage] if [keepPage] is true.
- /// * Second, from the [ExtendedPageController]'s [initialPage].
- ///
- /// 2. More than one [PageView] using the same [ExtendedPageController].
- ///
- /// The [hasClients] property can be used to check if a [PageView] is attached
- /// prior to accessing [page].
- double? get page {
- assert(
- positions.isNotEmpty,
- 'PageController.page cannot be accessed before a PageView is built with it.',
- );
- assert(
- positions.length == 1,
- 'The page property cannot be read when multiple PageViews are attached to '
- 'the same PageController.',
- );
- final ExtendedPagePosition position = this.position as ExtendedPagePosition;
- return position.page;
- }
-
- /// Animates the controlled [PageView] from the current page to the given page.
- ///
- /// The animation lasts for the given duration and follows the given curve.
- /// The returned [Future] resolves when the animation completes.
- ///
- /// The `duration` and `curve` arguments must not be null.
- Future animateToPage(
- int page, {
- required Duration duration,
- required Curve curve,
- }) {
- final ExtendedPagePosition position = this.position as ExtendedPagePosition;
- if (position._cachedPage != null) {
- position._cachedPage = page.toDouble();
- return Future.value();
- }
- return position.animateTo(
- position.getPixelsFromPage(page.toDouble()),
- duration: duration,
- curve: curve,
- );
- }
-
- /// Changes which page is displayed in the controlled [PageView].
- ///
- /// Jumps the page position from its current value to the given value,
- /// without animation, and without checking if the new value is in range.
- void jumpToPage(int page) {
- final ExtendedPagePosition position = this.position as ExtendedPagePosition;
- if (position._cachedPage != null) {
- position._cachedPage = page.toDouble();
- return;
- }
- position.jumpTo(position.getPixelsFromPage(page.toDouble()));
- }
-
- /// Animates the controlled [PageView] to the next page.
- ///
- /// The animation lasts for the given duration and follows the given curve.
- /// The returned [Future] resolves when the animation completes.
- ///
- /// The `duration` and `curve` arguments must not be null.
- Future nextPage({required Duration duration, required Curve curve}) {
- return animateToPage(page!.round() + 1, duration: duration, curve: curve);
- }
-
- /// Animates the controlled [PageView] to the previous page.
- ///
- /// The animation lasts for the given duration and follows the given curve.
- /// The returned [Future] resolves when the animation completes.
- ///
- /// The `duration` and `curve` arguments must not be null.
- Future previousPage(
- {required Duration duration, required Curve curve}) {
- return animateToPage(page!.round() - 1, duration: duration, curve: curve);
- }
-
- @override
- ScrollPosition createScrollPosition(ScrollPhysics physics,
- ScrollContext context, ScrollPosition? oldPosition) {
- return ExtendedPagePosition(
- physics: physics,
- context: context,
- initialPage: initialPage,
- keepPage: keepPage,
- viewportFraction: viewportFraction,
- oldPosition: oldPosition,
- pageSpacing: pageSpacing,
- );
- }
-
- @override
- void attach(ScrollPosition position) {
- super.attach(position);
- final ExtendedPagePosition pagePosition = position as ExtendedPagePosition;
- pagePosition.viewportFraction = viewportFraction;
- pagePosition.pageSpacing = pageSpacing;
- }
-}
diff --git a/lib/src/gesture/page_view/widgets/scroll_position.dart b/lib/src/gesture/page_view/widgets/scroll_position.dart
deleted file mode 100644
index 55e9ce9e..00000000
--- a/lib/src/gesture/page_view/widgets/scroll_position.dart
+++ /dev/null
@@ -1,234 +0,0 @@
-part of 'page_controller.dart';
-// ignore_for_file: unnecessary_null_comparison, always_put_control_body_on_new_line
-
-class ExtendedPagePosition extends ScrollPositionWithSingleContext
- implements PageMetrics {
- ExtendedPagePosition({
- required ScrollPhysics physics,
- required ScrollContext context,
- this.initialPage = 0,
- bool keepPage = true,
- double viewportFraction = 1.0,
- ScrollPosition? oldPosition,
- double pageSpacing = 0.0,
- }) : assert(initialPage != null),
- assert(keepPage != null),
- assert(viewportFraction != null),
- assert(viewportFraction > 0.0),
- assert(pageSpacing != null),
- assert(pageSpacing >= 0.0),
- _viewportFraction = viewportFraction,
- _pageSpacing = pageSpacing,
- _pageToUseOnStartup = initialPage.toDouble(),
- super(
- physics: physics,
- context: context,
- initialPixels: null,
- keepScrollOffset: keepPage,
- oldPosition: oldPosition,
- );
-
- final int initialPage;
- double _pageToUseOnStartup;
- // When the viewport has a zero-size, the `page` can not
- // be retrieved by `getPageFromPixels`, so we need to cache the page
- // for use when resizing the viewport to non-zero next time.
- double? _cachedPage;
-
- @override
- Future ensureVisible(
- RenderObject object, {
- double alignment = 0.0,
- Duration duration = Duration.zero,
- Curve curve = Curves.ease,
- ScrollPositionAlignmentPolicy alignmentPolicy =
- ScrollPositionAlignmentPolicy.explicit,
- RenderObject? targetRenderObject,
- }) {
- // Since the _PagePosition is intended to cover the available space within
- // its viewport, stop trying to move the target render object to the center
- // - otherwise, could end up changing which page is visible and moving the
- // targetRenderObject out of the viewport.
- return super.ensureVisible(
- object,
- alignment: alignment,
- duration: duration,
- curve: curve,
- alignmentPolicy: alignmentPolicy,
- targetRenderObject: null,
- );
- }
-
- @override
- double get viewportFraction => _viewportFraction;
- double _viewportFraction;
- set viewportFraction(double value) {
- if (_viewportFraction == value) return;
- final double? oldPage = page;
- _viewportFraction = value;
- if (oldPage != null) forcePixels(getPixelsFromPage(oldPage));
- }
-
- double _pageSpacing;
- double get pageSpacing => _pageSpacing;
- set pageSpacing(double value) {
- if (_pageSpacing != value) {
- final double? oldPage = page;
- _pageSpacing = value;
- if (oldPage != null) forcePixels(getPixelsFromPage(oldPage));
- }
- }
-
- // fix viewportDimension
- @override
- double get viewportDimension => super.viewportDimension + pageSpacing;
-
- // The amount of offset that will be added to [minScrollExtent] and subtracted
- // from [maxScrollExtent], such that every page will properly snap to the center
- // of the viewport when viewportFraction is greater than 1.
- //
- // The value is 0 if viewportFraction is less than or equal to 1, larger than 0
- // otherwise.
- double get _initialPageOffset =>
- math.max(0, viewportDimension * (viewportFraction - 1) / 2);
-
- double getPageFromPixels(double pixels, double viewportDimension) {
- final double actual = math.max(0.0, pixels - _initialPageOffset) /
- math.max(1.0, viewportDimension * viewportFraction);
- final double round = actual.roundToDouble();
- if ((actual - round).abs() < precisionErrorTolerance) {
- return round;
- }
- return actual;
- }
-
- double getPixelsFromPage(double page) {
- return page * viewportDimension * viewportFraction + _initialPageOffset;
- }
-
- @override
- double? get page {
- assert(
- !hasPixels || hasContentDimensions,
- 'Page value is only available after content dimensions are established.',
- );
- return !hasPixels || !hasContentDimensions
- ? null
- : _cachedPage ??
- getPageFromPixels(
- clampDouble(pixels, minScrollExtent, maxScrollExtent),
- viewportDimension);
- }
-
- @override
- void saveScrollOffset() {
- PageStorage.maybeOf(context.storageContext)?.writeState(
- context.storageContext,
- _cachedPage ?? getPageFromPixels(pixels, viewportDimension));
- }
-
- @override
- void restoreScrollOffset() {
- if (!hasPixels) {
- final double? value = PageStorage.maybeOf(context.storageContext)
- ?.readState(context.storageContext) as double?;
- if (value != null) {
- _pageToUseOnStartup = value;
- }
- }
- }
-
- @override
- void saveOffset() {
- context.saveOffset(
- _cachedPage ?? getPageFromPixels(pixels, viewportDimension));
- }
-
- @override
- void restoreOffset(double offset, {bool initialRestore = false}) {
- assert(initialRestore != null);
- assert(offset != null);
- if (initialRestore) {
- _pageToUseOnStartup = offset;
- } else {
- jumpTo(getPixelsFromPage(offset));
- }
- }
-
- @override
- bool applyViewportDimension(double viewportDimension) {
- final double? oldViewportDimensions =
- // fix viewportDimension
- hasViewportDimension ? this.viewportDimension - pageSpacing : null;
-
- if (viewportDimension == oldViewportDimensions) {
- return true;
- }
- final bool result = super.applyViewportDimension(viewportDimension);
- final double? oldPixels = hasPixels ? pixels : null;
- double page;
- if (oldPixels == null) {
- page = _pageToUseOnStartup;
- } else if (oldViewportDimensions == 0.0) {
- // If resize from zero, we should use the _cachedPage to recover the state.
- page = _cachedPage!;
- } else {
- page = getPageFromPixels(oldPixels, oldViewportDimensions!);
- }
- final double newPixels = getPixelsFromPage(page);
-
- // If the viewportDimension is zero, cache the page
- // in case the viewport is resized to be non-zero.
- _cachedPage = (viewportDimension == 0.0) ? page : null;
- if (newPixels != oldPixels) {
- correctPixels(newPixels);
- return false;
- }
- return result;
- }
-
- @override
- void absorb(ScrollPosition other) {
- super.absorb(other);
- assert(_cachedPage == null);
-
- if (other is! ExtendedPagePosition) {
- return;
- }
-
- if (other._cachedPage != null) {
- _cachedPage = other._cachedPage;
- }
- }
-
- @override
- bool applyContentDimensions(double minScrollExtent, double maxScrollExtent) {
- final double newMinScrollExtent = minScrollExtent + _initialPageOffset;
- return super.applyContentDimensions(
- newMinScrollExtent,
- math.max(newMinScrollExtent, maxScrollExtent - _initialPageOffset),
- );
- }
-
- @override
- PageMetrics copyWith({
- double? minScrollExtent,
- double? maxScrollExtent,
- double? pixels,
- double? viewportDimension,
- AxisDirection? axisDirection,
- double? viewportFraction,
- }) {
- return PageMetrics(
- minScrollExtent: minScrollExtent ??
- (hasContentDimensions ? this.minScrollExtent : null),
- maxScrollExtent: maxScrollExtent ??
- (hasContentDimensions ? this.maxScrollExtent : null),
- pixels: pixels ?? (hasPixels ? this.pixels : null),
- viewportDimension: viewportDimension ??
- (hasViewportDimension ? this.viewportDimension : null),
- axisDirection: axisDirection ?? this.axisDirection,
- viewportFraction: viewportFraction ?? this.viewportFraction,
- );
- }
-}
diff --git a/lib/src/gesture_detector/drag_gesture_recognizer.dart b/lib/src/gesture_detector/drag_gesture_recognizer.dart
new file mode 100644
index 00000000..a7ba8796
--- /dev/null
+++ b/lib/src/gesture_detector/drag_gesture_recognizer.dart
@@ -0,0 +1,147 @@
+part of 'official.dart';
+
+typedef CanHorizontalOrVerticalDrag = bool Function();
+
+mixin DragGestureRecognizerMixin on _DragGestureRecognizer {
+ bool get canDrag =>
+ canHorizontalOrVerticalDrag == null || canHorizontalOrVerticalDrag!();
+
+ bool _shouldAccpet() {
+ if (!canDrag) {
+ return false;
+ }
+ if (_velocityTrackers.keys.length == 1) {
+ return true;
+ }
+
+ // if pointers are not the only, check whether they are in the negative
+ // maybe this is a Horizontal/Vertical zoom
+ Offset offset = const Offset(1, 1);
+ for (final VelocityTracker tracker in _velocityTrackers.values) {
+ if (tracker is ExtendedVelocityTracker) {
+ final Offset delta = tracker.getSamplesDelta();
+ offset = Offset(offset.dx * (delta.dx == 0 ? 1 : delta.dx),
+ offset.dy * (delta.dy == 0 ? 1 : delta.dy));
+ }
+ }
+
+ return !(offset.dx < 0 || offset.dy < 0);
+ }
+
+ CanHorizontalOrVerticalDrag? get canHorizontalOrVerticalDrag;
+
+ @override
+ void handleEvent(PointerEvent event) {
+ assert(_state != _DragState.ready);
+ if (!event.synthesized &&
+ (event is PointerDownEvent ||
+ event is PointerMoveEvent ||
+ event is PointerPanZoomStartEvent ||
+ event is PointerPanZoomUpdateEvent)) {
+ final VelocityTracker tracker = _velocityTrackers[event.pointer]!;
+ if (event is PointerPanZoomStartEvent) {
+ tracker.addPosition(event.timeStamp, Offset.zero);
+ } else if (event is PointerPanZoomUpdateEvent) {
+ tracker.addPosition(event.timeStamp, event.pan);
+ } else {
+ tracker.addPosition(event.timeStamp, event.localPosition);
+ }
+ }
+ if (event is PointerMoveEvent && event.buttons != _initialButtons) {
+ _giveUpPointer(event.pointer);
+ return;
+ }
+ if (event is PointerMoveEvent || event is PointerPanZoomUpdateEvent) {
+ final Offset delta = (event is PointerMoveEvent)
+ ? event.delta
+ : (event as PointerPanZoomUpdateEvent).panDelta;
+ final Offset localDelta = (event is PointerMoveEvent)
+ ? event.localDelta
+ : (event as PointerPanZoomUpdateEvent).localPanDelta;
+ final Offset position = (event is PointerMoveEvent)
+ ? event.position
+ : (event.position + (event as PointerPanZoomUpdateEvent).pan);
+ final Offset localPosition = (event is PointerMoveEvent)
+ ? event.localPosition
+ : (event.localPosition +
+ (event as PointerPanZoomUpdateEvent).localPan);
+ if (_state == _DragState.accepted) {
+ _checkUpdate(
+ sourceTimeStamp: event.timeStamp,
+ delta: _getDeltaForDetails(localDelta),
+ primaryDelta: _getPrimaryValueFromOffset(localDelta),
+ globalPosition: position,
+ localPosition: localPosition,
+ );
+ } else {
+ _pendingDragOffset += OffsetPair(local: localDelta, global: delta);
+ _lastPendingEventTimestamp = event.timeStamp;
+ _lastTransform = event.transform;
+ final Offset movedLocally = _getDeltaForDetails(localDelta);
+ final Matrix4? localToGlobalTransform = event.transform == null
+ ? null
+ : Matrix4.tryInvert(event.transform!);
+ _globalDistanceMoved += PointerEvent.transformDeltaViaPositions(
+ transform: localToGlobalTransform,
+ untransformedDelta: movedLocally,
+ untransformedEndPosition: localPosition)
+ .distance *
+ (_getPrimaryValueFromOffset(movedLocally) ?? 1).sign;
+ if (_hasSufficientGlobalDistanceToAccept(
+ event.kind, gestureSettings?.touchSlop) &&
+ // zmtzawqlp
+ _shouldAccpet()) {
+ resolve(GestureDisposition.accepted);
+ }
+ }
+ }
+ if (event is PointerUpEvent ||
+ event is PointerCancelEvent ||
+ event is PointerPanZoomEndEvent) {
+ _giveUpPointer(event.pointer);
+ }
+ }
+}
+
+abstract class ExtendedDragGestureRecognizer extends _DragGestureRecognizer
+ with DragGestureRecognizerMixin {
+ ExtendedDragGestureRecognizer({
+ super.debugOwner,
+ super.dragStartBehavior = DragStartBehavior.start,
+ super.velocityTrackerBuilder = _defaultBuilder,
+ super.supportedDevices,
+ super.allowedButtonsFilter,
+ this.canHorizontalOrVerticalDrag,
+ });
+
+ static ExtendedVelocityTracker _defaultBuilder(PointerEvent event) =>
+ ExtendedVelocityTracker.withKind(event.kind);
+ @override
+ final CanHorizontalOrVerticalDrag? canHorizontalOrVerticalDrag;
+}
+
+class ExtendedHorizontalDragGestureRecognizer
+ extends _HorizontalDragGestureRecognizer with DragGestureRecognizerMixin {
+ ExtendedHorizontalDragGestureRecognizer({
+ super.debugOwner,
+ super.supportedDevices,
+ super.allowedButtonsFilter,
+ this.canHorizontalOrVerticalDrag,
+ });
+
+ @override
+ final CanHorizontalOrVerticalDrag? canHorizontalOrVerticalDrag;
+}
+
+class ExtendedVerticalDragGestureRecognizer
+ extends _VerticalDragGestureRecognizer with DragGestureRecognizerMixin {
+ ExtendedVerticalDragGestureRecognizer({
+ super.debugOwner,
+ super.supportedDevices,
+ super.allowedButtonsFilter,
+ this.canHorizontalOrVerticalDrag,
+ });
+
+ @override
+ final CanHorizontalOrVerticalDrag? canHorizontalOrVerticalDrag;
+}
diff --git a/lib/src/gesture_detector/drag_gesture_recognizer_mixin.dart b/lib/src/gesture_detector/drag_gesture_recognizer_mixin.dart
deleted file mode 100644
index 386b1e51..00000000
--- a/lib/src/gesture_detector/drag_gesture_recognizer_mixin.dart
+++ /dev/null
@@ -1,32 +0,0 @@
-part of 'drag.dart';
-
-typedef CanHorizontalOrVerticalDrag = bool Function();
-mixin DragGestureRecognizerMixin {
- bool get canDrag =>
- canHorizontalOrVerticalDrag == null || canHorizontalOrVerticalDrag!();
- Map get _velocityTrackers;
-
- bool _shouldAccpet() {
- if (!canDrag) {
- return false;
- }
- if (_velocityTrackers.keys.length == 1) {
- return true;
- }
-
- // if pointers are not the only, check whether they are in the negative
- // maybe this is a Horizontal/Vertical zoom
- Offset offset = const Offset(1, 1);
- for (final VelocityTracker tracker in _velocityTrackers.values) {
- if (tracker is ExtendedVelocityTracker) {
- final Offset delta = tracker.getSamplesDelta();
- offset = Offset(offset.dx * (delta.dx == 0 ? 1 : delta.dx),
- offset.dy * (delta.dy == 0 ? 1 : delta.dy));
- }
- }
-
- return !(offset.dx < 0 || offset.dy < 0);
- }
-
- CanHorizontalOrVerticalDrag? get canHorizontalOrVerticalDrag;
-}
diff --git a/lib/src/gesture_detector/drag.dart b/lib/src/gesture_detector/official.dart
similarity index 63%
rename from lib/src/gesture_detector/drag.dart
rename to lib/src/gesture_detector/official.dart
index 3a211683..c187c832 100644
--- a/lib/src/gesture_detector/drag.dart
+++ b/lib/src/gesture_detector/official.dart
@@ -1,9 +1,10 @@
-// ignore_for_file: unnecessary_null_comparison
+// ignore_for_file: overridden_fields
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
-import 'velocity_tracker.dart';
-part 'drag_gesture_recognizer_mixin.dart';
+
+part 'drag_gesture_recognizer.dart';
+part 'velocity_tracker.dart';
enum _DragState {
ready,
@@ -11,155 +12,47 @@ enum _DragState {
accepted,
}
-/// Recognizes movement in the vertical direction.
-///
-/// Used for vertical scrolling.
-///
-/// See also:
-///
-/// * [ExtendedHorizontalDragGestureRecognizer], for a similar recognizer but for
-/// horizontal movement.
-/// * [MultiDragGestureRecognizer], for a family of gesture recognizers that
-/// track each touch point independently.
-class ExtendedVerticalDragGestureRecognizer
- extends ExtendedDragGestureRecognizer {
- /// Create a gesture recognizer for interactions in the vertical axis.
- ///
- /// {@macro flutter.gestures.GestureRecognizer.supportedDevices}
- ExtendedVerticalDragGestureRecognizer({
- super.debugOwner,
- @Deprecated(
- 'Migrate to supportedDevices. '
- 'This feature was deprecated after v2.3.0-1.0.pre.',
- )
- super.kind,
- super.supportedDevices,
- super.canHorizontalOrVerticalDrag,
- });
-
- @override
- bool isFlingGesture(VelocityEstimate estimate, PointerDeviceKind kind) {
- final double minVelocity = minFlingVelocity ?? kMinFlingVelocity;
- final double minDistance =
- minFlingDistance ?? computeHitSlop(kind, gestureSettings);
- return estimate.pixelsPerSecond.dy.abs() > minVelocity &&
- estimate.offset.dy.abs() > minDistance;
- }
-
- @override
- bool _hasSufficientGlobalDistanceToAccept(
- PointerDeviceKind pointerDeviceKind, double? deviceTouchSlop) {
- return _globalDistanceMoved.abs() >
- computeHitSlop(pointerDeviceKind, gestureSettings);
- }
-
- @override
- Offset _getDeltaForDetails(Offset delta) => Offset(0.0, delta.dy);
-
- @override
- double _getPrimaryValueFromOffset(Offset value) => value.dy;
-
- @override
- String get debugDescription => 'vertical drag';
-}
-
-/// Recognizes movement in the horizontal direction.
-///
-/// Used for horizontal scrolling.
-///
-/// See also:
-///
-/// * [VerticalDragGestureRecognizer], for a similar recognizer but for
-/// vertical movement.
-/// * [MultiDragGestureRecognizer], for a family of gesture recognizers that
-/// track each touch point independently.
-class ExtendedHorizontalDragGestureRecognizer
- extends ExtendedDragGestureRecognizer {
- /// Create a gesture recognizer for interactions in the horizontal axis.
- ///
- /// {@macro flutter.gestures.GestureRecognizer.supportedDevices}
- ExtendedHorizontalDragGestureRecognizer({
- super.debugOwner,
- @Deprecated(
- 'Migrate to supportedDevices. '
- 'This feature was deprecated after v2.3.0-1.0.pre.',
- )
- super.kind,
- super.supportedDevices,
- super.canHorizontalOrVerticalDrag,
- });
-
- @override
- bool isFlingGesture(VelocityEstimate estimate, PointerDeviceKind kind) {
- final double minVelocity = minFlingVelocity ?? kMinFlingVelocity;
- final double minDistance =
- minFlingDistance ?? computeHitSlop(kind, gestureSettings);
- return estimate.pixelsPerSecond.dx.abs() > minVelocity &&
- estimate.offset.dx.abs() > minDistance;
- }
-
- @override
- bool _hasSufficientGlobalDistanceToAccept(
- PointerDeviceKind pointerDeviceKind, double? deviceTouchSlop) {
- return _globalDistanceMoved.abs() >
- computeHitSlop(pointerDeviceKind, gestureSettings);
- }
-
- @override
- Offset _getDeltaForDetails(Offset delta) => Offset(delta.dx, 0.0);
-
- @override
- double _getPrimaryValueFromOffset(Offset value) => value.dx;
-
- @override
- String get debugDescription => 'horizontal drag';
-}
-
/// Recognizes movement.
///
-/// In contrast to [MultiDragGestureRecognizer], [ExtendedDragGestureRecognizer]
+/// In contrast to [MultiDragGestureRecognizer], [DragGestureRecognizer]
/// recognizes a single gesture sequence for all the pointers it watches, which
/// means that the recognizer has at most one drag sequence active at any given
/// time regardless of how many pointers are in contact with the screen.
///
-/// [ExtendedDragGestureRecognizer] is not intended to be used directly. Instead,
+/// [DragGestureRecognizer] is not intended to be used directly. Instead,
/// consider using one of its subclasses to recognize specific types for drag
/// gestures.
///
-/// [ExtendedDragGestureRecognizer] competes on pointer events of [kPrimaryButton]
-/// only when it has at least one non-null callback. If it has no callbacks, it
-/// is a no-op.
+/// [DragGestureRecognizer] competes on pointer events only when it has at
+/// least one non-null callback. If it has no callbacks, it is a no-op.
///
/// See also:
///
-/// * [ExtendedHorizontalDragGestureRecognizer], for left and right drags.
-/// * [VerticalDragGestureRecognizer], for up and down drags.
+/// * [_HorizontalDragGestureRecognizer], for left and right drags.
+/// * [_VerticalDragGestureRecognizer], for up and down drags.
/// * [PanGestureRecognizer], for drags that are not locked to a single axis.
-abstract class ExtendedDragGestureRecognizer
- extends OneSequenceGestureRecognizer with DragGestureRecognizerMixin {
+abstract class _DragGestureRecognizer extends OneSequenceGestureRecognizer {
/// Initialize the object.
///
/// [dragStartBehavior] must not be null.
///
/// {@macro flutter.gestures.GestureRecognizer.supportedDevices}
- ExtendedDragGestureRecognizer({
+ _DragGestureRecognizer({
super.debugOwner,
- @Deprecated(
- 'Migrate to supportedDevices. '
- 'This feature was deprecated after v2.3.0-1.0.pre.',
- )
- super.kind,
this.dragStartBehavior = DragStartBehavior.start,
this.velocityTrackerBuilder = _defaultBuilder,
super.supportedDevices,
- this.canHorizontalOrVerticalDrag,
- }) : assert(dragStartBehavior != null);
+ AllowedButtonsFilter? allowedButtonsFilter,
+ }) : super(
+ allowedButtonsFilter:
+ allowedButtonsFilter ?? _defaultButtonAcceptBehavior);
- @override
- final CanHorizontalOrVerticalDrag? canHorizontalOrVerticalDrag;
+ static _VelocityTracker _defaultBuilder(PointerEvent event) =>
+ _VelocityTracker.withKind(event.kind);
- static ExtendedVelocityTracker _defaultBuilder(PointerEvent event) =>
- ExtendedVelocityTracker.withKind(event.kind);
+ // Accept the input if, and only if, [kPrimaryButton] is pressed.
+ static bool _defaultButtonAcceptBehavior(int buttons) =>
+ buttons == kPrimaryButton;
/// Configure the behavior of offsets passed to [onStart].
///
@@ -177,10 +70,10 @@ abstract class ExtendedDragGestureRecognizer
///
/// ## Example:
///
- /// A [ExtendedHorizontalDragGestureRecognizer] and a [VerticalDragGestureRecognizer]
+ /// A [_HorizontalDragGestureRecognizer] and a [_VerticalDragGestureRecognizer]
/// compete with each other. A finger presses down on the screen with
/// offset (500.0, 500.0), and then moves to position (510.0, 500.0) before
- /// the [ExtendedHorizontalDragGestureRecognizer] wins the arena. With
+ /// the [_HorizontalDragGestureRecognizer] wins the arena. With
/// [dragStartBehavior] set to [DragStartBehavior.down], the [onStart]
/// callback will be called with position (500.0, 500.0). If it is
/// instead set to [DragStartBehavior.start], [onStart] will be called with
@@ -195,12 +88,14 @@ abstract class ExtendedDragGestureRecognizer
///
/// See also:
///
- /// * [kPrimaryButton], the button this callback responds to.
+ /// * [allowedButtonsFilter], which decides which button will be allowed.
/// * [DragDownDetails], which is passed as an argument to this callback.
GestureDragDownCallback? onDown;
+ /// {@template flutter.gestures.monodrag.DragGestureRecognizer.onStart}
/// A pointer has contacted the screen with a primary button and has begun to
/// move.
+ /// {@endtemplate}
///
/// The position of the pointer is provided in the callback's `details`
/// argument, which is a [DragStartDetails] object. The [dragStartBehavior]
@@ -208,32 +103,52 @@ abstract class ExtendedDragGestureRecognizer
///
/// See also:
///
- /// * [kPrimaryButton], the button this callback responds to.
+ /// * [allowedButtonsFilter], which decides which button will be allowed.
/// * [DragStartDetails], which is passed as an argument to this callback.
GestureDragStartCallback? onStart;
+ /// {@template flutter.gestures.monodrag.DragGestureRecognizer.onUpdate}
/// A pointer that is in contact with the screen with a primary button and
/// moving has moved again.
+ /// {@endtemplate}
///
/// The distance traveled by the pointer since the last update is provided in
/// the callback's `details` argument, which is a [DragUpdateDetails] object.
///
+ /// If this gesture recognizer recognizes movement on a single axis (a
+ /// [_VerticalDragGestureRecognizer] or [_HorizontalDragGestureRecognizer]),
+ /// then `details` will reflect movement only on that axis and its
+ /// [DragUpdateDetails.primaryDelta] will be non-null.
+ /// If this gesture recognizer recognizes movement in all directions
+ /// (a [PanGestureRecognizer]), then `details` will reflect movement on
+ /// both axes and its [DragUpdateDetails.primaryDelta] will be null.
+ ///
/// See also:
///
- /// * [kPrimaryButton], the button this callback responds to.
+ /// * [allowedButtonsFilter], which decides which button will be allowed.
/// * [DragUpdateDetails], which is passed as an argument to this callback.
GestureDragUpdateCallback? onUpdate;
+ /// {@template flutter.gestures.monodrag.DragGestureRecognizer.onEnd}
/// A pointer that was previously in contact with the screen with a primary
/// button and moving is no longer in contact with the screen and was moving
/// at a specific velocity when it stopped contacting the screen.
+ /// {@endtemplate}
///
/// The velocity is provided in the callback's `details` argument, which is a
/// [DragEndDetails] object.
///
+ /// If this gesture recognizer recognizes movement on a single axis (a
+ /// [_VerticalDragGestureRecognizer] or [_HorizontalDragGestureRecognizer]),
+ /// then `details` will reflect movement only on that axis and its
+ /// [DragEndDetails.primaryVelocity] will be non-null.
+ /// If this gesture recognizer recognizes movement in all directions
+ /// (a [PanGestureRecognizer]), then `details` will reflect movement on
+ /// both axes and its [DragEndDetails.primaryVelocity] will be null.
+ ///
/// See also:
///
- /// * [kPrimaryButton], the button this callback responds to.
+ /// * [allowedButtonsFilter], which decides which button will be allowed.
/// * [DragEndDetails], which is passed as an argument to this callback.
GestureDragEndCallback? onEnd;
@@ -241,10 +156,10 @@ abstract class ExtendedDragGestureRecognizer
///
/// See also:
///
- /// * [kPrimaryButton], the button this callback responds to.
+ /// * [allowedButtonsFilter], which decides which button will be allowed.
GestureDragCancelCallback? onCancel;
- /// The minimum distance an input pointer drag must have moved to
+ /// The minimum distance an input pointer drag must have moved
/// to be considered a fling gesture.
///
/// This value is typically compared with the distance traveled along the
@@ -266,7 +181,7 @@ abstract class ExtendedDragGestureRecognizer
/// Determines the type of velocity estimation method to use for a potential
/// drag gesture, when a new pointer is added.
///
- /// To estimate the velocity of a gesture, [ExtendedDragGestureRecognizer] calls
+ /// To estimate the velocity of a gesture, [DragGestureRecognizer] calls
/// [velocityTrackerBuilder] when it starts to track a new pointer in
/// [addAllowedPointer], and add subsequent updates on the pointer to the
/// resulting velocity tracker, until the gesture recognizer stops tracking
@@ -275,11 +190,11 @@ abstract class ExtendedDragGestureRecognizer
/// tracker this [GestureVelocityTrackerBuilder] returns.
///
/// If left unspecified the default [velocityTrackerBuilder] creates a new
- /// [VelocityTracker] for every pointer added.
+ /// [_VelocityTracker] for every pointer added.
///
/// See also:
///
- /// * [VelocityTracker], a velocity tracker that uses least squares estimation
+ /// * [_VelocityTracker], a velocity tracker that uses least squares estimation
/// on the 20 most recent pointer data samples. It's a well-rounded velocity
/// tracker and is used by default.
/// * [IOSScrollViewFlingVelocityTracker], a specialized velocity tracker for
@@ -291,6 +206,24 @@ abstract class ExtendedDragGestureRecognizer
late OffsetPair _initialPosition;
late OffsetPair _pendingDragOffset;
Duration? _lastPendingEventTimestamp;
+
+ /// When asserts are enabled, returns the last tracked pending event timestamp
+ /// for this recognizer.
+ ///
+ /// Otherwise, returns null.
+ ///
+ /// This getter is intended for use in framework unit tests. Applications must
+ /// not depend on its value.
+ @visibleForTesting
+ Duration? get debugLastPendingEventTimestamp {
+ Duration? lastPendingEventTimestamp;
+ assert(() {
+ lastPendingEventTimestamp = _lastPendingEventTimestamp;
+ return true;
+ }());
+ return lastPendingEventTimestamp;
+ }
+
// The buttons sent by `PointerDownEvent`. If a `PointerMoveEvent` comes with a
// different set of buttons, the gesture is canceled.
int? _initialButtons;
@@ -309,29 +242,30 @@ abstract class ExtendedDragGestureRecognizer
/// inertia, for example.
bool isFlingGesture(VelocityEstimate estimate, PointerDeviceKind kind);
+ /// Determines if a gesture is a fling or not, and if so its effective velocity.
+ ///
+ /// A fling calls its gesture end callback with a velocity, allowing the
+ /// provider of the callback to respond by carrying the gesture forward with
+ /// inertia, for example.
+ DragEndDetails? _considerFling(
+ VelocityEstimate estimate, PointerDeviceKind kind);
+
Offset _getDeltaForDetails(Offset delta);
double? _getPrimaryValueFromOffset(Offset value);
bool _hasSufficientGlobalDistanceToAccept(
PointerDeviceKind pointerDeviceKind, double? deviceTouchSlop);
- @override
final Map _velocityTrackers = {};
@override
bool isPointerAllowed(PointerEvent event) {
if (_initialButtons == null) {
- switch (event.buttons) {
- case kPrimaryButton:
- if (onDown == null &&
- onStart == null &&
- onUpdate == null &&
- onEnd == null &&
- onCancel == null) {
- return false;
- }
- break;
- default:
- return false;
+ if (onDown == null &&
+ onStart == null &&
+ onUpdate == null &&
+ onEnd == null &&
+ onCancel == null) {
+ return false;
}
} else {
// There can be multiple drags simultaneously. Their effects are combined.
@@ -386,7 +320,6 @@ abstract class ExtendedDragGestureRecognizer
event is PointerPanZoomStartEvent ||
event is PointerPanZoomUpdateEvent)) {
final VelocityTracker tracker = _velocityTrackers[event.pointer]!;
- assert(tracker != null);
if (event is PointerPanZoomStartEvent) {
tracker.addPosition(event.timeStamp, Offset.zero);
} else if (event is PointerPanZoomUpdateEvent) {
@@ -436,9 +369,7 @@ abstract class ExtendedDragGestureRecognizer
.distance *
(_getPrimaryValueFromOffset(movedLocally) ?? 1).sign;
if (_hasSufficientGlobalDistanceToAccept(
- event.kind, gestureSettings?.touchSlop) &&
- // zmtzawqlp
- _shouldAccpet()) {
+ event.kind, gestureSettings?.touchSlop)) {
resolve(GestureDisposition.accepted);
}
}
@@ -459,7 +390,7 @@ abstract class ExtendedDragGestureRecognizer
if (_state != _DragState.accepted) {
_state = _DragState.accepted;
final OffsetPair delta = _pendingDragOffset;
- final Duration timestamp = _lastPendingEventTimestamp!;
+ final Duration? timestamp = _lastPendingEventTimestamp;
final Matrix4? transform = _lastTransform;
final Offset localUpdateDelta;
switch (dragStartBehavior) {
@@ -521,7 +452,6 @@ abstract class ExtendedDragGestureRecognizer
resolve(GestureDisposition.rejected);
_checkCancel();
break;
-
case _DragState.accepted:
_checkEnd(pointer);
break;
@@ -541,7 +471,6 @@ abstract class ExtendedDragGestureRecognizer
}
void _checkDown() {
- assert(_initialButtons == kPrimaryButton);
if (onDown != null) {
final DragDownDetails details = DragDownDetails(
globalPosition: _initialPosition.global,
@@ -551,8 +480,7 @@ abstract class ExtendedDragGestureRecognizer
}
}
- void _checkStart(Duration timestamp, int pointer) {
- assert(_initialButtons == kPrimaryButton);
+ void _checkStart(Duration? timestamp, int pointer) {
if (onStart != null) {
final DragStartDetails details = DragStartDetails(
sourceTimeStamp: timestamp,
@@ -571,7 +499,6 @@ abstract class ExtendedDragGestureRecognizer
required Offset globalPosition,
Offset? localPosition,
}) {
- assert(_initialButtons == kPrimaryButton);
if (onUpdate != null) {
final DragUpdateDetails details = DragUpdateDetails(
sourceTimeStamp: sourceTimeStamp,
@@ -585,47 +512,30 @@ abstract class ExtendedDragGestureRecognizer
}
void _checkEnd(int pointer) {
- assert(_initialButtons == kPrimaryButton);
if (onEnd == null) {
return;
}
final VelocityTracker tracker = _velocityTrackers[pointer]!;
- assert(tracker != null);
+ final VelocityEstimate? estimate = tracker.getVelocityEstimate();
- final DragEndDetails details;
+ DragEndDetails? details;
final String Function() debugReport;
-
- final VelocityEstimate? estimate = tracker.getVelocityEstimate();
- if (estimate != null && isFlingGesture(estimate, tracker.kind)) {
- final Velocity velocity =
- Velocity(pixelsPerSecond: estimate.pixelsPerSecond).clampMagnitude(
- minFlingVelocity ?? kMinFlingVelocity,
- maxFlingVelocity ?? kMaxFlingVelocity);
- details = DragEndDetails(
- velocity: velocity,
- primaryVelocity: _getPrimaryValueFromOffset(velocity.pixelsPerSecond),
- );
- debugReport = () {
- return '$estimate; fling at $velocity.';
- };
+ if (estimate == null) {
+ debugReport = () => 'Could not estimate velocity.';
} else {
- details = DragEndDetails(
- primaryVelocity: 0.0,
- );
- debugReport = () {
- if (estimate == null) {
- return 'Could not estimate velocity.';
- }
- return '$estimate; judged to not be a fling.';
- };
+ details = _considerFling(estimate, tracker.kind);
+ debugReport = (details != null)
+ ? () => '$estimate; fling at ${details!.velocity}.'
+ : () => '$estimate; judged to not be a fling.';
}
- invokeCallback('onEnd', () => onEnd!(details),
+ details ??= DragEndDetails(primaryVelocity: 0.0);
+
+ invokeCallback('onEnd', () => onEnd!(details!),
debugReport: debugReport);
}
void _checkCancel() {
- assert(_initialButtons == kPrimaryButton);
if (onCancel != null) {
invokeCallback('onCancel', onCancel!);
}
@@ -644,3 +554,272 @@ abstract class ExtendedDragGestureRecognizer
EnumProperty('start behavior', dragStartBehavior));
}
}
+
+/// Recognizes movement in the vertical direction.
+///
+/// Used for vertical scrolling.
+///
+/// See also:
+///
+/// * [_HorizontalDragGestureRecognizer], for a similar recognizer but for
+/// horizontal movement.
+/// * [MultiDragGestureRecognizer], for a family of gesture recognizers that
+/// track each touch point independently.
+class _VerticalDragGestureRecognizer extends _DragGestureRecognizer {
+ /// Create a gesture recognizer for interactions in the vertical axis.
+ ///
+ /// {@macro flutter.gestures.GestureRecognizer.supportedDevices}
+ _VerticalDragGestureRecognizer({
+ super.debugOwner,
+ super.supportedDevices,
+ super.allowedButtonsFilter,
+ });
+
+ @override
+ bool isFlingGesture(VelocityEstimate estimate, PointerDeviceKind kind) {
+ final double minVelocity = minFlingVelocity ?? kMinFlingVelocity;
+ final double minDistance =
+ minFlingDistance ?? computeHitSlop(kind, gestureSettings);
+ return estimate.pixelsPerSecond.dy.abs() > minVelocity &&
+ estimate.offset.dy.abs() > minDistance;
+ }
+
+ @override
+ DragEndDetails? _considerFling(
+ VelocityEstimate estimate, PointerDeviceKind kind) {
+ if (!isFlingGesture(estimate, kind)) {
+ return null;
+ }
+ final double maxVelocity = maxFlingVelocity ?? kMaxFlingVelocity;
+ final double dy =
+ clampDouble(estimate.pixelsPerSecond.dy, -maxVelocity, maxVelocity);
+ return DragEndDetails(
+ velocity: Velocity(pixelsPerSecond: Offset(0, dy)),
+ primaryVelocity: dy,
+ );
+ }
+
+ @override
+ bool _hasSufficientGlobalDistanceToAccept(
+ PointerDeviceKind pointerDeviceKind, double? deviceTouchSlop) {
+ return _globalDistanceMoved.abs() >
+ computeHitSlop(pointerDeviceKind, gestureSettings);
+ }
+
+ @override
+ Offset _getDeltaForDetails(Offset delta) => Offset(0.0, delta.dy);
+
+ @override
+ double _getPrimaryValueFromOffset(Offset value) => value.dy;
+
+ @override
+ String get debugDescription => 'vertical drag';
+}
+
+/// Recognizes movement in the horizontal direction.
+///
+/// Used for horizontal scrolling.
+///
+/// See also:
+///
+/// * [_VerticalDragGestureRecognizer], for a similar recognizer but for
+/// vertical movement.
+/// * [MultiDragGestureRecognizer], for a family of gesture recognizers that
+/// track each touch point independently.
+class _HorizontalDragGestureRecognizer extends _DragGestureRecognizer {
+ /// Create a gesture recognizer for interactions in the horizontal axis.
+ ///
+ /// {@macro flutter.gestures.GestureRecognizer.supportedDevices}
+ _HorizontalDragGestureRecognizer({
+ super.debugOwner,
+ super.supportedDevices,
+ super.allowedButtonsFilter,
+ });
+
+ @override
+ bool isFlingGesture(VelocityEstimate estimate, PointerDeviceKind kind) {
+ final double minVelocity = minFlingVelocity ?? kMinFlingVelocity;
+ final double minDistance =
+ minFlingDistance ?? computeHitSlop(kind, gestureSettings);
+ return estimate.pixelsPerSecond.dx.abs() > minVelocity &&
+ estimate.offset.dx.abs() > minDistance;
+ }
+
+ @override
+ DragEndDetails? _considerFling(
+ VelocityEstimate estimate, PointerDeviceKind kind) {
+ if (!isFlingGesture(estimate, kind)) {
+ return null;
+ }
+ final double maxVelocity = maxFlingVelocity ?? kMaxFlingVelocity;
+ final double dx =
+ clampDouble(estimate.pixelsPerSecond.dx, -maxVelocity, maxVelocity);
+ return DragEndDetails(
+ velocity: Velocity(pixelsPerSecond: Offset(dx, 0)),
+ primaryVelocity: dx,
+ );
+ }
+
+ @override
+ bool _hasSufficientGlobalDistanceToAccept(
+ PointerDeviceKind pointerDeviceKind, double? deviceTouchSlop) {
+ return _globalDistanceMoved.abs() >
+ computeHitSlop(pointerDeviceKind, gestureSettings);
+ }
+
+ @override
+ Offset _getDeltaForDetails(Offset delta) => Offset(delta.dx, 0.0);
+
+ @override
+ double _getPrimaryValueFromOffset(Offset value) => value.dx;
+
+ @override
+ String get debugDescription => 'horizontal drag';
+}
+
+class _PointAtTime {
+ const _PointAtTime(this.point, this.time);
+
+ final Duration time;
+ final Offset point;
+
+ @override
+ String toString() => '_PointAtTime($point at $time)';
+}
+
+// Computes a pointer's velocity based on data from [PointerMoveEvent]s.
+///
+/// The input data is provided by calling [addPosition]. Adding data is cheap.
+///
+/// To obtain a velocity, call [getVelocity] or [getVelocityEstimate]. This will
+/// compute the velocity based on the data added so far. Only call these when
+/// you need to use the velocity, as they are comparatively expensive.
+///
+/// The quality of the velocity estimation will be better if more data points
+/// have been received.
+class _VelocityTracker extends VelocityTracker {
+ /// Create a new velocity tracker for a pointer [kind].
+ _VelocityTracker.withKind(this.kind) : super.withKind(kind);
+
+ static const int _assumePointerMoveStoppedMilliseconds = 40;
+ static const int _historySize = 20;
+ static const int _horizonMilliseconds = 100;
+ static const int _minSampleSize = 3;
+
+ /// The kind of pointer this tracker is for.
+ @override
+ final PointerDeviceKind kind;
+
+ // Circular buffer; current sample at _index.
+ final List<_PointAtTime?> _samples =
+ List<_PointAtTime?>.filled(_historySize, null);
+ int _index = 0;
+
+ /// Adds a position as the given time to the tracker.
+ @override
+ void addPosition(Duration time, Offset position) {
+ _index += 1;
+ if (_index == _historySize) {
+ _index = 0;
+ }
+ _samples[_index] = _PointAtTime(position, time);
+ }
+
+ /// Returns an estimate of the velocity of the object being tracked by the
+ /// tracker given the current information available to the tracker.
+ ///
+ /// Information is added using [addPosition].
+ ///
+ /// Returns null if there is no data on which to base an estimate.
+ @override
+ VelocityEstimate? getVelocityEstimate() {
+ final List x = [];
+ final List y = [];
+ final List w = [];
+ final List time = [];
+ int sampleCount = 0;
+ int index = _index;
+
+ final _PointAtTime? newestSample = _samples[index];
+ if (newestSample == null) {
+ return null;
+ }
+
+ _PointAtTime previousSample = newestSample;
+ _PointAtTime oldestSample = newestSample;
+
+ // Starting with the most recent PointAtTime sample, iterate backwards while
+ // the samples represent continuous motion.
+ do {
+ final _PointAtTime? sample = _samples[index];
+ if (sample == null) {
+ break;
+ }
+
+ final double age =
+ (newestSample.time - sample.time).inMicroseconds.toDouble() / 1000;
+ final double delta =
+ (sample.time - previousSample.time).inMicroseconds.abs().toDouble() /
+ 1000;
+ previousSample = sample;
+ if (age > _horizonMilliseconds ||
+ delta > _assumePointerMoveStoppedMilliseconds) {
+ break;
+ }
+
+ oldestSample = sample;
+ final Offset position = sample.point;
+ x.add(position.dx);
+ y.add(position.dy);
+ w.add(1.0);
+ time.add(-age);
+ index = (index == 0 ? _historySize : index) - 1;
+
+ sampleCount += 1;
+ } while (sampleCount < _historySize);
+
+ if (sampleCount >= _minSampleSize) {
+ final LeastSquaresSolver xSolver = LeastSquaresSolver(time, x, w);
+ final PolynomialFit? xFit = xSolver.solve(2);
+ if (xFit != null) {
+ final LeastSquaresSolver ySolver = LeastSquaresSolver(time, y, w);
+ final PolynomialFit? yFit = ySolver.solve(2);
+ if (yFit != null) {
+ return VelocityEstimate(
+ // convert from pixels/ms to pixels/s
+ pixelsPerSecond: Offset(
+ xFit.coefficients[1] * 1000, yFit.coefficients[1] * 1000),
+ confidence: xFit.confidence * yFit.confidence,
+ duration: newestSample.time - oldestSample.time,
+ offset: newestSample.point - oldestSample.point,
+ );
+ }
+ }
+ }
+
+ // We're unable to make a velocity estimate but we did have at least one
+ // valid pointer position.
+ return VelocityEstimate(
+ pixelsPerSecond: Offset.zero,
+ confidence: 1.0,
+ duration: newestSample.time - oldestSample.time,
+ offset: newestSample.point - oldestSample.point,
+ );
+ }
+
+ /// Computes the velocity of the pointer at the time of the last
+ /// provided data point.
+ ///
+ /// This can be expensive. Only call this when you need the velocity.
+ ///
+ /// Returns [Velocity.zero] if there is no data from which to compute an
+ /// estimate or if the estimated velocity is zero.
+ @override
+ Velocity getVelocity() {
+ final VelocityEstimate? estimate = getVelocityEstimate();
+ if (estimate == null || estimate.pixelsPerSecond == Offset.zero) {
+ return Velocity.zero;
+ }
+ return Velocity(pixelsPerSecond: estimate.pixelsPerSecond);
+ }
+}
diff --git a/lib/src/gesture_detector/velocity_tracker.dart b/lib/src/gesture_detector/velocity_tracker.dart
index 3c83598b..3418f048 100644
--- a/lib/src/gesture_detector/velocity_tracker.dart
+++ b/lib/src/gesture_detector/velocity_tracker.dart
@@ -1,146 +1,27 @@
-// ignore_for_file: unnecessary_null_comparison
-
-import 'package:flutter/gestures.dart';
-part 'velocity_tracker_mixin.dart';
-
-class _PointAtTime {
- const _PointAtTime(this.point, this.time)
- : assert(point != null),
- assert(time != null);
-
- final Duration time;
- final Offset point;
-
- @override
- String toString() => '_PointAtTime($point at $time)';
-}
-
-class ExtendedVelocityTracker extends VelocityTracker
- with VelocityTrackerMixin {
- /// Create a new velocity tracker for a pointer [kind].
- ExtendedVelocityTracker.withKind(PointerDeviceKind kind)
- : super.withKind(kind);
-
- static const int _assumePointerMoveStoppedMilliseconds = 40;
- static const int _historySize = 20;
- static const int _horizonMilliseconds = 100;
- static const int _minSampleSize = 3;
-
- // /// The kind of pointer this tracker is for.
- // @override
- // final PointerDeviceKind kind;
-
- // Circular buffer; current sample at _index.
- @override
- final List<_PointAtTime?> _samples =
- List<_PointAtTime?>.filled(_historySize, null);
- int _index = 0;
-
- /// Adds a position as the given time to the tracker.
- @override
- void addPosition(Duration time, Offset position) {
- _index += 1;
- if (_index == _historySize) {
- _index = 0;
- }
- _samples[_index] = _PointAtTime(position, time);
- }
-
- /// Returns an estimate of the velocity of the object being tracked by the
- /// tracker given the current information available to the tracker.
- ///
- /// Information is added using [addPosition].
- ///
- /// Returns null if there is no data on which to base an estimate.
- @override
- VelocityEstimate? getVelocityEstimate() {
- final List x = [];
- final List y = [];
- final List w = [];
- final List time = [];
- int sampleCount = 0;
- int index = _index;
-
- final _PointAtTime? newestSample = _samples[index];
- if (newestSample == null) {
- return null;
- }
-
- _PointAtTime previousSample = newestSample;
- _PointAtTime oldestSample = newestSample;
-
- // Starting with the most recent PointAtTime sample, iterate backwards while
- // the samples represent continuous motion.
- do {
- final _PointAtTime? sample = _samples[index];
- if (sample == null) {
+part of 'official.dart';
+
+class ExtendedVelocityTracker extends _VelocityTracker {
+ ExtendedVelocityTracker.withKind(super.kind) : super.withKind();
+ Offset getSamplesDelta() {
+ Offset? first;
+ Offset? last;
+ for (int i = 0; i < _samples.length; i++) {
+ final _PointAtTime? d = _samples[i];
+ if (d != null && first == null) {
+ first = d.point;
break;
}
+ }
- final double age =
- (newestSample.time - sample.time).inMicroseconds.toDouble() / 1000;
- final double delta =
- (sample.time - previousSample.time).inMicroseconds.abs().toDouble() /
- 1000;
- previousSample = sample;
- if (age > _horizonMilliseconds ||
- delta > _assumePointerMoveStoppedMilliseconds) {
+ for (int i = _samples.length - 1; i >= 0; i--) {
+ final _PointAtTime? d = _samples[i];
+ if (d != null && last == null) {
+ last = d.point;
break;
}
-
- oldestSample = sample;
- final Offset position = sample.point;
- x.add(position.dx);
- y.add(position.dy);
- w.add(1.0);
- time.add(-age);
- index = (index == 0 ? _historySize : index) - 1;
-
- sampleCount += 1;
- } while (sampleCount < _historySize);
-
- if (sampleCount >= _minSampleSize) {
- final LeastSquaresSolver xSolver = LeastSquaresSolver(time, x, w);
- final PolynomialFit? xFit = xSolver.solve(2);
- if (xFit != null) {
- final LeastSquaresSolver ySolver = LeastSquaresSolver(time, y, w);
- final PolynomialFit? yFit = ySolver.solve(2);
- if (yFit != null) {
- return VelocityEstimate(
- // convert from pixels/ms to pixels/s
- pixelsPerSecond: Offset(
- xFit.coefficients[1] * 1000, yFit.coefficients[1] * 1000),
- confidence: xFit.confidence * yFit.confidence,
- duration: newestSample.time - oldestSample.time,
- offset: newestSample.point - oldestSample.point,
- );
- }
- }
- }
-
- // We're unable to make a velocity estimate but we did have at least one
- // valid pointer position.
- return VelocityEstimate(
- pixelsPerSecond: Offset.zero,
- confidence: 1.0,
- duration: newestSample.time - oldestSample.time,
- offset: newestSample.point - oldestSample.point,
- );
- }
-
- /// Computes the velocity of the pointer at the time of the last
- /// provided data point.
- ///
- /// This can be expensive. Only call this when you need the velocity.
- ///
- /// Returns [Velocity.zero] if there is no data from which to compute an
- /// estimate or if the estimated velocity is zero.
- @override
- Velocity getVelocity() {
- final VelocityEstimate? estimate = getVelocityEstimate();
- if (estimate == null || estimate.pixelsPerSecond == Offset.zero) {
- return Velocity.zero;
}
- return Velocity(pixelsPerSecond: estimate.pixelsPerSecond);
+ last ??= Offset.zero;
+ first ??= Offset.zero;
+ return last - first;
}
}
diff --git a/lib/src/gesture_detector/velocity_tracker_mixin.dart b/lib/src/gesture_detector/velocity_tracker_mixin.dart
deleted file mode 100644
index 15006a2d..00000000
--- a/lib/src/gesture_detector/velocity_tracker_mixin.dart
+++ /dev/null
@@ -1,27 +0,0 @@
-part of 'velocity_tracker.dart';
-
-mixin VelocityTrackerMixin on VelocityTracker {
- List<_PointAtTime?> get _samples;
- Offset getSamplesDelta() {
- Offset? first;
- Offset? last;
- for (int i = 0; i < _samples.length; i++) {
- final _PointAtTime? d = _samples[i];
- if (d != null && first == null) {
- first = d.point;
- break;
- }
- }
-
- for (int i = _samples.length - 1; i >= 0; i--) {
- final _PointAtTime? d = _samples[i];
- if (d != null && last == null) {
- last = d.point;
- break;
- }
- }
- last ??= Offset.zero;
- first ??= Offset.zero;
- return last - first;
- }
-}
diff --git a/lib/src/image/painting.dart b/lib/src/image/painting.dart
index c9b4b437..aa5c53c0 100644
--- a/lib/src/image/painting.dart
+++ b/lib/src/image/painting.dart
@@ -1,3 +1,5 @@
+// ignore_for_file: invalid_use_of_visible_for_testing_member
+
import 'dart:math';
import 'dart:ui' as ui show Image;
import 'package:extended_image/extended_image.dart';
@@ -180,7 +182,8 @@ void paintExtendedImage(
if (needSave) {
canvas.save();
}
- if (repeat != ImageRepeat.noRepeat) {
+ if (repeat != ImageRepeat.noRepeat && centerSlice != null) {
+ // Don't clip if an image shader is used.
canvas.clipRect(paintRect);
}
if (flipHorizontally) {
@@ -196,9 +199,12 @@ void paintExtendedImage(
if (repeat == ImageRepeat.noRepeat) {
canvas.drawImageRect(image, sourceRect, destinationRect, paint);
} else {
- for (final Rect tileRect
- in _generateImageTileRects(rect, destinationRect, repeat))
- canvas.drawImageRect(image, sourceRect, tileRect, paint);
+ final ImageTilingInfo info =
+ createTilingInfo(repeat, rect, destinationRect, sourceRect);
+ final ImageShader shader = ImageShader(
+ image, info.tmx, info.tmy, info.transform.storage,
+ filterQuality: filterQuality);
+ canvas.drawRect(rect, paint..shader = shader);
}
} else {
canvas.scale(1 / scale);
@@ -226,8 +232,8 @@ void paintExtendedImage(
}
}
-Iterable _generateImageTileRects(
- Rect outputRect, Rect fundamentalRect, ImageRepeat repeat) sync* {
+List _generateImageTileRects(
+ Rect outputRect, Rect fundamentalRect, ImageRepeat repeat) {
int startX = 0;
int startY = 0;
int stopX = 0;
@@ -245,11 +251,11 @@ Iterable _generateImageTileRects(
stopY = ((outputRect.bottom - fundamentalRect.bottom) / strideY).ceil();
}
- for (int i = startX; i <= stopX; ++i) {
- for (int j = startY; j <= stopY; ++j) {
- yield fundamentalRect.shift(Offset(i * strideX, j * strideY));
- }
- }
+ return [
+ for (int i = startX; i <= stopX; ++i)
+ for (int j = startY; j <= stopY; ++j)
+ fundamentalRect.shift(Offset(i * strideX, j * strideY)),
+ ];
}
Rect _scaleRect(Rect rect, double scale) => Rect.fromLTRB(rect.left * scale,
diff --git a/lib/src/utils.dart b/lib/src/utils.dart
index 3de26b10..6c2c2faa 100644
--- a/lib/src/utils.dart
+++ b/lib/src/utils.dart
@@ -13,7 +13,7 @@ enum LoadState {
failed
}
-abstract class ExtendedImageState {
+mixin ExtendedImageState {
void reLoadImage();
ImageInfo? get extendedImageInfo;
LoadState get extendedImageLoadState;
diff --git a/pubspec.yaml b/pubspec.yaml
index 9c8e7d94..1a8b6780 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,11 +1,11 @@
name: extended_image
description: Official extension image, support placeholder(loading)/ failed state, cache network, zoom/pan, photo view, slide out page, editor(crop,rotate,flip), painting etc.
-version: 7.0.2
+version: 8.0.1
homepage: https://github.com/fluttercandies/extended_image
environment:
- sdk: '>=2.18.0 <3.0.0'
- flutter: '>=3.7.0'
+ sdk: '>=2.18.0 <4.0.0'
+ flutter: '>=3.10.0'
dependencies:
extended_image_library: ^3.4.0