diff --git a/lib/my_flutter_drag_scale/.metadata b/lib/my_flutter_drag_scale/.metadata
new file mode 100644
index 0000000..317f3d1
--- /dev/null
+++ b/lib/my_flutter_drag_scale/.metadata
@@ -0,0 +1,10 @@
+# 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.
+
+version:
+ revision: 035e0765cc575c3b455689c2402cce073d564fce
+ channel: master
+
+project_type: plugin
diff --git a/lib/my_flutter_drag_scale/CHANGELOG.md b/lib/my_flutter_drag_scale/CHANGELOG.md
new file mode 100644
index 0000000..41cc7d8
--- /dev/null
+++ b/lib/my_flutter_drag_scale/CHANGELOG.md
@@ -0,0 +1,3 @@
+## 0.0.1
+
+* TODO: Describe initial release.
diff --git a/lib/my_flutter_drag_scale/LICENSE b/lib/my_flutter_drag_scale/LICENSE
new file mode 100644
index 0000000..ba75c69
--- /dev/null
+++ b/lib/my_flutter_drag_scale/LICENSE
@@ -0,0 +1 @@
+TODO: Add your license here.
diff --git a/lib/my_flutter_drag_scale/README.md b/lib/my_flutter_drag_scale/README.md
new file mode 100644
index 0000000..dbdc878
--- /dev/null
+++ b/lib/my_flutter_drag_scale/README.md
@@ -0,0 +1,68 @@
+# flutter_drag_scale
+
+A new flutter plugin project.
+
+
+```
+可缩放可拖拽的功能,可实现图片或者其他widget的缩放已经拖拽
+并支持双击放大的功能
+```
+wechat :674668211 加微信进flutter微信群
+
+掘金: https://juejin.im/user/581206302f301e005c60cd2f
+
+简书:https://www.jianshu.com/u/4a5dce56807b
+
+csdn:https://me.csdn.net/liu__520
+
+github : https://github.com/LiuC520/
+
+
+我们知道官方提供了双击缩放,但是不支持拖拽的功能,我们要实现向百度地图那样可以缩放又可以拖拽的功能,官方的方法就不支持了。
+下面先演示下功能:
+
+
+参数只有两个:
+1、child ,是一个widget,可以是图片或者任意的widget
+2、doubleTapStillScale,默认是true,意思是双击一直放大,还是只放大一次,再次双击缩小到原图片的大小,如果为false,第一次双击放大图片2倍,再次双击回位。
+
+用法很简单:
+1、导入依赖库
+```
+dependencies:
+ flutter:
+ sdk: flutter
+ flutter_drag_scale:
+ git: https://github.com/LiuC520/flutter_drag_scale.git
+```
+2、引入库:
+```
+import 'package:flutter_drag_scale/flutter_drag_scale.dart';
+```
+3、如下的用法:
+```
+import 'package:flutter/material.dart';
+import 'package:flutter_drag_scale/flutter_drag_scale.dart';
+
+void main() => runApp(MyApp());
+
+class MyApp extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ height: 400.0,
+ width: 400,
+ child: Center(
+ child: DragScaleContainer(
+ doubleTapStillScale: true,
+ child: new Image(
+ image: new NetworkImage(
+ 'http://h.hiphotos.baidu.com/zhidao/wh%3D450%2C600/sign=0d023672312ac65c67506e77cec29e27/9f2f070828381f30dea167bbad014c086e06f06c.jpg'),
+ ),
+ ),
+ ),
+ );
+ }
+}
+
+```
\ No newline at end of file
diff --git a/lib/my_flutter_drag_scale/android/build.gradle b/lib/my_flutter_drag_scale/android/build.gradle
new file mode 100644
index 0000000..13b9989
--- /dev/null
+++ b/lib/my_flutter_drag_scale/android/build.gradle
@@ -0,0 +1,34 @@
+group 'com.example.flutter_drag_scale'
+version '1.0-SNAPSHOT'
+
+buildscript {
+ repositories {
+ google()
+ jcenter()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.2.1'
+ }
+}
+
+rootProject.allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 28
+
+ defaultConfig {
+ minSdkVersion 16
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ lintOptions {
+ disable 'InvalidPackage'
+ }
+}
diff --git a/lib/my_flutter_drag_scale/android/gradle.properties b/lib/my_flutter_drag_scale/android/gradle.properties
new file mode 100644
index 0000000..8bd86f6
--- /dev/null
+++ b/lib/my_flutter_drag_scale/android/gradle.properties
@@ -0,0 +1 @@
+org.gradle.jvmargs=-Xmx1536M
diff --git a/lib/my_flutter_drag_scale/android/settings.gradle b/lib/my_flutter_drag_scale/android/settings.gradle
new file mode 100644
index 0000000..c6b9053
--- /dev/null
+++ b/lib/my_flutter_drag_scale/android/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = 'flutter_drag_scale'
diff --git a/lib/my_flutter_drag_scale/android/src/main/AndroidManifest.xml b/lib/my_flutter_drag_scale/android/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..5fa7a25
--- /dev/null
+++ b/lib/my_flutter_drag_scale/android/src/main/AndroidManifest.xml
@@ -0,0 +1,3 @@
+
+
diff --git a/lib/my_flutter_drag_scale/android/src/main/java/com/example/flutter_drag_scale/FlutterDragScalePlugin.java b/lib/my_flutter_drag_scale/android/src/main/java/com/example/flutter_drag_scale/FlutterDragScalePlugin.java
new file mode 100644
index 0000000..4af8578
--- /dev/null
+++ b/lib/my_flutter_drag_scale/android/src/main/java/com/example/flutter_drag_scale/FlutterDragScalePlugin.java
@@ -0,0 +1,25 @@
+package com.example.flutter_drag_scale;
+
+import io.flutter.plugin.common.MethodCall;
+import io.flutter.plugin.common.MethodChannel;
+import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
+import io.flutter.plugin.common.MethodChannel.Result;
+import io.flutter.plugin.common.PluginRegistry.Registrar;
+
+/** FlutterDragScalePlugin */
+public class FlutterDragScalePlugin implements MethodCallHandler {
+ /** Plugin registration. */
+ public static void registerWith(Registrar registrar) {
+ final MethodChannel channel = new MethodChannel(registrar.messenger(), "flutter_drag_scale");
+ channel.setMethodCallHandler(new FlutterDragScalePlugin());
+ }
+
+ @Override
+ public void onMethodCall(MethodCall call, Result result) {
+ if (call.method.equals("getPlatformVersion")) {
+ result.success("Android " + android.os.Build.VERSION.RELEASE);
+ } else {
+ result.notImplemented();
+ }
+ }
+}
diff --git a/lib/my_flutter_drag_scale/example/.metadata b/lib/my_flutter_drag_scale/example/.metadata
new file mode 100644
index 0000000..66fe509
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/.metadata
@@ -0,0 +1,10 @@
+# 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.
+
+version:
+ revision: 035e0765cc575c3b455689c2402cce073d564fce
+ channel: master
+
+project_type: app
diff --git a/lib/my_flutter_drag_scale/example/README.md b/lib/my_flutter_drag_scale/example/README.md
new file mode 100644
index 0000000..b681c3f
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/README.md
@@ -0,0 +1,16 @@
+# flutter_drag_scale_example
+
+Demonstrates how to use the flutter_drag_scale plugin.
+
+## Getting Started
+
+This project is a starting point for a Flutter application.
+
+A few resources to get you started if this is your first Flutter project:
+
+- [Lab: Write your first Flutter app](https://flutter.io/docs/get-started/codelab)
+- [Cookbook: Useful Flutter samples](https://flutter.io/docs/cookbook)
+
+For help getting started with Flutter, view our
+[online documentation](https://flutter.io/docs), which offers tutorials,
+samples, guidance on mobile development, and a full API reference.
diff --git a/lib/my_flutter_drag_scale/example/android/app/build.gradle b/lib/my_flutter_drag_scale/example/android/app/build.gradle
new file mode 100644
index 0000000..eda47ed
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/android/app/build.gradle
@@ -0,0 +1,61 @@
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+ localPropertiesFile.withReader('UTF-8') { reader ->
+ localProperties.load(reader)
+ }
+}
+
+def flutterRoot = localProperties.getProperty('flutter.sdk')
+if (flutterRoot == null) {
+ throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
+}
+
+def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
+if (flutterVersionCode == null) {
+ flutterVersionCode = '1'
+}
+
+def flutterVersionName = localProperties.getProperty('flutter.versionName')
+if (flutterVersionName == null) {
+ flutterVersionName = '1.0'
+}
+
+apply plugin: 'com.android.application'
+apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
+
+android {
+ compileSdkVersion 28
+
+ lintOptions {
+ disable 'InvalidPackage'
+ }
+
+ defaultConfig {
+ // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
+ applicationId "com.example.flutter_drag_scale_example"
+ minSdkVersion 16
+ targetSdkVersion 28
+ versionCode flutterVersionCode.toInteger()
+ versionName flutterVersionName
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ // TODO: Add your own signing config for the release build.
+ // Signing with the debug keys for now, so `flutter run --release` works.
+ signingConfig signingConfigs.debug
+ }
+ }
+}
+
+flutter {
+ source '../..'
+}
+
+dependencies {
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'com.android.support.test:runner:1.0.2'
+ androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
+}
diff --git a/lib/my_flutter_drag_scale/example/android/app/src/debug/AndroidManifest.xml b/lib/my_flutter_drag_scale/example/android/app/src/debug/AndroidManifest.xml
new file mode 100644
index 0000000..cbeb081
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/android/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/lib/my_flutter_drag_scale/example/android/app/src/main/AndroidManifest.xml b/lib/my_flutter_drag_scale/example/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..d09dffa
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/my_flutter_drag_scale/example/android/app/src/main/java/com/example/flutter_drag_scale_example/MainActivity.java b/lib/my_flutter_drag_scale/example/android/app/src/main/java/com/example/flutter_drag_scale_example/MainActivity.java
new file mode 100644
index 0000000..b204b09
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/android/app/src/main/java/com/example/flutter_drag_scale_example/MainActivity.java
@@ -0,0 +1,13 @@
+package com.example.flutter_drag_scale_example;
+
+import android.os.Bundle;
+import io.flutter.app.FlutterActivity;
+import io.flutter.plugins.GeneratedPluginRegistrant;
+
+public class MainActivity extends FlutterActivity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ GeneratedPluginRegistrant.registerWith(this);
+ }
+}
diff --git a/lib/my_flutter_drag_scale/example/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java b/lib/my_flutter_drag_scale/example/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java
new file mode 100644
index 0000000..83e890e
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java
@@ -0,0 +1,25 @@
+package io.flutter.plugins;
+
+import io.flutter.plugin.common.PluginRegistry;
+import com.example.flutter_drag_scale.FlutterDragScalePlugin;
+
+/**
+ * Generated file. Do not edit.
+ */
+public final class GeneratedPluginRegistrant {
+ public static void registerWith(PluginRegistry registry) {
+ if (alreadyRegisteredWith(registry)) {
+ return;
+ }
+ FlutterDragScalePlugin.registerWith(registry.registrarFor("com.example.flutter_drag_scale.FlutterDragScalePlugin"));
+ }
+
+ private static boolean alreadyRegisteredWith(PluginRegistry registry) {
+ final String key = GeneratedPluginRegistrant.class.getCanonicalName();
+ if (registry.hasPlugin(key)) {
+ return true;
+ }
+ registry.registrarFor(key);
+ return false;
+ }
+}
diff --git a/lib/my_flutter_drag_scale/example/android/app/src/main/res/drawable/launch_background.xml b/lib/my_flutter_drag_scale/example/android/app/src/main/res/drawable/launch_background.xml
new file mode 100644
index 0000000..304732f
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/android/app/src/main/res/drawable/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/lib/my_flutter_drag_scale/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/lib/my_flutter_drag_scale/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..db77bb4
Binary files /dev/null and b/lib/my_flutter_drag_scale/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/lib/my_flutter_drag_scale/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/lib/my_flutter_drag_scale/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..17987b7
Binary files /dev/null and b/lib/my_flutter_drag_scale/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/lib/my_flutter_drag_scale/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/lib/my_flutter_drag_scale/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..09d4391
Binary files /dev/null and b/lib/my_flutter_drag_scale/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/lib/my_flutter_drag_scale/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/lib/my_flutter_drag_scale/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..d5f1c8d
Binary files /dev/null and b/lib/my_flutter_drag_scale/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/lib/my_flutter_drag_scale/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/lib/my_flutter_drag_scale/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4d6372e
Binary files /dev/null and b/lib/my_flutter_drag_scale/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/lib/my_flutter_drag_scale/example/android/app/src/main/res/values/styles.xml b/lib/my_flutter_drag_scale/example/android/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..00fa441
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/android/app/src/main/res/values/styles.xml
@@ -0,0 +1,8 @@
+
+
+
+
diff --git a/lib/my_flutter_drag_scale/example/android/app/src/profile/AndroidManifest.xml b/lib/my_flutter_drag_scale/example/android/app/src/profile/AndroidManifest.xml
new file mode 100644
index 0000000..cbeb081
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/android/app/src/profile/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/lib/my_flutter_drag_scale/example/android/build.gradle b/lib/my_flutter_drag_scale/example/android/build.gradle
new file mode 100644
index 0000000..bb8a303
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/android/build.gradle
@@ -0,0 +1,29 @@
+buildscript {
+ repositories {
+ google()
+ jcenter()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.2.1'
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+rootProject.buildDir = '../build'
+subprojects {
+ project.buildDir = "${rootProject.buildDir}/${project.name}"
+}
+subprojects {
+ project.evaluationDependsOn(':app')
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/lib/my_flutter_drag_scale/example/android/gradle.properties b/lib/my_flutter_drag_scale/example/android/gradle.properties
new file mode 100644
index 0000000..8bd86f6
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/android/gradle.properties
@@ -0,0 +1 @@
+org.gradle.jvmargs=-Xmx1536M
diff --git a/lib/my_flutter_drag_scale/example/android/gradle/wrapper/gradle-wrapper.properties b/lib/my_flutter_drag_scale/example/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..bae5290
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Jun 23 08:50:38 CEST 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
diff --git a/lib/my_flutter_drag_scale/example/android/local.properties b/lib/my_flutter_drag_scale/example/android/local.properties
new file mode 100644
index 0000000..5acce31
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/android/local.properties
@@ -0,0 +1,2 @@
+sdk.dir=S:\\Android\\Android-SDK-Windows
+flutter.sdk=R:\\Flutter\\FlutterSDK\\1.22.6
\ No newline at end of file
diff --git a/lib/my_flutter_drag_scale/example/android/settings.gradle b/lib/my_flutter_drag_scale/example/android/settings.gradle
new file mode 100644
index 0000000..5a2f14f
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/android/settings.gradle
@@ -0,0 +1,15 @@
+include ':app'
+
+def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
+
+def plugins = new Properties()
+def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
+if (pluginsFile.exists()) {
+ pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
+}
+
+plugins.each { name, path ->
+ def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
+ include ":$name"
+ project(":$name").projectDir = pluginDirectory
+}
diff --git a/lib/my_flutter_drag_scale/example/ios/Flutter/AppFrameworkInfo.plist b/lib/my_flutter_drag_scale/example/ios/Flutter/AppFrameworkInfo.plist
new file mode 100644
index 0000000..9367d48
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/ios/Flutter/AppFrameworkInfo.plist
@@ -0,0 +1,26 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ App
+ CFBundleIdentifier
+ io.flutter.flutter.app
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ App
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1.0
+ MinimumOSVersion
+ 8.0
+
+
diff --git a/lib/my_flutter_drag_scale/example/ios/Flutter/Debug.xcconfig b/lib/my_flutter_drag_scale/example/ios/Flutter/Debug.xcconfig
new file mode 100644
index 0000000..e8efba1
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/ios/Flutter/Debug.xcconfig
@@ -0,0 +1,2 @@
+#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
+#include "Generated.xcconfig"
diff --git a/lib/my_flutter_drag_scale/example/ios/Flutter/Generated.xcconfig b/lib/my_flutter_drag_scale/example/ios/Flutter/Generated.xcconfig
new file mode 100644
index 0000000..187943c
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/ios/Flutter/Generated.xcconfig
@@ -0,0 +1,7 @@
+// This is a generated file; do not edit or check into version control.
+FLUTTER_ROOT=/Users/majialun/Documents/flutter/flutter
+FLUTTER_APPLICATION_PATH=/Users/majialun/Desktop/github/flutter_drag_scale/example
+FLUTTER_TARGET=lib/main.dart
+FLUTTER_BUILD_DIR=build
+SYMROOT=${SOURCE_ROOT}/../build/ios
+FLUTTER_FRAMEWORK_DIR=/Users/majialun/Documents/flutter/flutter/bin/cache/artifacts/engine/ios
diff --git a/lib/my_flutter_drag_scale/example/ios/Flutter/Release.xcconfig b/lib/my_flutter_drag_scale/example/ios/Flutter/Release.xcconfig
new file mode 100644
index 0000000..399e934
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/ios/Flutter/Release.xcconfig
@@ -0,0 +1,2 @@
+#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
+#include "Generated.xcconfig"
diff --git a/lib/my_flutter_drag_scale/example/ios/Podfile b/lib/my_flutter_drag_scale/example/ios/Podfile
new file mode 100644
index 0000000..d077b08
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/ios/Podfile
@@ -0,0 +1,69 @@
+# Uncomment this line to define a global platform for your project
+# platform :ios, '9.0'
+
+# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
+ENV['COCOAPODS_DISABLE_STATS'] = 'true'
+
+project 'Runner', {
+ 'Debug' => :debug,
+ 'Profile' => :release,
+ 'Release' => :release,
+}
+
+def parse_KV_file(file, separator='=')
+ file_abs_path = File.expand_path(file)
+ if !File.exists? file_abs_path
+ return [];
+ end
+ pods_ary = []
+ skip_line_start_symbols = ["#", "/"]
+ File.foreach(file_abs_path) { |line|
+ next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ }
+ plugin = line.split(pattern=separator)
+ if plugin.length == 2
+ podname = plugin[0].strip()
+ path = plugin[1].strip()
+ podpath = File.expand_path("#{path}", file_abs_path)
+ pods_ary.push({:name => podname, :path => podpath});
+ else
+ puts "Invalid plugin specification: #{line}"
+ end
+ }
+ return pods_ary
+end
+
+target 'Runner' do
+ # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
+ # referring to absolute paths on developers' machines.
+ system('rm -rf .symlinks')
+ system('mkdir -p .symlinks/plugins')
+
+ # Flutter Pods
+ generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig')
+ if generated_xcode_build_settings.empty?
+ puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first."
+ end
+ generated_xcode_build_settings.map { |p|
+ if p[:name] == 'FLUTTER_FRAMEWORK_DIR'
+ symlink = File.join('.symlinks', 'flutter')
+ File.symlink(File.dirname(p[:path]), symlink)
+ pod 'Flutter', :path => File.join(symlink, File.basename(p[:path]))
+ end
+ }
+
+ # Plugin Pods
+ plugin_pods = parse_KV_file('../.flutter-plugins')
+ plugin_pods.map { |p|
+ symlink = File.join('.symlinks', 'plugins', p[:name])
+ File.symlink(p[:path], symlink)
+ pod p[:name], :path => File.join(symlink, 'ios')
+ }
+end
+
+post_install do |installer|
+ installer.pods_project.targets.each do |target|
+ target.build_configurations.each do |config|
+ config.build_settings['ENABLE_BITCODE'] = 'NO'
+ end
+ end
+end
diff --git a/lib/my_flutter_drag_scale/example/ios/Runner.xcodeproj/project.pbxproj b/lib/my_flutter_drag_scale/example/ios/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..4763772
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/ios/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,506 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
+ 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; };
+ 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; };
+ 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; };
+ 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };
+ 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };
+ 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 */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */,
+ 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */,
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
+ 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
+ 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
+ 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
+ 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; };
+ 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
+ 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
+ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 97C146EB1CF9000F007C117D /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */,
+ 3B80C3941E831B6300D905FE /* App.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 9740EEB11CF90186004384FC /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 3B80C3931E831B6300D905FE /* App.framework */,
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
+ 9740EEBA1CF902C7004384FC /* Flutter.framework */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */,
+ );
+ name = Flutter;
+ sourceTree = "";
+ };
+ 97C146E51CF9000F007C117D = {
+ isa = PBXGroup;
+ children = (
+ 9740EEB11CF90186004384FC /* Flutter */,
+ 97C146F01CF9000F007C117D /* Runner */,
+ 97C146EF1CF9000F007C117D /* Products */,
+ CF3B75C9A7D2FA2A4C99F110 /* Frameworks */,
+ );
+ sourceTree = "";
+ };
+ 97C146EF1CF9000F007C117D /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146EE1CF9000F007C117D /* Runner.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 97C146F01CF9000F007C117D /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */,
+ 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */,
+ 97C146FA1CF9000F007C117D /* Main.storyboard */,
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */,
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
+ 97C147021CF9000F007C117D /* Info.plist */,
+ 97C146F11CF9000F007C117D /* Supporting Files */,
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
+ );
+ path = Runner;
+ sourceTree = "";
+ };
+ 97C146F11CF9000F007C117D /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146F21CF9000F007C117D /* main.m */,
+ );
+ name = "Supporting Files";
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 97C146ED1CF9000F007C117D /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ 9740EEB61CF901F6004384FC /* Run Script */,
+ 97C146EA1CF9000F007C117D /* Sources */,
+ 97C146EB1CF9000F007C117D /* Frameworks */,
+ 97C146EC1CF9000F007C117D /* Resources */,
+ 9705A1C41CF9048500538489 /* Embed Frameworks */,
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = Runner;
+ productName = Runner;
+ productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 97C146E61CF9000F007C117D /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 0910;
+ ORGANIZATIONNAME = "The Chromium Authors";
+ TargetAttributes = {
+ 97C146ED1CF9000F007C117D = {
+ CreatedOnToolsVersion = 7.3.1;
+ };
+ };
+ };
+ buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 97C146E51CF9000F007C117D;
+ productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 97C146ED1CF9000F007C117D /* Runner */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 97C146EC1CF9000F007C117D /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
+ 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */,
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Thin Binary";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin";
+ };
+ 9740EEB61CF901F6004384FC /* Run Script */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Run Script";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 97C146EA1CF9000F007C117D /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */,
+ 97C146F31CF9000F007C117D /* main.m in Sources */,
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C146FB1CF9000F007C117D /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "";
+ };
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C147001CF9000F007C117D /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 249021D3217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Profile;
+ };
+ 249021D4217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ DEVELOPMENT_TEAM = S8QB4VV633;
+ ENABLE_BITCODE = NO;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterDragScaleExample;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Profile;
+ };
+ 97C147031CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 97C147041CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 97C147061CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterDragScaleExample;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Debug;
+ };
+ 97C147071CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Flutter",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterDragScaleExample;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 97C147031CF9000F007C117D /* Debug */,
+ 97C147041CF9000F007C117D /* Release */,
+ 249021D3217E4FDB00AE95B9 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 97C147061CF9000F007C117D /* Debug */,
+ 97C147071CF9000F007C117D /* Release */,
+ 249021D4217E4FDB00AE95B9 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 97C146E61CF9000F007C117D /* Project object */;
+}
diff --git a/lib/my_flutter_drag_scale/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/lib/my_flutter_drag_scale/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..1d526a1
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/lib/my_flutter_drag_scale/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/lib/my_flutter_drag_scale/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 0000000..786d6aa
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/my_flutter_drag_scale/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/lib/my_flutter_drag_scale/example/ios/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..1d526a1
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/ios/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/lib/my_flutter_drag_scale/example/ios/Runner/AppDelegate.h b/lib/my_flutter_drag_scale/example/ios/Runner/AppDelegate.h
new file mode 100644
index 0000000..36e21bb
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/ios/Runner/AppDelegate.h
@@ -0,0 +1,6 @@
+#import
+#import
+
+@interface AppDelegate : FlutterAppDelegate
+
+@end
diff --git a/lib/my_flutter_drag_scale/example/ios/Runner/AppDelegate.m b/lib/my_flutter_drag_scale/example/ios/Runner/AppDelegate.m
new file mode 100644
index 0000000..59a72e9
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/ios/Runner/AppDelegate.m
@@ -0,0 +1,13 @@
+#include "AppDelegate.h"
+#include "GeneratedPluginRegistrant.h"
+
+@implementation AppDelegate
+
+- (BOOL)application:(UIApplication *)application
+ didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+ [GeneratedPluginRegistrant registerWithRegistry:self];
+ // Override point for customization after application launch.
+ return [super application:application didFinishLaunchingWithOptions:launchOptions];
+}
+
+@end
diff --git a/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..d36b1fa
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,122 @@
+{
+ "images" : [
+ {
+ "size" : "20x20",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-20x20@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-20x20@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-40x40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-40x40@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-60x60@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-60x60@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-20x20@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-20x20@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-29x29@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-29x29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-40x40@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-40x40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-76x76@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-76x76@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "83.5x83.5",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-83.5x83.5@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "1024x1024",
+ "idiom" : "ios-marketing",
+ "filename" : "Icon-App-1024x1024@1x.png",
+ "scale" : "1x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
new file mode 100644
index 0000000..3d43d11
Binary files /dev/null and b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ
diff --git a/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
new file mode 100644
index 0000000..28c6bf0
Binary files /dev/null and b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ
diff --git a/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
new file mode 100644
index 0000000..2ccbfd9
Binary files /dev/null and b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ
diff --git a/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
new file mode 100644
index 0000000..f091b6b
Binary files /dev/null and b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ
diff --git a/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
new file mode 100644
index 0000000..4cde121
Binary files /dev/null and b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ
diff --git a/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
new file mode 100644
index 0000000..d0ef06e
Binary files /dev/null and b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ
diff --git a/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
new file mode 100644
index 0000000..dcdc230
Binary files /dev/null and b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ
diff --git a/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
new file mode 100644
index 0000000..2ccbfd9
Binary files /dev/null and b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ
diff --git a/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
new file mode 100644
index 0000000..c8f9ed8
Binary files /dev/null and b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ
diff --git a/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
new file mode 100644
index 0000000..a6d6b86
Binary files /dev/null and b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ
diff --git a/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
new file mode 100644
index 0000000..a6d6b86
Binary files /dev/null and b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ
diff --git a/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
new file mode 100644
index 0000000..75b2d16
Binary files /dev/null and b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ
diff --git a/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
new file mode 100644
index 0000000..c4df70d
Binary files /dev/null and b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ
diff --git a/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
new file mode 100644
index 0000000..6a84f41
Binary files /dev/null and b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ
diff --git a/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
new file mode 100644
index 0000000..d0e1f58
Binary files /dev/null and b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
new file mode 100644
index 0000000..0bedcf2
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
new file mode 100644
index 0000000..9da19ea
Binary files /dev/null and b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ
diff --git a/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
new file mode 100644
index 0000000..9da19ea
Binary files /dev/null and b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ
diff --git a/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
new file mode 100644
index 0000000..9da19ea
Binary files /dev/null and b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ
diff --git a/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
new file mode 100644
index 0000000..89c2725
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
@@ -0,0 +1,5 @@
+# Launch Screen Assets
+
+You can customize the launch screen with your own desired assets by replacing the image files in this directory.
+
+You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
\ No newline at end of file
diff --git a/lib/my_flutter_drag_scale/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/lib/my_flutter_drag_scale/example/ios/Runner/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000..f2e259c
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/my_flutter_drag_scale/example/ios/Runner/Base.lproj/Main.storyboard b/lib/my_flutter_drag_scale/example/ios/Runner/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..f3c2851
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/ios/Runner/Base.lproj/Main.storyboard
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/my_flutter_drag_scale/example/ios/Runner/GeneratedPluginRegistrant.h b/lib/my_flutter_drag_scale/example/ios/Runner/GeneratedPluginRegistrant.h
new file mode 100644
index 0000000..ed9a5c6
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/ios/Runner/GeneratedPluginRegistrant.h
@@ -0,0 +1,17 @@
+//
+// Generated file. Do not edit.
+//
+
+#ifndef GeneratedPluginRegistrant_h
+#define GeneratedPluginRegistrant_h
+
+#import
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface GeneratedPluginRegistrant : NSObject
++ (void)registerWithRegistry:(NSObject*)registry;
+@end
+
+NS_ASSUME_NONNULL_END
+#endif /* GeneratedPluginRegistrant_h */
diff --git a/lib/my_flutter_drag_scale/example/ios/Runner/GeneratedPluginRegistrant.m b/lib/my_flutter_drag_scale/example/ios/Runner/GeneratedPluginRegistrant.m
new file mode 100644
index 0000000..5f5062e
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/ios/Runner/GeneratedPluginRegistrant.m
@@ -0,0 +1,19 @@
+//
+// Generated file. Do not edit.
+//
+
+#import "GeneratedPluginRegistrant.h"
+
+#if __has_include()
+#import
+#else
+@import flutter_drag_scale;
+#endif
+
+@implementation GeneratedPluginRegistrant
+
++ (void)registerWithRegistry:(NSObject*)registry {
+ [FlutterDragScalePlugin registerWithRegistrar:[registry registrarForPlugin:@"FlutterDragScalePlugin"]];
+}
+
+@end
diff --git a/lib/my_flutter_drag_scale/example/ios/Runner/Info.plist b/lib/my_flutter_drag_scale/example/ios/Runner/Info.plist
new file mode 100644
index 0000000..1d4d669
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/ios/Runner/Info.plist
@@ -0,0 +1,45 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ flutter_drag_scale_example
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $(FLUTTER_BUILD_NAME)
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ $(FLUTTER_BUILD_NUMBER)
+ LSRequiresIPhoneOS
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIMainStoryboardFile
+ Main
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UIViewControllerBasedStatusBarAppearance
+
+
+
diff --git a/lib/my_flutter_drag_scale/example/ios/Runner/main.m b/lib/my_flutter_drag_scale/example/ios/Runner/main.m
new file mode 100644
index 0000000..dff6597
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/ios/Runner/main.m
@@ -0,0 +1,9 @@
+#import
+#import
+#import "AppDelegate.h"
+
+int main(int argc, char* argv[]) {
+ @autoreleasepool {
+ return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
+ }
+}
diff --git a/lib/my_flutter_drag_scale/example/lib/main.dart b/lib/my_flutter_drag_scale/example/lib/main.dart
new file mode 100644
index 0000000..3936c3e
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/lib/main.dart
@@ -0,0 +1,23 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_drag_scale/flutter_drag_scale.dart';
+
+void main() => runApp(MyApp());
+
+class MyApp extends StatelessWidget {
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ height: 400.0,
+ width: 400,
+ child: Center(
+ child: DragScaleContainer(
+ doubleTapStillScale: true,
+ child: new Image(
+ image: new NetworkImage(
+ 'http://h.hiphotos.baidu.com/zhidao/wh%3D450%2C600/sign=0d023672312ac65c67506e77cec29e27/9f2f070828381f30dea167bbad014c086e06f06c.jpg'),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/my_flutter_drag_scale/example/pubspec.yaml b/lib/my_flutter_drag_scale/example/pubspec.yaml
new file mode 100644
index 0000000..0221cdb
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/pubspec.yaml
@@ -0,0 +1,66 @@
+name: flutter_drag_scale_example
+description: Demonstrates how to use the flutter_drag_scale plugin.
+publish_to: 'none'
+
+environment:
+ sdk: ">=2.1.0 <3.0.0"
+
+dependencies:
+ flutter:
+ sdk: flutter
+
+ # The following adds the Cupertino Icons font to your application.
+ # Use with the CupertinoIcons class for iOS style icons.
+ cupertino_icons: ^0.1.2
+
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
+
+ flutter_drag_scale:
+ git: https://github.com/LiuC520/flutter_drag_scale.git
+
+ # flutter_drag_scale:
+ # path: ../
+
+# For information on the generic Dart part of this file, see the
+# following page: https://www.dartlang.org/tools/pub/pubspec
+
+# The following section is specific to Flutter.
+flutter:
+
+ # The following line ensures that the Material Icons font is
+ # included with your application, so that you can use the icons in
+ # the material Icons class.
+ uses-material-design: true
+
+ # To add assets to your application, add an assets section, like this:
+ # assets:
+ # - images/a_dot_burr.jpeg
+ # - images/a_dot_ham.jpeg
+
+ # An image asset can refer to one or more resolution-specific "variants", see
+ # https://flutter.io/assets-and-images/#resolution-aware.
+
+ # For details regarding adding assets from package dependencies, see
+ # https://flutter.io/assets-and-images/#from-packages
+
+ # To add custom fonts to your application, add a fonts section here,
+ # in this "flutter" section. Each entry in this list should have a
+ # "family" key with the font family name, and a "fonts" key with a
+ # list giving the asset and other descriptors for the font. For
+ # example:
+ # fonts:
+ # - family: Schyler
+ # fonts:
+ # - asset: fonts/Schyler-Regular.ttf
+ # - asset: fonts/Schyler-Italic.ttf
+ # style: italic
+ # - family: Trajan Pro
+ # fonts:
+ # - asset: fonts/TrajanPro.ttf
+ # - asset: fonts/TrajanPro_Bold.ttf
+ # weight: 700
+ #
+ # For details regarding fonts from package dependencies,
+ # see https://flutter.io/custom-fonts/#from-packages
diff --git a/lib/my_flutter_drag_scale/example/test/widget_test.dart b/lib/my_flutter_drag_scale/example/test/widget_test.dart
new file mode 100644
index 0000000..0fb031a
--- /dev/null
+++ b/lib/my_flutter_drag_scale/example/test/widget_test.dart
@@ -0,0 +1,27 @@
+// This is a basic Flutter widget test.
+//
+// To perform an interaction with a widget in your test, use the WidgetTester
+// utility that Flutter provides. For example, you can send tap and scroll
+// gestures. You can also use WidgetTester to find child widgets in the widget
+// tree, read text, and verify that the values of widget properties are correct.
+
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import 'package:flutter_drag_scale_example/main.dart';
+
+void main() {
+ testWidgets('Verify Platform version', (WidgetTester tester) async {
+ // Build our app and trigger a frame.
+ await tester.pumpWidget(MyApp());
+
+ // Verify that platform version is retrieved.
+ expect(
+ find.byWidgetPredicate(
+ (Widget widget) => widget is Text &&
+ widget.data.startsWith('Running on:'),
+ ),
+ findsOneWidget,
+ );
+ });
+}
diff --git a/lib/my_flutter_drag_scale/ios/Assets/.gitkeep b/lib/my_flutter_drag_scale/ios/Assets/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/lib/my_flutter_drag_scale/ios/Classes/FlutterDragScalePlugin.h b/lib/my_flutter_drag_scale/ios/Classes/FlutterDragScalePlugin.h
new file mode 100644
index 0000000..21e4a5a
--- /dev/null
+++ b/lib/my_flutter_drag_scale/ios/Classes/FlutterDragScalePlugin.h
@@ -0,0 +1,4 @@
+#import
+
+@interface FlutterDragScalePlugin : NSObject
+@end
diff --git a/lib/my_flutter_drag_scale/ios/Classes/FlutterDragScalePlugin.m b/lib/my_flutter_drag_scale/ios/Classes/FlutterDragScalePlugin.m
new file mode 100644
index 0000000..31fb8d2
--- /dev/null
+++ b/lib/my_flutter_drag_scale/ios/Classes/FlutterDragScalePlugin.m
@@ -0,0 +1,20 @@
+#import "FlutterDragScalePlugin.h"
+
+@implementation FlutterDragScalePlugin
++ (void)registerWithRegistrar:(NSObject*)registrar {
+ FlutterMethodChannel* channel = [FlutterMethodChannel
+ methodChannelWithName:@"flutter_drag_scale"
+ binaryMessenger:[registrar messenger]];
+ FlutterDragScalePlugin* instance = [[FlutterDragScalePlugin alloc] init];
+ [registrar addMethodCallDelegate:instance channel:channel];
+}
+
+- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
+ if ([@"getPlatformVersion" isEqualToString:call.method]) {
+ result([@"iOS " stringByAppendingString:[[UIDevice currentDevice] systemVersion]]);
+ } else {
+ result(FlutterMethodNotImplemented);
+ }
+}
+
+@end
diff --git a/lib/my_flutter_drag_scale/ios/flutter_drag_scale.podspec b/lib/my_flutter_drag_scale/ios/flutter_drag_scale.podspec
new file mode 100644
index 0000000..41478b0
--- /dev/null
+++ b/lib/my_flutter_drag_scale/ios/flutter_drag_scale.podspec
@@ -0,0 +1,21 @@
+#
+# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
+#
+Pod::Spec.new do |s|
+ s.name = 'flutter_drag_scale'
+ s.version = '0.0.1'
+ s.summary = 'A new flutter plugin project.'
+ s.description = <<-DESC
+A new flutter plugin project.
+ DESC
+ s.homepage = 'http://example.com'
+ s.license = { :file => '../LICENSE' }
+ s.author = { 'Your Company' => 'email@example.com' }
+ s.source = { :path => '.' }
+ s.source_files = 'Classes/**/*'
+ s.public_header_files = 'Classes/**/*.h'
+ s.dependency 'Flutter'
+
+ s.ios.deployment_target = '8.0'
+end
+
diff --git a/lib/my_flutter_drag_scale/lib/core/custom_gesture_detector.dart b/lib/my_flutter_drag_scale/lib/core/custom_gesture_detector.dart
new file mode 100644
index 0000000..b4e7cba
--- /dev/null
+++ b/lib/my_flutter_drag_scale/lib/core/custom_gesture_detector.dart
@@ -0,0 +1,925 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// import 'package:flutter/cupertino.dart';
+// import 'package:photo_view_test/common.dart';
+// import 'package:photo_view_test/EventBus.dart';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/gestures.dart'
+ show
+ GestureRecognizer,
+ GestureTapDownCallback,
+ GestureTapUpCallback,
+ GestureTapCallback,
+ GestureTapCancelCallback,
+ GestureLongPressUpCallback,
+ GestureLongPressCallback,
+ GestureDragStartCallback,
+ GestureDragUpdateCallback,
+ GestureDragEndCallback,
+ GestureDragCancelCallback,
+ GestureDragDownCallback,
+ GestureForcePressStartCallback,
+ GestureForcePressPeakCallback,
+ GestureForcePressUpdateCallback,
+ GestureForcePressEndCallback,
+ TapGestureRecognizer,
+ LongPressGestureRecognizer,
+ VerticalDragGestureRecognizer,
+ ForcePressGestureRecognizer,
+ HorizontalDragGestureRecognizer,
+ TapDownDetails,
+ PanGestureRecognizer,
+ TapUpDetails,
+ DragUpdateDetails,
+ DragDownDetails,
+ DragStartDetails,
+ DragEndDetails;
+import 'package:flutter/rendering.dart';
+
+import 'package:flutter/src/widgets/basic.dart' show Listener;
+import 'package:flutter/src/widgets/framework.dart'
+ show
+ StatelessWidget,
+ StatefulWidget,
+ State,
+ Widget,
+ Element,
+ BuildContext,
+ SingleChildRenderObjectWidget;
+import './scale.dart';
+import './multitap.dart';
+import './double_details.dart';
+export 'package:flutter/gestures.dart'
+ show
+ DragDownDetails,
+ DragStartDetails,
+ DragUpdateDetails,
+ DragEndDetails,
+ GestureTapDownCallback,
+ GestureTapUpCallback,
+ GestureTapCallback,
+ GestureTapCancelCallback,
+ GestureLongPressCallback,
+ GestureDragDownCallback,
+ GestureDragStartCallback,
+ GestureDragUpdateCallback,
+ GestureDragEndCallback,
+ GestureDragCancelCallback,
+ // GestureScaleStartCallback,
+ // GestureScaleUpdateCallback,
+ // GestureScaleEndCallback,
+ GestureForcePressStartCallback,
+ GestureForcePressPeakCallback,
+ GestureForcePressEndCallback,
+ GestureForcePressUpdateCallback,
+ // ScaleStartDetails,
+ // ScaleUpdateDetails,
+ // ScaleEndDetails,
+ TapDownDetails,
+ TapUpDetails,
+ Velocity;
+export './scale.dart'
+ show
+ GestureScaleStartCallback,
+ GestureScaleUpdateCallback,
+ GestureScaleEndCallback,
+ ScaleStartDetails,
+ ScaleUpdateDetails,
+ ScaleEndDetails;
+export './double_details.dart' show DoubleDetails;
+
+// Examples can assume:
+// bool _lights;
+// void setState(VoidCallback fn) { }
+// String _last;
+
+/// Factory for creating gesture recognizers.
+///
+/// `T` is the type of gesture recognizer this class manages.
+///
+/// Used by [RawGestureDetector.gestures].
+@optionalTypeArgs
+abstract class GestureRecognizerFactory {
+ /// Abstract const constructor. This constructor enables subclasses to provide
+ /// const constructors so that they can be used in const expressions.
+ const GestureRecognizerFactory();
+
+ /// Must return an instance of T.
+ T constructor();
+
+ /// Must configure the given instance (which will have been created by
+ /// `constructor`).
+ ///
+ /// This normally means setting the callbacks.
+ void initializer(T instance);
+
+ bool _debugAssertTypeMatches(Type type) {
+ assert(
+ type == T, 'GestureRecognizerFactory of type $T was used where type $type was specified.');
+ return true;
+ }
+}
+
+/// Signature for closures that implement [GestureRecognizerFactory.constructor].
+typedef GestureRecognizerFactoryConstructor = T Function();
+
+/// Signature for closures that implement [GestureRecognizerFactory.initializer].
+typedef GestureRecognizerFactoryInitializer = void Function(
+ T instance);
+
+/// Factory for creating gesture recognizers that delegates to callbacks.
+///
+/// Used by [RawGestureDetector.gestures].
+class GestureRecognizerFactoryWithHandlers
+ extends GestureRecognizerFactory {
+ /// Creates a gesture recognizer factory with the given callbacks.
+ ///
+ /// The arguments must not be null.
+ const GestureRecognizerFactoryWithHandlers(this._constructor, this._initializer)
+ : assert(_constructor != null),
+ assert(_initializer != null);
+
+ final GestureRecognizerFactoryConstructor _constructor;
+
+ final GestureRecognizerFactoryInitializer _initializer;
+
+ @override
+ T constructor() => _constructor();
+
+ @override
+ void initializer(T instance) => _initializer(instance);
+}
+
+/// A widget that detects gestures.
+///
+/// Attempts to recognize gestures that correspond to its non-null callbacks.
+///
+/// If this widget has a child, it defers to that child for its sizing behavior.
+/// If it does not have a child, it grows to fit the parent instead.
+///
+/// By default a GestureDetector with an invisible child ignores touches;
+/// this behavior can be controlled with [behavior].
+///
+/// GestureDetector also listens for accessibility events and maps
+/// them to the callbacks. To ignore accessibility events, set
+/// [excludeFromSemantics] to true.
+///
+/// See for additional information.
+///
+/// Material design applications typically react to touches with ink splash
+/// effects. The [InkWell] class implements this effect and can be used in place
+/// of a [GestureDetector] for handling taps.
+///
+/// {@tool sample}
+///
+/// This example makes a rectangle react to being tapped by setting the
+/// `_lights` field:
+///
+/// ```dart
+/// GestureDetector(
+/// onTap: () {
+/// setState(() { _lights = true; });
+/// },
+/// child: Container(
+/// color: Colors.yellow,
+/// child: Text('TURN LIGHTS ON'),
+/// ),
+/// )
+/// ```
+/// {@end-tool}
+///
+/// ## Debugging
+///
+/// To see how large the hit test box of a [GestureDetector] is for debugging
+/// purposes, set [debugPaintPointersEnabled] to true.
+class GestureDetector extends StatelessWidget {
+ /// Creates a widget that detects gestures.
+ ///
+ /// Pan and scale callbacks cannot be used simultaneously because scale is a
+ /// superset of pan. Simply use the scale callbacks instead.
+ ///
+ /// Horizontal and vertical drag callbacks cannot be used simultaneously
+ /// because a combination of a horizontal and vertical drag is a pan. Simply
+ /// use the pan callbacks instead.
+ ///
+ /// By default, gesture detectors contribute semantic information to the tree
+ /// that is used by assistive technology.
+ GestureDetector(
+ {Key key,
+ this.child,
+ this.onTapDown,
+ this.onTapUp,
+ this.onTap,
+ this.onTapCancel,
+ this.onDoubleTap,
+ this.onLongPress,
+ this.onLongPressUp,
+ this.onVerticalDragDown,
+ this.onVerticalDragStart,
+ this.onVerticalDragUpdate,
+ this.onVerticalDragEnd,
+ this.onVerticalDragCancel,
+ this.onHorizontalDragDown,
+ this.onHorizontalDragStart,
+ this.onHorizontalDragUpdate,
+ this.onHorizontalDragEnd,
+ this.onHorizontalDragCancel,
+ this.onForcePressStart,
+ this.onForcePressPeak,
+ this.onForcePressUpdate,
+ this.onForcePressEnd,
+ this.pointerDownCallback,
+ this.pointerUpCallback,
+ this.onPanDown,
+ this.onPanStart,
+ this.onPanUpdate,
+ this.onPanEnd,
+ this.onPanCancel,
+ this.onScaleStart,
+ this.onScaleUpdate,
+ this.onScaleEnd,
+ this.behavior,
+ this.excludeFromSemantics = false})
+ : assert(excludeFromSemantics != null),
+ assert(() {
+ final bool haveVerticalDrag = onVerticalDragStart != null ||
+ onVerticalDragUpdate != null ||
+ onVerticalDragEnd != null;
+ final bool haveHorizontalDrag = onHorizontalDragStart != null ||
+ onHorizontalDragUpdate != null ||
+ onHorizontalDragEnd != null;
+ final bool havePan = onPanStart != null || onPanUpdate != null || onPanEnd != null;
+ final bool haveScale =
+ onScaleStart != null || onScaleUpdate != null || onScaleEnd != null;
+ if (havePan || haveScale) {
+ if (havePan && haveScale) {
+ throw FlutterError('Incorrect GestureDetector arguments.\n'
+ 'Having both a pan gesture recognizer and a scale gesture recognizer is redundant; scale is a superset of pan. Just use the scale gesture recognizer.');
+ }
+ final String recognizer = havePan ? 'pan' : 'scale';
+ if (haveVerticalDrag && haveHorizontalDrag) {
+ throw FlutterError('Incorrect GestureDetector arguments.\n'
+ 'Simultaneously having a vertical drag gesture recognizer, a horizontal drag gesture recognizer, and a $recognizer gesture recognizer '
+ 'will result in the $recognizer gesture recognizer being ignored, since the other two will catch all drags.');
+ }
+ }
+ return true;
+ }()),
+ super(key: key);
+
+ /// The widget below this widget in the tree.
+ ///
+ /// {@macro flutter.widgets.child}
+ final Widget child;
+
+ /// A pointer that might cause a tap has contacted the screen at a particular
+ /// location.
+ ///
+ /// This is called after a short timeout, even if the winning gesture has not
+ /// yet been selected. If the tap gesture wins, [onTapUp] will be called,
+ /// otherwise [onTapCancel] will be called.
+ final GestureTapDownCallback onTapDown;
+
+ /// A pointer that will trigger a tap has stopped contacting the screen at a
+ /// particular location.
+ ///
+ /// This triggers immediately before [onTap] in the case of the tap gesture
+ /// winning. If the tap gesture did not win, [onTapCancel] is called instead.
+ final GestureTapUpCallback onTapUp;
+
+ /// A tap has occurred.
+ ///
+ /// This triggers when the tap gesture wins. If the tap gesture did not win,
+ /// [onTapCancel] is called instead.
+ ///
+ /// See also:
+ ///
+ /// * [onTapUp], which is called at the same time but includes details
+ /// regarding the pointer position.
+ final GestureTapCallback onTap;
+
+ /// The pointer that previously triggered [onTapDown] will not end up causing
+ /// a tap.
+ ///
+ /// This is called after [onTapDown], and instead of [onTapUp] and [onTap], if
+ /// the tap gesture did not win.
+ final GestureTapCancelCallback onTapCancel;
+
+ /// The user has tapped the screen at the same location twice in quick
+ /// succession.
+ final GestureDoubleTapCallback onDoubleTap;
+
+ /// A pointer has remained in contact with the screen at the same location for
+ /// a long period of time.
+ final GestureLongPressCallback onLongPress;
+
+ /// A pointer that has triggered a long-press has stopped contacting the screen.
+ final GestureLongPressUpCallback onLongPressUp;
+
+ /// A pointer has contacted the screen and might begin to move vertically.
+ final GestureDragDownCallback onVerticalDragDown;
+
+ /// A pointer has contacted the screen and has begun to move vertically.
+ final GestureDragStartCallback onVerticalDragStart;
+
+ /// A pointer that is in contact with the screen and moving vertically has
+ /// moved in the vertical direction.
+ final GestureDragUpdateCallback onVerticalDragUpdate;
+
+ /// A pointer that was previously in contact with the screen and moving
+ /// vertically is no longer in contact with the screen and was moving at a
+ /// specific velocity when it stopped contacting the screen.
+ final GestureDragEndCallback onVerticalDragEnd;
+
+ /// The pointer that previously triggered [onVerticalDragDown] did not
+ /// complete.
+ final GestureDragCancelCallback onVerticalDragCancel;
+
+ /// A pointer has contacted the screen and might begin to move horizontally.
+ final GestureDragDownCallback onHorizontalDragDown;
+
+ /// A pointer has contacted the screen and has begun to move horizontally.
+ final GestureDragStartCallback onHorizontalDragStart;
+
+ /// A pointer that is in contact with the screen and moving horizontally has
+ /// moved in the horizontal direction.
+ final GestureDragUpdateCallback onHorizontalDragUpdate;
+
+ /// A pointer that was previously in contact with the screen and moving
+ /// horizontally is no longer in contact with the screen and was moving at a
+ /// specific velocity when it stopped contacting the screen.
+ final GestureDragEndCallback onHorizontalDragEnd;
+
+ /// The pointer that previously triggered [onHorizontalDragDown] did not
+ /// complete.
+ final GestureDragCancelCallback onHorizontalDragCancel;
+
+ Function() pointerDownCallback; // 图片指针按下回调函数
+ Function() pointerUpCallback; // 图片指针弹起回调函数
+
+ /// A pointer has contacted the screen and might begin to move.
+ final GestureDragDownCallback onPanDown;
+
+ /// A pointer has contacted the screen and has begun to move.
+ final GestureDragStartCallback onPanStart;
+
+ /// A pointer that is in contact with the screen and moving has moved again.
+ final GestureDragUpdateCallback onPanUpdate;
+
+ /// A pointer that was previously in contact with the screen and moving
+ /// is no longer in contact with the screen and was moving at a specific
+ /// velocity when it stopped contacting the screen.
+ final GestureDragEndCallback onPanEnd;
+
+ /// The pointer that previously triggered [onPanDown] did not complete.
+ final GestureDragCancelCallback onPanCancel;
+
+ /// The pointers in contact with the screen have established a focal point and
+ /// initial scale of 1.0.
+ final GestureScaleStartCallback onScaleStart;
+
+ /// The pointers in contact with the screen have indicated a new focal point
+ /// and/or scale.
+ final GestureScaleUpdateCallback onScaleUpdate;
+
+ /// The pointers are no longer in contact with the screen.
+ final GestureScaleEndCallback onScaleEnd;
+
+ /// The pointer is in contact with the screen and has pressed with sufficient
+ /// force to initiate a force press. The amount of force is at least
+ /// [ForcePressGestureRecognizer.startPressure].
+ ///
+ /// Note that this callback will only be fired on devices with pressure
+ /// detecting screens.
+ final GestureForcePressStartCallback onForcePressStart;
+
+ /// The pointer is in contact with the screen and has pressed with the maximum
+ /// force. The amount of force is at least
+ /// [ForcePressGestureRecognizer.peakPressure].
+ ///
+ /// Note that this callback will only be fired on devices with pressure
+ /// detecting screens.
+ final GestureForcePressPeakCallback onForcePressPeak;
+
+ /// A pointer is in contact with the screen, has previously passed the
+ /// [ForcePressGestureRecognizer.startPressure] and is either moving on the
+ /// plane of the screen, pressing the screen with varying forces or both
+ /// simultaneously.
+ ///
+ /// Note that this callback will only be fired on devices with pressure
+ /// detecting screens.
+ final GestureForcePressUpdateCallback onForcePressUpdate;
+
+ /// The pointer is no longer in contact with the screen.
+ ///
+ /// Note that this callback will only be fired on devices with pressure
+ /// detecting screens.
+ final GestureForcePressEndCallback onForcePressEnd;
+
+ /// How this gesture detector should behave during hit testing.
+ ///
+ /// This defaults to [HitTestBehavior.deferToChild] if [child] is not null and
+ /// [HitTestBehavior.translucent] if child is null.
+ final HitTestBehavior behavior;
+
+ /// Whether to exclude these gestures from the semantics tree. For
+ /// example, the long-press gesture for showing a tooltip is
+ /// excluded because the tooltip itself is included in the semantics
+ /// tree directly and so having a gesture to show it would result in
+ /// duplication of information.
+ final bool excludeFromSemantics;
+
+ @override
+ Widget build(BuildContext context) {
+ final Map gestures = {};
+
+ if (onTapDown != null || onTapUp != null || onTap != null || onTapCancel != null) {
+ gestures[TapGestureRecognizer] = GestureRecognizerFactoryWithHandlers(
+ () => TapGestureRecognizer(debugOwner: this),
+ (TapGestureRecognizer instance) {
+ instance
+ ..onTapDown = onTapDown
+ ..onTapUp = onTapUp
+ ..onTap = onTap
+ ..onTapCancel = onTapCancel;
+ },
+ );
+ }
+
+ if (onDoubleTap != null) {
+ gestures[DoubleTapGestureRecognizer] =
+ GestureRecognizerFactoryWithHandlers(
+ () => DoubleTapGestureRecognizer(debugOwner: this),
+ (DoubleTapGestureRecognizer instance) {
+ instance..onDoubleTap = onDoubleTap;
+ },
+ );
+ }
+
+ if (onLongPress != null || onLongPressUp != null) {
+ gestures[LongPressGestureRecognizer] =
+ GestureRecognizerFactoryWithHandlers(
+ () => LongPressGestureRecognizer(debugOwner: this),
+ (LongPressGestureRecognizer instance) {
+ instance
+ ..onLongPress = onLongPress
+ ..onLongPressUp = onLongPressUp;
+ },
+ );
+ }
+ if (onVerticalDragDown != null ||
+ onVerticalDragStart != null ||
+ onVerticalDragUpdate != null ||
+ onVerticalDragEnd != null ||
+ onVerticalDragCancel != null) {
+ gestures[VerticalDragGestureRecognizer] =
+ GestureRecognizerFactoryWithHandlers(
+ () => VerticalDragGestureRecognizer(debugOwner: this),
+ (VerticalDragGestureRecognizer instance) {
+ instance
+ ..onDown = onVerticalDragDown
+ ..onStart = onVerticalDragStart
+ ..onUpdate = onVerticalDragUpdate
+ ..onEnd = onVerticalDragEnd
+ ..onCancel = onVerticalDragCancel;
+ },
+ );
+ }
+
+ if (onHorizontalDragDown != null ||
+ onHorizontalDragStart != null ||
+ onHorizontalDragUpdate != null ||
+ onHorizontalDragEnd != null ||
+ onHorizontalDragCancel != null) {
+ gestures[HorizontalDragGestureRecognizer] =
+ GestureRecognizerFactoryWithHandlers(
+ () => HorizontalDragGestureRecognizer(debugOwner: this),
+ (HorizontalDragGestureRecognizer instance) {
+ instance
+ ..onDown = onHorizontalDragDown
+ ..onStart = onHorizontalDragStart
+ ..onUpdate = onHorizontalDragUpdate
+ ..onEnd = onHorizontalDragEnd
+ ..onCancel = onHorizontalDragCancel;
+ },
+ );
+ }
+
+ if (onPanDown != null ||
+ onPanStart != null ||
+ onPanUpdate != null ||
+ onPanEnd != null ||
+ onPanCancel != null) {
+ gestures[PanGestureRecognizer] = GestureRecognizerFactoryWithHandlers(
+ () => PanGestureRecognizer(debugOwner: this),
+ (PanGestureRecognizer instance) {
+ instance
+ ..onDown = onPanDown
+ ..onStart = onPanStart
+ ..onUpdate = onPanUpdate
+ ..onEnd = onPanEnd
+ ..onCancel = onPanCancel;
+ },
+ );
+ }
+
+ if (onScaleStart != null || onScaleUpdate != null || onScaleEnd != null) {
+ gestures[ScaleGestureRecognizer] =
+ GestureRecognizerFactoryWithHandlers(
+ () => ScaleGestureRecognizer(debugOwner: this),
+ (ScaleGestureRecognizer instance) {
+ instance
+ ..onStart = onScaleStart
+ ..onUpdate = onScaleUpdate
+ ..onEnd = onScaleEnd;
+ },
+ );
+ }
+
+ if (onForcePressStart != null ||
+ onForcePressPeak != null ||
+ onForcePressUpdate != null ||
+ onForcePressEnd != null) {
+ gestures[ForcePressGestureRecognizer] =
+ GestureRecognizerFactoryWithHandlers(
+ () => ForcePressGestureRecognizer(debugOwner: this),
+ (ForcePressGestureRecognizer instance) {
+ instance
+ ..onStart = onForcePressStart
+ ..onPeak = onForcePressPeak
+ ..onUpdate = onForcePressUpdate
+ ..onEnd = onForcePressEnd;
+ },
+ );
+ }
+
+ return RawGestureDetector(
+ gestures: gestures,
+ pointerDownCallback: pointerDownCallback,
+ pointerUpCallback: pointerUpCallback,
+ behavior: behavior,
+ excludeFromSemantics: excludeFromSemantics,
+ child: child,
+ );
+ }
+}
+
+/// A widget that detects gestures described by the given gesture
+/// factories.
+///
+/// For common gestures, use a [GestureRecognizer].
+/// [RawGestureDetector] is useful primarily when developing your
+/// own gesture recognizers.
+///
+/// Configuring the gesture recognizers requires a carefully constructed map, as
+/// described in [gestures] and as shown in the example below.
+///
+/// {@tool sample}
+///
+/// This example shows how to hook up a [TapGestureRecognizer]. It assumes that
+/// the code is being used inside a [State] object with a `_last` field that is
+/// then displayed as the child of the gesture detector.
+///
+/// ```dart
+/// RawGestureDetector(
+/// gestures: {
+/// TapGestureRecognizer: GestureRecognizerFactoryWithHandlers(
+/// () => TapGestureRecognizer(),
+/// (TapGestureRecognizer instance) {
+/// instance
+/// ..onTapDown = (TapDownDetails details) { setState(() { _last = 'down'; }); }
+/// ..onTapUp = (TapUpDetails details) { setState(() { _last = 'up'; }); }
+/// ..onTap = () { setState(() { _last = 'tap'; }); }
+/// ..onTapCancel = () { setState(() { _last = 'cancel'; }); };
+/// },
+/// ),
+/// },
+/// child: Container(width: 300.0, height: 300.0, color: Colors.yellow, child: Text(_last)),
+/// )
+/// ```
+/// {@end-tool}
+///
+/// See also:
+///
+/// * [GestureDetector], a less flexible but much simpler widget that does the same thing.
+/// * [Listener], a widget that reports raw pointer events.
+/// * [GestureRecognizer], the class that you extend to create a custom gesture recognizer.
+class RawGestureDetector extends StatefulWidget {
+ /// Creates a widget that detects gestures.
+ ///
+ /// By default, gesture detectors contribute semantic information to the tree
+ /// that is used by assistive technology. This can be controlled using
+ /// [excludeFromSemantics].
+ RawGestureDetector(
+ {Key key,
+ this.child,
+ this.gestures = const {},
+ this.pointerDownCallback,
+ this.pointerUpCallback,
+ this.behavior,
+ this.excludeFromSemantics = false})
+ : assert(gestures != null),
+ assert(excludeFromSemantics != null),
+ super(key: key);
+
+ Function() pointerDownCallback; // 图片指针按下回调函数
+ Function() pointerUpCallback; // 图片指针弹起回调函数
+
+ /// The widget below this widget in the tree.
+ ///
+ /// {@macro flutter.widgets.child}
+ final Widget child;
+
+ /// The gestures that this widget will attempt to recognize.
+ ///
+ /// This should be a map from [GestureRecognizer] subclasses to
+ /// [GestureRecognizerFactory] subclasses specialized with the same type.
+ ///
+ /// This value can be late-bound at layout time using
+ /// [RawGestureDetectorState.replaceGestureRecognizers].
+ final Map gestures;
+
+ /// How this gesture detector should behave during hit testing.
+ ///
+ /// This defaults to [HitTestBehavior.deferToChild] if [child] is not null and
+ /// [HitTestBehavior.translucent] if child is null.
+ final HitTestBehavior behavior;
+
+ /// Whether to exclude these gestures from the semantics tree. For
+ /// example, the long-press gesture for showing a tooltip is
+ /// excluded because the tooltip itself is included in the semantics
+ /// tree directly and so having a gesture to show it would result in
+ /// duplication of information.
+ final bool excludeFromSemantics;
+
+ @override
+ RawGestureDetectorState createState() => RawGestureDetectorState();
+}
+
+/// State for a [RawGestureDetector].
+class RawGestureDetectorState extends State {
+ Map _recognizers = const {};
+
+ @override
+ void initState() {
+ super.initState();
+ _syncAll(widget.gestures);
+ }
+
+ @override
+ void didUpdateWidget(RawGestureDetector oldWidget) {
+ super.didUpdateWidget(oldWidget);
+ _syncAll(widget.gestures);
+ }
+
+ /// This method can be called after the build phase, during the
+ /// layout of the nearest descendant [RenderObjectWidget] of the
+ /// gesture detector, to update the list of active gesture
+ /// recognizers.
+ ///
+ /// The typical use case is [Scrollable]s, which put their viewport
+ /// in their gesture detector, and then need to know the dimensions
+ /// of the viewport and the viewport's child to determine whether
+ /// the gesture detector should be enabled.
+ ///
+ /// The argument should follow the same conventions as
+ /// [RawGestureDetector.gestures]. It acts like a temporary replacement for
+ /// that value until the next build.
+ void replaceGestureRecognizers(Map gestures) {
+ assert(() {
+ if (!context.findRenderObject().owner.debugDoingLayout) {
+ throw FlutterError(
+ 'Unexpected call to replaceGestureRecognizers() method of RawGestureDetectorState.\n'
+ 'The replaceGestureRecognizers() method can only be called during the layout phase. '
+ 'To set the gesture recognizers at other times, trigger a new build using setState() '
+ 'and provide the new gesture recognizers as constructor arguments to the corresponding '
+ 'RawGestureDetector or GestureDetector object.');
+ }
+ return true;
+ }());
+ _syncAll(gestures);
+ if (!widget.excludeFromSemantics) {
+ final RenderSemanticsGestureHandler semanticsGestureHandler = context.findRenderObject();
+ context.visitChildElements((Element element) {
+ final _GestureSemantics widget = element.widget;
+ widget._updateHandlers(semanticsGestureHandler);
+ });
+ }
+ }
+
+ /// This method can be called outside of the build phase to filter the list of
+ /// available semantic actions.
+ ///
+ /// The actual filtering is happening in the next frame and a frame will be
+ /// scheduled if non is pending.
+ ///
+ /// This is used by [Scrollable] to configure system accessibility tools so
+ /// that they know in which direction a particular list can be scrolled.
+ ///
+ /// If this is never called, then the actions are not filtered. If the list of
+ /// actions to filter changes, it must be called again.
+ void replaceSemanticsActions(Set actions) {
+ assert(() {
+ final Element element = context;
+ if (element.owner.debugBuilding) {
+ throw FlutterError(
+ 'Unexpected call to replaceSemanticsActions() method of RawGestureDetectorState.\n'
+ 'The replaceSemanticsActions() method can only be called outside of the build phase.');
+ }
+ return true;
+ }());
+ if (!widget.excludeFromSemantics) {
+ final RenderSemanticsGestureHandler semanticsGestureHandler = context.findRenderObject();
+ semanticsGestureHandler.validActions =
+ actions; // will call _markNeedsSemanticsUpdate(), if required.
+ }
+ }
+
+ @override
+ void dispose() {
+ for (GestureRecognizer recognizer in _recognizers.values) recognizer.dispose();
+ _recognizers = null;
+ super.dispose();
+ }
+
+ void _syncAll(Map gestures) {
+ assert(_recognizers != null);
+ final Map oldRecognizers = _recognizers;
+ _recognizers = {};
+ for (Type type in gestures.keys) {
+ assert(gestures[type] != null);
+ assert(gestures[type]._debugAssertTypeMatches(type));
+ assert(!_recognizers.containsKey(type));
+ _recognizers[type] = oldRecognizers[type] ?? gestures[type].constructor();
+ assert(_recognizers[type].runtimeType == type,
+ 'GestureRecognizerFactory of type $type created a GestureRecognizer of type ${_recognizers[type].runtimeType}. The GestureRecognizerFactory must be specialized with the type of the class that it returns from its constructor method.');
+ gestures[type].initializer(_recognizers[type]);
+ }
+ for (Type type in oldRecognizers.keys) {
+ if (!_recognizers.containsKey(type)) oldRecognizers[type].dispose();
+ }
+ }
+
+ void _handlePointerUp(PointerUpEvent event) {
+ if (widget.pointerUpCallback != null) {
+ widget.pointerUpCallback();
+ }
+ }
+
+ void _handlePointerDown(PointerDownEvent event) {
+ if (widget.pointerDownCallback != null) {
+ widget.pointerDownCallback();
+ }
+
+ assert(_recognizers != null);
+ for (GestureRecognizer recognizer in _recognizers.values) recognizer.addPointer(event);
+ }
+
+ HitTestBehavior get _defaultBehavior {
+ return widget.child == null ? HitTestBehavior.translucent : HitTestBehavior.deferToChild;
+ }
+
+ void _handleSemanticsTap() {
+ final TapGestureRecognizer recognizer = _recognizers[TapGestureRecognizer];
+ assert(recognizer != null);
+ if (recognizer.onTapDown != null) recognizer.onTapDown(TapDownDetails());
+ if (recognizer.onTapUp != null) recognizer.onTapUp(TapUpDetails());
+ if (recognizer.onTap != null) recognizer.onTap();
+ }
+
+ void _handleSemanticsLongPress() {
+ final LongPressGestureRecognizer recognizer = _recognizers[LongPressGestureRecognizer];
+ assert(recognizer != null);
+ if (recognizer.onLongPress != null) recognizer.onLongPress();
+ }
+
+ void _handleSemanticsHorizontalDragUpdate(DragUpdateDetails updateDetails) {
+ {
+ final HorizontalDragGestureRecognizer recognizer =
+ _recognizers[HorizontalDragGestureRecognizer];
+ if (recognizer != null) {
+ if (recognizer.onDown != null) recognizer.onDown(DragDownDetails());
+ if (recognizer.onStart != null) recognizer.onStart(DragStartDetails());
+ if (recognizer.onUpdate != null) recognizer.onUpdate(updateDetails);
+ if (recognizer.onEnd != null) recognizer.onEnd(DragEndDetails(primaryVelocity: 0.0));
+ return;
+ }
+ }
+ {
+ final PanGestureRecognizer recognizer = _recognizers[PanGestureRecognizer];
+ if (recognizer != null) {
+ if (recognizer.onDown != null) recognizer.onDown(DragDownDetails());
+ if (recognizer.onStart != null) recognizer.onStart(DragStartDetails());
+ if (recognizer.onUpdate != null) recognizer.onUpdate(updateDetails);
+ if (recognizer.onEnd != null) recognizer.onEnd(DragEndDetails());
+ return;
+ }
+ }
+ }
+
+ void _handleSemanticsVerticalDragUpdate(DragUpdateDetails updateDetails) {
+ {
+ final VerticalDragGestureRecognizer recognizer = _recognizers[VerticalDragGestureRecognizer];
+ if (recognizer != null) {
+ if (recognizer.onDown != null) recognizer.onDown(DragDownDetails());
+ if (recognizer.onStart != null) recognizer.onStart(DragStartDetails());
+ if (recognizer.onUpdate != null) recognizer.onUpdate(updateDetails);
+ if (recognizer.onEnd != null) recognizer.onEnd(DragEndDetails(primaryVelocity: 0.0));
+ return;
+ }
+ }
+ {
+ final PanGestureRecognizer recognizer = _recognizers[PanGestureRecognizer];
+ if (recognizer != null) {
+ if (recognizer.onDown != null) recognizer.onDown(DragDownDetails());
+ if (recognizer.onStart != null) recognizer.onStart(DragStartDetails());
+ if (recognizer.onUpdate != null) recognizer.onUpdate(updateDetails);
+ if (recognizer.onEnd != null) recognizer.onEnd(DragEndDetails());
+ return;
+ }
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ Widget result = Listener(
+ onPointerDown: _handlePointerDown,
+ onPointerUp: _handlePointerUp,
+ behavior: widget.behavior ?? _defaultBehavior,
+ child: widget.child);
+ if (!widget.excludeFromSemantics) result = _GestureSemantics(owner: this, child: result);
+ return result;
+ }
+
+ @override
+ void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+ super.debugFillProperties(properties);
+ if (_recognizers == null) {
+ properties.add(DiagnosticsNode.message('DISPOSED'));
+ } else {
+ final List gestures = _recognizers.values
+ .map((GestureRecognizer recognizer) => recognizer.debugDescription)
+ .toList();
+ properties.add(IterableProperty('gestures', gestures, ifEmpty: ''));
+ properties.add(IterableProperty('recognizers', _recognizers.values,
+ level: DiagnosticLevel.fine));
+ }
+ properties.add(EnumProperty('behavior', widget.behavior, defaultValue: null));
+ }
+}
+
+class _GestureSemantics extends SingleChildRenderObjectWidget {
+ const _GestureSemantics({Key key, Widget child, this.owner}) : super(key: key, child: child);
+
+ final RawGestureDetectorState owner;
+
+ @override
+ RenderSemanticsGestureHandler createRenderObject(BuildContext context) {
+ return RenderSemanticsGestureHandler(
+ onTap: _onTapHandler,
+ onLongPress: _onLongPressHandler,
+ onHorizontalDragUpdate: _onHorizontalDragUpdateHandler,
+ onVerticalDragUpdate: _onVerticalDragUpdateHandler,
+ );
+ }
+
+ void _updateHandlers(RenderSemanticsGestureHandler renderObject) {
+ renderObject
+ ..onTap = _onTapHandler
+ ..onLongPress = _onLongPressHandler
+ ..onHorizontalDragUpdate = _onHorizontalDragUpdateHandler
+ ..onVerticalDragUpdate = _onVerticalDragUpdateHandler;
+ }
+
+ @override
+ void updateRenderObject(BuildContext context, RenderSemanticsGestureHandler renderObject) {
+ _updateHandlers(renderObject);
+ }
+
+ GestureTapCallback get _onTapHandler {
+ return owner._recognizers.containsKey(TapGestureRecognizer) ? owner._handleSemanticsTap : null;
+ }
+
+ GestureTapCallback get _onLongPressHandler {
+ return owner._recognizers.containsKey(LongPressGestureRecognizer)
+ ? owner._handleSemanticsLongPress
+ : null;
+ }
+
+ GestureDragUpdateCallback get _onHorizontalDragUpdateHandler {
+ return owner._recognizers.containsKey(HorizontalDragGestureRecognizer) ||
+ owner._recognizers.containsKey(PanGestureRecognizer)
+ ? owner._handleSemanticsHorizontalDragUpdate
+ : null;
+ }
+
+ GestureDragUpdateCallback get _onVerticalDragUpdateHandler {
+ return owner._recognizers.containsKey(VerticalDragGestureRecognizer) ||
+ owner._recognizers.containsKey(PanGestureRecognizer)
+ ? owner._handleSemanticsVerticalDragUpdate
+ : null;
+ }
+}
diff --git a/lib/my_flutter_drag_scale/lib/core/double_details.dart b/lib/my_flutter_drag_scale/lib/core/double_details.dart
new file mode 100644
index 0000000..9a2a725
--- /dev/null
+++ b/lib/my_flutter_drag_scale/lib/core/double_details.dart
@@ -0,0 +1,14 @@
+import 'package:flutter/src/gestures/events.dart' show PointerEvent;
+
+/// Signature for callback when the user has tapped the screen at the same
+/// location twice in quick succession.
+typedef GestureDoubleTapCallback = void Function(DoubleDetails details);
+
+/// double tap callback details
+/// 双击的回调信息
+class DoubleDetails {
+ DoubleDetails({this.pointerEvent});
+ final PointerEvent pointerEvent;
+ @override
+ String toString() => 'DoubleDetails(pointerEvent: $pointerEvent)';
+}
diff --git a/lib/my_flutter_drag_scale/lib/core/drag_scale_widget.dart b/lib/my_flutter_drag_scale/lib/core/drag_scale_widget.dart
new file mode 100644
index 0000000..bc611b0
--- /dev/null
+++ b/lib/my_flutter_drag_scale/lib/core/drag_scale_widget.dart
@@ -0,0 +1,43 @@
+import 'package:flutter/material.dart';
+import './touchable_container.dart';
+
+@immutable
+class DragScaleContainer extends StatefulWidget {
+ Widget child;
+
+ /// 双击内容是否一致放大,默认是true,也就是一致放大
+ /// 如果为false,第一次双击放大两倍,再次双击恢复原本大小
+ bool doubleTapStillScale;
+ ValueChanged scaleChangedCallback; // 图片缩放倍数改变回调函数
+ Function() pointerDownCallback; // 图片指针按下回调函数
+ Function() pointerUpCallback; // 图片指针弹起回调函数
+
+ DragScaleContainer({
+ Key key,
+ @required this.child,
+ this.scaleChangedCallback,
+ this.pointerDownCallback,
+ this.pointerUpCallback,
+ this.doubleTapStillScale = true,
+ });
+
+ @override
+ State createState() {
+ return _DragScaleContainerState();
+ }
+}
+
+class _DragScaleContainerState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return ClipRect(
+ child: TouchableContainer(
+ child: widget.child,
+ doubleTapStillScale: widget.doubleTapStillScale,
+ scaleChangedCallback: widget.scaleChangedCallback,
+ pointerDownCallback: widget.pointerDownCallback,
+ pointerUpCallback: widget.pointerUpCallback,
+ ),
+ );
+ }
+}
diff --git a/lib/my_flutter_drag_scale/lib/core/multitap.dart b/lib/my_flutter_drag_scale/lib/core/multitap.dart
new file mode 100644
index 0000000..dc5860a
--- /dev/null
+++ b/lib/my_flutter_drag_scale/lib/core/multitap.dart
@@ -0,0 +1,403 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:ui' show Offset;
+
+import 'package:flutter/src/gestures/binding.dart';
+import 'package:flutter/src/gestures/pointer_router.dart';
+import 'package:flutter/src/gestures/tap.dart';
+
+import 'package:flutter/src/gestures/arena.dart';
+import 'package:flutter/src/gestures/constants.dart';
+import 'package:flutter/src/gestures/events.dart';
+import 'package:flutter/src/gestures/recognizer.dart';
+import 'package:flutter/src/gestures/velocity_tracker.dart';
+import './double_details.dart';
+
+/// Signature used by [MultiTapGestureRecognizer] for when a pointer that might
+/// cause a tap has contacted the screen at a particular location.
+typedef GestureMultiTapDownCallback = void Function(
+ int pointer, TapDownDetails details);
+
+/// Signature used by [MultiTapGestureRecognizer] for when a pointer that will
+/// trigger a tap has stopped contacting the screen at a particular location.
+typedef GestureMultiTapUpCallback = void Function(
+ int pointer, TapUpDetails details);
+
+/// Signature used by [MultiTapGestureRecognizer] for when a tap has occurred.
+typedef GestureMultiTapCallback = void Function(int pointer);
+
+/// Signature for when the pointer that previously triggered a
+/// [GestureMultiTapDownCallback] will not end up causing a tap.
+typedef GestureMultiTapCancelCallback = void Function(int pointer);
+
+/// TapTracker helps track individual tap sequences as part of a
+/// larger gesture.
+class _TapTracker {
+ _TapTracker({PointerDownEvent event, this.entry})
+ : pointer = event.pointer,
+ _initialPosition = event.position;
+
+ final int pointer;
+ final GestureArenaEntry entry;
+ final Offset _initialPosition;
+
+ bool _isTrackingPointer = false;
+
+ void startTrackingPointer(PointerRoute route) {
+ if (!_isTrackingPointer) {
+ _isTrackingPointer = true;
+ GestureBinding.instance.pointerRouter.addRoute(pointer, route);
+ }
+ }
+
+ void stopTrackingPointer(PointerRoute route) {
+ if (_isTrackingPointer) {
+ _isTrackingPointer = false;
+ GestureBinding.instance.pointerRouter.removeRoute(pointer, route);
+ }
+ }
+
+ bool isWithinTolerance(PointerEvent event, double tolerance) {
+ final Offset offset = event.position - _initialPosition;
+ return offset.distance <= tolerance;
+ }
+}
+
+/// Recognizes when the user has tapped the screen at the same location twice in
+/// quick succession.
+class DoubleTapGestureRecognizer extends GestureRecognizer {
+ /// Create a gesture recognizer for double taps.
+ DoubleTapGestureRecognizer({Object debugOwner})
+ : super(debugOwner: debugOwner);
+
+ // Implementation notes:
+ // The double tap recognizer can be in one of four states. There's no
+ // explicit enum for the states, because they are already captured by
+ // the state of existing fields. Specifically:
+ // Waiting on first tap: In this state, the _trackers list is empty, and
+ // _firstTap is null.
+ // First tap in progress: In this state, the _trackers list contains all
+ // the states for taps that have begun but not completed. This list can
+ // have more than one entry if two pointers begin to tap.
+ // Waiting on second tap: In this state, one of the in-progress taps has
+ // completed successfully. The _trackers list is again empty, and
+ // _firstTap records the successful tap.
+ // Second tap in progress: Much like the "first tap in progress" state, but
+ // _firstTap is non-null. If a tap completes successfully while in this
+ // state, the callback is called and the state is reset.
+ // There are various other scenarios that cause the state to reset:
+ // - All in-progress taps are rejected (by time, distance, pointercancel, etc)
+ // - The long timer between taps expires
+ // - The gesture arena decides we have been rejected wholesale
+
+ /// Called when the user has tapped the screen at the same location twice in
+ /// quick succession.
+ GestureDoubleTapCallback onDoubleTap;
+
+ Timer _doubleTapTimer;
+ _TapTracker _firstTap;
+ final Map _trackers = {};
+
+ @override
+ void addPointer(PointerEvent event) {
+ // Ignore out-of-bounds second taps.
+ if (_firstTap != null &&
+ !_firstTap.isWithinTolerance(event, kDoubleTapSlop)) return;
+ _stopDoubleTapTimer();
+ final _TapTracker tracker = _TapTracker(
+ event: event,
+ entry: GestureBinding.instance.gestureArena.add(event.pointer, this));
+ _trackers[event.pointer] = tracker;
+ tracker.startTrackingPointer(_handleEvent);
+ }
+
+ void _handleEvent(PointerEvent event) {
+ final _TapTracker tracker = _trackers[event.pointer];
+ assert(tracker != null);
+ if (event is PointerUpEvent) {
+ if (_firstTap == null)
+ _registerFirstTap(tracker);
+ else
+ _registerSecondTap(tracker, event);
+ } else if (event is PointerMoveEvent) {
+ if (!tracker.isWithinTolerance(event, kDoubleTapTouchSlop))
+ _reject(tracker);
+ } else if (event is PointerCancelEvent) {
+ _reject(tracker);
+ }
+ }
+
+ @override
+ void acceptGesture(int pointer) {}
+
+ @override
+ void rejectGesture(int pointer) {
+ _TapTracker tracker = _trackers[pointer];
+ // If tracker isn't in the list, check if this is the first tap tracker
+ if (tracker == null && _firstTap != null && _firstTap.pointer == pointer)
+ tracker = _firstTap;
+ // If tracker is still null, we rejected ourselves already
+ if (tracker != null) _reject(tracker);
+ }
+
+ void _reject(_TapTracker tracker) {
+ _trackers.remove(tracker.pointer);
+ tracker.entry.resolve(GestureDisposition.rejected);
+ _freezeTracker(tracker);
+ // If the first tap is in progress, and we've run out of taps to track,
+ // reset won't have any work to do. But if we're in the second tap, we need
+ // to clear intermediate state.
+ if (_firstTap != null && (_trackers.isEmpty || tracker == _firstTap))
+ _reset();
+ }
+
+ @override
+ void dispose() {
+ _reset();
+ super.dispose();
+ }
+
+ void _reset() {
+ _stopDoubleTapTimer();
+ if (_firstTap != null) {
+ // Note, order is important below in order for the resolve -> reject logic
+ // to work properly.
+ final _TapTracker tracker = _firstTap;
+ _firstTap = null;
+ _reject(tracker);
+ GestureBinding.instance.gestureArena.release(tracker.pointer);
+ }
+ _clearTrackers();
+ }
+
+ void _registerFirstTap(_TapTracker tracker) {
+ _startDoubleTapTimer();
+ GestureBinding.instance.gestureArena.hold(tracker.pointer);
+ // Note, order is important below in order for the clear -> reject logic to
+ // work properly.
+ _freezeTracker(tracker);
+ _trackers.remove(tracker.pointer);
+ _clearTrackers();
+ _firstTap = tracker;
+ }
+
+ void _registerSecondTap(_TapTracker tracker, PointerEvent event) {
+ _firstTap.entry.resolve(GestureDisposition.accepted);
+ tracker.entry.resolve(GestureDisposition.accepted);
+ _freezeTracker(tracker);
+ _trackers.remove(tracker.pointer);
+ if (onDoubleTap != null)
+ invokeCallback(
+ 'onDoubleTap', () => onDoubleTap(DoubleDetails(pointerEvent: event)));
+ _reset();
+ }
+
+ void _clearTrackers() {
+ _trackers.values.toList().forEach(_reject);
+ assert(_trackers.isEmpty);
+ }
+
+ void _freezeTracker(_TapTracker tracker) {
+ tracker.stopTrackingPointer(_handleEvent);
+ }
+
+ void _startDoubleTapTimer() {
+ _doubleTapTimer ??= Timer(kDoubleTapTimeout, _reset);
+ }
+
+ void _stopDoubleTapTimer() {
+ if (_doubleTapTimer != null) {
+ _doubleTapTimer.cancel();
+ _doubleTapTimer = null;
+ }
+ }
+
+ @override
+ String get debugDescription => 'double tap';
+}
+
+/// TapGesture represents a full gesture resulting from a single tap sequence,
+/// as part of a [MultiTapGestureRecognizer]. Tap gestures are passive, meaning
+/// that they will not preempt any other arena member in play.
+class _TapGesture extends _TapTracker {
+ _TapGesture(
+ {this.gestureRecognizer, PointerEvent event, Duration longTapDelay})
+ : _lastPosition = event.position,
+ super(
+ event: event,
+ entry: GestureBinding.instance.gestureArena
+ .add(event.pointer, gestureRecognizer)) {
+ startTrackingPointer(handleEvent);
+ if (longTapDelay > Duration.zero) {
+ _timer = Timer(longTapDelay, () {
+ _timer = null;
+ gestureRecognizer._dispatchLongTap(event.pointer, _lastPosition);
+ });
+ }
+ }
+
+ final MultiTapGestureRecognizer gestureRecognizer;
+
+ bool _wonArena = false;
+ Timer _timer;
+
+ Offset _lastPosition;
+ Offset _finalPosition;
+
+ void handleEvent(PointerEvent event) {
+ assert(event.pointer == pointer);
+ if (event is PointerMoveEvent) {
+ if (!isWithinTolerance(event, kTouchSlop))
+ cancel();
+ else
+ _lastPosition = event.position;
+ } else if (event is PointerCancelEvent) {
+ cancel();
+ } else if (event is PointerUpEvent) {
+ stopTrackingPointer(handleEvent);
+ _finalPosition = event.position;
+ _check();
+ }
+ }
+
+ @override
+ void stopTrackingPointer(PointerRoute route) {
+ _timer?.cancel();
+ _timer = null;
+ super.stopTrackingPointer(route);
+ }
+
+ void accept() {
+ _wonArena = true;
+ _check();
+ }
+
+ void reject() {
+ stopTrackingPointer(handleEvent);
+ gestureRecognizer._dispatchCancel(pointer);
+ }
+
+ void cancel() {
+ // If we won the arena already, then entry is resolved, so resolving
+ // again is a no-op. But we still need to clean up our own state.
+ if (_wonArena)
+ reject();
+ else
+ entry.resolve(GestureDisposition.rejected); // eventually calls reject()
+ }
+
+ void _check() {
+ if (_wonArena && _finalPosition != null)
+ gestureRecognizer._dispatchTap(pointer, _finalPosition);
+ }
+}
+
+/// Recognizes taps on a per-pointer basis.
+///
+/// [MultiTapGestureRecognizer] considers each sequence of pointer events that
+/// could constitute a tap independently of other pointers: For example, down-1,
+/// down-2, up-1, up-2 produces two taps, on up-1 and up-2.
+///
+/// See also:
+///
+/// * [TapGestureRecognizer]
+class MultiTapGestureRecognizer extends GestureRecognizer {
+ /// Creates a multi-tap gesture recognizer.
+ ///
+ /// The [longTapDelay] defaults to [Duration.zero], which means
+ /// [onLongTapDown] is called immediately after [onTapDown].
+ MultiTapGestureRecognizer({
+ this.longTapDelay = Duration.zero,
+ Object debugOwner,
+ }) : super(debugOwner: debugOwner);
+
+ /// A pointer that might cause a tap has contacted the screen at a particular
+ /// location.
+ GestureMultiTapDownCallback onTapDown;
+
+ /// A pointer that will trigger a tap has stopped contacting the screen at a
+ /// particular location.
+ GestureMultiTapUpCallback onTapUp;
+
+ /// A tap has occurred.
+ GestureMultiTapCallback onTap;
+
+ /// The pointer that previously triggered [onTapDown] will not end up causing
+ /// a tap.
+ GestureMultiTapCancelCallback onTapCancel;
+
+ /// The amount of time between [onTapDown] and [onLongTapDown].
+ Duration longTapDelay;
+
+ /// A pointer that might cause a tap is still in contact with the screen at a
+ /// particular location after [longTapDelay].
+ GestureMultiTapDownCallback onLongTapDown;
+
+ final Map _gestureMap = {};
+
+ @override
+ void addPointer(PointerEvent event) {
+ assert(!_gestureMap.containsKey(event.pointer));
+ _gestureMap[event.pointer] = _TapGesture(
+ gestureRecognizer: this, event: event, longTapDelay: longTapDelay);
+ if (onTapDown != null)
+ invokeCallback(
+ 'onTapDown',
+ () => onTapDown(
+ event.pointer, TapDownDetails(globalPosition: event.position)));
+ }
+
+ @override
+ void acceptGesture(int pointer) {
+ assert(_gestureMap.containsKey(pointer));
+ _gestureMap[pointer].accept();
+ }
+
+ @override
+ void rejectGesture(int pointer) {
+ assert(_gestureMap.containsKey(pointer));
+ _gestureMap[pointer].reject();
+ assert(!_gestureMap.containsKey(pointer));
+ }
+
+ void _dispatchCancel(int pointer) {
+ assert(_gestureMap.containsKey(pointer));
+ _gestureMap.remove(pointer);
+ if (onTapCancel != null)
+ invokeCallback('onTapCancel', () => onTapCancel(pointer));
+ }
+
+ void _dispatchTap(int pointer, Offset globalPosition) {
+ assert(_gestureMap.containsKey(pointer));
+ _gestureMap.remove(pointer);
+ if (onTapUp != null)
+ invokeCallback('onTapUp',
+ () => onTapUp(pointer, TapUpDetails(globalPosition: globalPosition)));
+ if (onTap != null) invokeCallback('onTap', () => onTap(pointer));
+ }
+
+ void _dispatchLongTap(int pointer, Offset lastPosition) {
+ assert(_gestureMap.containsKey(pointer));
+ if (onLongTapDown != null)
+ invokeCallback(
+ 'onLongTapDown',
+ () => onLongTapDown(
+ pointer, TapDownDetails(globalPosition: lastPosition)));
+ }
+
+ @override
+ void dispose() {
+ final List<_TapGesture> localGestures =
+ List<_TapGesture>.from(_gestureMap.values);
+ for (_TapGesture gesture in localGestures) gesture.cancel();
+ // Rejection of each gesture should cause it to be removed from our map
+ assert(_gestureMap.isEmpty);
+ super.dispose();
+ }
+
+ @override
+ String get debugDescription => 'multitap';
+}
diff --git a/lib/my_flutter_drag_scale/lib/core/scale.dart b/lib/my_flutter_drag_scale/lib/core/scale.dart
new file mode 100644
index 0000000..1bb6dd9
--- /dev/null
+++ b/lib/my_flutter_drag_scale/lib/core/scale.dart
@@ -0,0 +1,513 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:math' as math;
+import 'dart:async' show Timer;
+
+import 'package:flutter/src/gestures/arena.dart';
+import 'package:flutter/src/gestures/constants.dart';
+import 'package:flutter/src/gestures/events.dart';
+import 'package:flutter/src/gestures/recognizer.dart';
+import 'package:flutter/src/gestures/velocity_tracker.dart';
+import './double_details.dart';
+
+/// The possible states of a [ScaleGestureRecognizer].
+enum _ScaleState {
+ /// The recognizer is ready to start recognizing a gesture.
+ ready,
+
+ /// The sequence of pointer events seen thus far is consistent with a scale
+ /// gesture but the gesture has not been accepted definitively.
+ possible,
+
+ /// The sequence of pointer events seen thus far has been accepted
+ /// definitively as a scale gesture.
+ accepted,
+
+ /// The sequence of pointer events seen thus far has been accepted
+ /// definitively as a scale gesture and the pointers established a focal point
+ /// and initial scale.
+ started,
+}
+
+/// Details for [GestureScaleStartCallback].
+class ScaleStartDetails {
+ /// Creates details for [GestureScaleStartCallback].
+ ///
+ /// The [focalPoint] argument must not be null.
+ ScaleStartDetails({this.focalPoint = Offset.zero})
+ : assert(focalPoint != null);
+
+ /// The initial focal point of the pointers in contact with the screen.
+ /// Reported in global coordinates.
+ final Offset focalPoint;
+
+ @override
+ String toString() => 'ScaleStartDetails(focalPoint: $focalPoint)';
+}
+
+/// Details for [GestureScaleUpdateCallback].
+class ScaleUpdateDetails {
+ /// Creates details for [GestureScaleUpdateCallback].
+ ///
+ /// The [focalPoint], [scale], [rotation] arguments must not be null. The [scale]
+ /// argument must be greater than or equal to zero.
+ ScaleUpdateDetails({
+ this.focalPoint = Offset.zero,
+ this.scale = 1.0,
+ this.rotation = 0.0,
+ this.pointerEvent,
+ this.pointCount = 1,
+ }) : assert(scale != null && scale >= 0.0),
+ assert(rotation != null);
+
+ /// The focal point of the pointers in contact with the screen. Reported in
+ /// global coordinates.
+ final Offset focalPoint;
+
+ /// The scale implied by the pointers in contact with the screen. A value
+ /// greater than or equal to zero.
+ final double scale;
+
+ /// The angle implied by the first two pointers to enter in contact with
+ /// the screen. Expressed in radians.
+ final double rotation;
+
+ final PointerEvent pointerEvent;
+ final int pointCount;
+
+ @override
+ String toString() =>
+ 'ScaleUpdateDetails(focalPoint: $focalPoint, scale: $scale, rotation: $rotation, pointerEvent: $pointerEvent, pointCount: $pointCount)';
+}
+
+/// Details for [GestureScaleEndCallback].
+class ScaleEndDetails {
+ /// Creates details for [GestureScaleEndCallback].
+ ///
+ /// The [velocity] argument must not be null.
+ ScaleEndDetails({this.velocity = Velocity.zero}) : assert(velocity != null);
+
+ /// The velocity of the last pointer to be lifted off of the screen.
+ final Velocity velocity;
+
+ @override
+ String toString() => 'ScaleEndDetails(velocity: $velocity)';
+}
+
+/// Signature for when the pointers in contact with the screen have established
+/// a focal point and initial scale of 1.0.
+typedef GestureScaleStartCallback = void Function(ScaleStartDetails details);
+
+/// Signature for when the pointers in contact with the screen have indicated a
+/// new focal point and/or scale.
+typedef GestureScaleUpdateCallback = void Function(ScaleUpdateDetails details);
+
+/// Signature for when the pointers are no longer in contact with the screen.
+typedef GestureScaleEndCallback = void Function(ScaleEndDetails details);
+
+bool _isFlingGesture(Velocity velocity) {
+ assert(velocity != null);
+ final double speedSquared = velocity.pixelsPerSecond.distanceSquared;
+ return speedSquared > kMinFlingVelocity * kMinFlingVelocity;
+}
+
+/// Defines a line between two pointers on screen.
+///
+/// [_LineBetweenPointers] is an abstraction of a line between two pointers in
+/// contact with the screen. Used to track the rotation of a scale gesture.
+class _LineBetweenPointers {
+ /// Creates a [_LineBetweenPointers]. None of the [pointerStartLocation], [pointerStartId]
+ /// [pointerEndLocation] and [pointerEndId] must be null. [pointerStartId] and [pointerEndId]
+ /// should be different.
+ _LineBetweenPointers(
+ {this.pointerStartLocation = Offset.zero,
+ this.pointerStartId = 0,
+ this.pointerEndLocation = Offset.zero,
+ this.pointerEndId = 1})
+ : assert(pointerStartLocation != null && pointerEndLocation != null),
+ assert(pointerStartId != null && pointerEndId != null),
+ assert(pointerStartId != pointerEndId);
+
+ // The location and the id of the pointer that marks the start of the line.
+ final Offset pointerStartLocation;
+ final int pointerStartId;
+
+ // The location and the id of the pointer that marks the end of the line.
+ final Offset pointerEndLocation;
+ final int pointerEndId;
+}
+
+/// Recognizes a scale gesture.
+///
+/// [ScaleGestureRecognizer] tracks the pointers in contact with the screen and
+/// calculates their focal point, indicated scale, and rotation. When a focal
+/// pointer is established, the recognizer calls [onStart]. As the focal point,
+/// scale, rotation change, the recognizer calls [onUpdate]. When the pointers
+/// are no longer in contact with the screen, the recognizer calls [onEnd].
+class ScaleGestureRecognizer extends OneSequenceGestureRecognizer {
+ /// Create a gesture recognizer for interactions intended for scaling content.
+ ScaleGestureRecognizer({Object debugOwner}) : super(debugOwner: debugOwner);
+
+ /// The pointers in contact with the screen have established a focal point and
+ /// initial scale of 1.0.
+ GestureScaleStartCallback onStart;
+
+ /// The pointers in contact with the screen have indicated a new focal point
+ /// and/or scale.
+ GestureScaleUpdateCallback onUpdate;
+
+ /// The pointers are no longer in contact with the screen.
+ GestureScaleEndCallback onEnd;
+
+ _ScaleState _state = _ScaleState.ready;
+
+ Offset _initialFocalPoint;
+ Offset _currentFocalPoint;
+ double _initialSpan;
+ double _currentSpan;
+ _LineBetweenPointers _initialLine;
+ _LineBetweenPointers _currentLine;
+ Map _pointerLocations;
+ List _pointerQueue;
+ int pointCount = 0;
+ bool isOnlyOnePoint = true; // 表示
+ /// --------------------------DoubleTap-start--------------------------
+
+ /// Called when the user has tapped the screen at the same location twice in
+ /// quick succession.
+ GestureDoubleTapCallback onDoubleTap;
+
+ /// is track pointer
+ /// 是否追踪手指
+ bool _isTrackingPointer = false;
+
+ bool isFirstTap = true;
+
+ /// timer
+ Timer _doubleTapTimer;
+
+ /// start track pointer
+ /// 开始追踪双击手指
+ void startDoubleTracking() {
+ if (!_isTrackingPointer) {
+ _isTrackingPointer = true;
+ }
+ }
+
+ /// stop track pointer
+ /// 停止追踪双击手指
+ void stopDoubleTracking() {
+ if (_isTrackingPointer) {
+ _isTrackingPointer = false;
+ }
+ }
+
+ /// is two point within tolerance
+ /// 双击 是否两个点在一个范围内
+ bool isWithinTolerance(PointerEvent event, double tolerance) {
+ final Offset offset = event.position - _initialFocalPoint;
+ return offset.distance <= tolerance;
+ }
+
+ void _reset() {
+ _stopDoubleTapTimer();
+ if (!isFirstTap) {
+ // Note, order is important below in order for the resolve -> reject logic
+ // to work properly.
+ isFirstTap = true;
+ }
+ }
+
+ void _registerFirstTap() {
+ _startDoubleTapTimer();
+ // Note, order is important below in order for the clear -> reject logic to
+ // work properly.
+ isFirstTap = false;
+ }
+
+ void _registerSecondTap(PointerEvent event) {
+ if (onDoubleTap != null)
+ invokeCallback(
+ 'onDoubleTap', () => onDoubleTap(DoubleDetails(pointerEvent: event)));
+ _reset();
+ }
+
+ void _startDoubleTapTimer() {
+ _doubleTapTimer ??= Timer(kDoubleTapTimeout, _reset);
+ }
+
+ void _stopDoubleTapTimer() {
+ if (_doubleTapTimer != null) {
+ _doubleTapTimer.cancel();
+ _doubleTapTimer = null;
+ }
+ }
+
+ void _reject() {
+ if (!isFirstTap) _reset();
+ }
+
+ void _doubleTapAddPoniter(PointerEvent event) {
+ _stopDoubleTapTimer();
+ if (event is PointerUpEvent) {
+ if (isFirstTap) {
+ _registerFirstTap();
+ } else {
+ _registerSecondTap(event);
+ }
+ } else if (event is PointerMoveEvent) {
+ if (!isWithinTolerance(event, kDoubleTapTouchSlop)) _reject();
+ } else if (event is PointerCancelEvent) {
+ _reject();
+ }
+ }
+
+ /// --------------------------DoubleTap-end--------------------------
+
+ /// A queue to sort pointers in order of entrance
+ final Map _velocityTrackers = {};
+
+ double get _scaleFactor =>
+ _initialSpan > 0.0 ? _currentSpan / _initialSpan : 1.0;
+
+ double _computeRotationFactor() {
+ if (_initialLine == null || _currentLine == null) {
+ return 0.0;
+ }
+ final double fx = _initialLine.pointerStartLocation.dx;
+ final double fy = _initialLine.pointerStartLocation.dy;
+ final double sx = _initialLine.pointerEndLocation.dx;
+ final double sy = _initialLine.pointerEndLocation.dy;
+
+ final double nfx = _currentLine.pointerStartLocation.dx;
+ final double nfy = _currentLine.pointerStartLocation.dy;
+ final double nsx = _currentLine.pointerEndLocation.dx;
+ final double nsy = _currentLine.pointerEndLocation.dy;
+
+ final double angle1 = math.atan2(fy - sy, fx - sx);
+ final double angle2 = math.atan2(nfy - nsy, nfx - nsx);
+
+ return angle2 - angle1;
+ }
+
+ @override
+ void addPointer(PointerEvent event) {
+ startTrackingPointer(event.pointer);
+ _velocityTrackers[event.pointer] = VelocityTracker();
+ if (_state == _ScaleState.ready) {
+ _state = _ScaleState.possible;
+ _initialSpan = 0.0;
+ _currentSpan = 0.0;
+ _pointerLocations = {};
+ _pointerQueue = [];
+ }
+ _doubleTapAddPoniter(event);
+ // else if (_state == _ScaleState.accepted && _pointerQueue.length == 0) {
+ // resolve(GestureDisposition.accepted);
+ // }
+ }
+
+ @override
+ void handleEvent(PointerEvent event) {
+ assert(_state != _ScaleState.ready);
+ bool didChangeConfiguration = false;
+ bool shouldStartIfAccepted = false;
+ if (event is PointerMoveEvent) {
+ final VelocityTracker tracker = _velocityTrackers[event.pointer];
+ assert(tracker != null);
+ if (!event.synthesized)
+ tracker.addPosition(event.timeStamp, event.position);
+ _pointerLocations[event.pointer] = event.position;
+ shouldStartIfAccepted = true;
+
+ pointCount = _pointerLocations.keys.length;
+ if (pointCount <= 1 && onUpdate != null && isOnlyOnePoint && !isFirstTap)
+ invokeCallback(
+ 'onUpdate',
+ () => onUpdate(ScaleUpdateDetails(
+ scale: _scaleFactor,
+ focalPoint: _currentFocalPoint,
+ rotation: _computeRotationFactor(),
+ pointerEvent: event,
+ pointCount: pointCount)));
+ } else if (event is PointerDownEvent) {
+ isOnlyOnePoint = _pointerQueue.length == 0;
+
+ _pointerLocations[event.pointer] = event.position;
+ _pointerQueue.add(event.pointer);
+ didChangeConfiguration = true;
+ shouldStartIfAccepted = true;
+ } else if (event is PointerUpEvent || event is PointerCancelEvent) {
+ _pointerLocations.remove(event.pointer);
+ _pointerQueue.remove(event.pointer);
+ didChangeConfiguration = true;
+ }
+ _updateLines();
+ _update();
+
+ if (!didChangeConfiguration || _reconfigure(event.pointer)) {
+ _advanceStateMachine(shouldStartIfAccepted, event);
+ }
+ stopTrackingIfPointerNoLongerDown(event);
+ }
+
+ void _update() {
+ final int count = _pointerLocations.keys.length;
+
+ // Compute the focal point
+ Offset focalPoint = Offset.zero;
+ for (int pointer in _pointerLocations.keys)
+ focalPoint += _pointerLocations[pointer];
+ _currentFocalPoint =
+ count > 0 ? focalPoint / count.toDouble() : Offset.zero;
+
+ // Span is the average deviation from focal point
+ double totalDeviation = 0.0;
+ for (int pointer in _pointerLocations.keys)
+ totalDeviation +=
+ (_currentFocalPoint - _pointerLocations[pointer]).distance;
+ _currentSpan = count > 0 ? totalDeviation / count : 0.0;
+ }
+
+ /// Updates [_initialLine] and [_currentLine] accordingly to the situation of
+ /// the registered pointers
+ void _updateLines() {
+ final int count = _pointerLocations.keys.length;
+ assert(_pointerQueue.length >= count);
+
+ /// In case of just one pointer registered, reconfigure [_initialLine]
+ if (count < 2) {
+ _initialLine = _currentLine;
+ } else if (_initialLine != null &&
+ _initialLine.pointerStartId == _pointerQueue[0] &&
+ _initialLine.pointerEndId == _pointerQueue[1]) {
+ /// Rotation updated, set the [_currentLine]
+ _currentLine = _LineBetweenPointers(
+ pointerStartId: _pointerQueue[0],
+ pointerStartLocation: _pointerLocations[_pointerQueue[0]],
+ pointerEndId: _pointerQueue[1],
+ pointerEndLocation: _pointerLocations[_pointerQueue[1]]);
+ } else {
+ /// A new rotation process is on the way, set the [_initialLine]
+ _initialLine = _LineBetweenPointers(
+ pointerStartId: _pointerQueue[0],
+ pointerStartLocation: _pointerLocations[_pointerQueue[0]],
+ pointerEndId: _pointerQueue[1],
+ pointerEndLocation: _pointerLocations[_pointerQueue[1]]);
+ _currentLine = null;
+ }
+ }
+
+ bool _reconfigure(int pointer) {
+ _initialFocalPoint = _currentFocalPoint;
+ _initialSpan = _currentSpan;
+ _initialLine = _currentLine;
+ if (_state == _ScaleState.started) {
+ if (onEnd != null) {
+ final VelocityTracker tracker = _velocityTrackers[pointer];
+ assert(tracker != null);
+
+ Velocity velocity = tracker.getVelocity();
+ if (_isFlingGesture(velocity)) {
+ final Offset pixelsPerSecond = velocity.pixelsPerSecond;
+ if (pixelsPerSecond.distanceSquared >
+ kMaxFlingVelocity * kMaxFlingVelocity)
+ velocity = Velocity(
+ pixelsPerSecond: (pixelsPerSecond / pixelsPerSecond.distance) *
+ kMaxFlingVelocity);
+ invokeCallback(
+ 'onEnd', () => onEnd(ScaleEndDetails(velocity: velocity)));
+ } else {
+ invokeCallback(
+ 'onEnd', () => onEnd(ScaleEndDetails(velocity: Velocity.zero)));
+ }
+ }
+ _state = _ScaleState.accepted;
+ return false;
+ }
+ return true;
+ }
+
+ void _advanceStateMachine(
+ bool shouldStartIfAccepted, PointerEvent pointerEvent) {
+ if (_state == _ScaleState.ready) _state = _ScaleState.possible;
+
+ if (_state == _ScaleState.possible) {
+ final double spanDelta = (_currentSpan - _initialSpan).abs();
+ final double focalPointDelta =
+ (_currentFocalPoint - _initialFocalPoint).distance;
+ if (spanDelta > kScaleSlop || focalPointDelta > kPanSlop)
+ resolve(GestureDisposition.accepted);
+ } else if (_state.index >= _ScaleState.accepted.index) {
+ resolve(GestureDisposition.accepted);
+ }
+
+ if (_state == _ScaleState.accepted && shouldStartIfAccepted) {
+ _state = _ScaleState.started;
+ _dispatchOnStartCallbackIfNeeded();
+ }
+
+ // if (_state == _ScaleState.started && onUpdate != null)
+ if (_state == _ScaleState.started && onUpdate != null)
+ invokeCallback(
+ 'onUpdate',
+ () => onUpdate(ScaleUpdateDetails(
+ scale: _scaleFactor,
+ focalPoint: _currentFocalPoint,
+ rotation: _computeRotationFactor(),
+ pointerEvent: pointerEvent,
+ pointCount: pointCount)));
+ }
+
+ void _dispatchOnStartCallbackIfNeeded() {
+ assert(_state == _ScaleState.started);
+ if (onStart != null)
+ invokeCallback('onStart',
+ () => onStart(ScaleStartDetails(focalPoint: _currentFocalPoint)));
+ }
+
+ @override
+ void acceptGesture(int pointer) {
+ resolve(GestureDisposition.accepted);
+ if (_state == _ScaleState.possible) {
+ _state = _ScaleState.started;
+ _dispatchOnStartCallbackIfNeeded();
+ }
+ }
+
+ @override
+ void rejectGesture(int pointer) {
+ stopTrackingPointer(pointer);
+ }
+
+ @override
+ void didStopTrackingLastPointer(int pointer) {
+ switch (_state) {
+ case _ScaleState.possible:
+ resolve(GestureDisposition.rejected);
+ break;
+ case _ScaleState.ready:
+ assert(false); // We should have not seen a pointer yet
+ break;
+ case _ScaleState.accepted:
+ break;
+ case _ScaleState.started:
+ assert(false); // We should be in the accepted state when user is done
+ break;
+ }
+ _state = _ScaleState.ready;
+ }
+
+ @override
+ void dispose() {
+ _velocityTrackers.clear();
+ _reset();
+
+ super.dispose();
+ }
+
+ @override
+ String get debugDescription => 'scale';
+}
diff --git a/lib/my_flutter_drag_scale/lib/core/touchable_container.dart b/lib/my_flutter_drag_scale/lib/core/touchable_container.dart
new file mode 100644
index 0000000..3305049
--- /dev/null
+++ b/lib/my_flutter_drag_scale/lib/core/touchable_container.dart
@@ -0,0 +1,162 @@
+import 'package:flutter/material.dart';
+import './custom_gesture_detector.dart' as gd;
+
+class ScaleChangedModel {
+ double scale;
+ Offset offset;
+
+ ScaleChangedModel({this.scale, this.offset});
+
+ @override
+ String toString() {
+ return 'ScaleChangedModel(scale: $scale, offset:$offset)';
+ }
+}
+
+class TouchableContainer extends StatefulWidget {
+ final Widget child;
+ final bool doubleTapStillScale;
+
+ ///用来约束图和坐标轴的
+ ///因为坐标轴和图是堆叠起来的,图在坐标轴的内部,需要制定margin,否则放大后图会超出坐标轴
+ final EdgeInsets margin;
+ ValueChanged scaleChangedCallback; // 图片缩放倍数改变回调函数
+ Function() pointerDownCallback; // 图片指针按下回调函数
+ Function() pointerUpCallback; // 图片指针弹起回调函数
+
+ TouchableContainer({
+ Key key,
+ this.child,
+ this.margin = const EdgeInsets.all(0),
+ this.pointerDownCallback,
+ this.pointerUpCallback,
+ this.scaleChangedCallback,
+ this.doubleTapStillScale,
+ });
+
+ _TouchableContainerState createState() => _TouchableContainerState();
+}
+
+class _TouchableContainerState extends State
+ with SingleTickerProviderStateMixin {
+ double _kMinFlingVelocity = 800.0;
+ AnimationController _controller;
+ Animation _flingAnimation;
+ Offset _offset = Offset.zero;
+ double _scale = 1.0;
+ Offset _normalizedOffset;
+ double _previousScale;
+ Offset doubleDownPositon;
+
+ @override
+ void initState() {
+ super.initState();
+ _controller = new AnimationController(vsync: this)..addListener(_handleFlingAnimation);
+ }
+
+ @override
+ void dispose() {
+ _controller.dispose();
+ super.dispose();
+ }
+
+ // The maximum offset value is 0,0. If the size of this renderer's box is w,h
+ // then the minimum offset value is w - _scale * w, h - _scale * h.
+ //也就是最小值是原点0,0,点从最大值到0的区间,也就是这个图可以从最大值移动到原点
+ Offset _clampOffset(Offset offset) {
+ final Size size = context.size; //容器的大小
+ final Offset minOffset = new Offset(size.width, size.height) * (1.0 - _scale);
+ return new Offset(offset.dx.clamp(minOffset.dx, 0.0), offset.dy.clamp(minOffset.dy, 0.0));
+ }
+
+ void _handleFlingAnimation() {
+ setState(() {
+ _offset = _flingAnimation.value;
+ });
+ }
+
+ void _handleOnScaleStart(gd.ScaleStartDetails details) {
+ setState(() {
+ _previousScale = _scale;
+ _normalizedOffset = (details.focalPoint - _offset) / _scale;
+ // The fling animation stops if an input gesture starts.
+ _controller.stop();
+ });
+ }
+
+ void _handleOnScaleUpdate(gd.ScaleUpdateDetails details) {
+ setState(() {
+ if (details.pointCount > 1) {
+ _scale = (_previousScale * details.scale).clamp(1.0, double.infinity);
+ }
+ // Ensure that image location under the focal point stays in the same place despite scaling.
+ _offset = _clampOffset(details.focalPoint - _normalizedOffset * _scale);
+ });
+ ScaleChangedModel model = new ScaleChangedModel(scale: _scale, offset: _offset);
+ if (widget.scaleChangedCallback != null) widget.scaleChangedCallback(model);
+ }
+
+ void _handleOnScaleEnd(gd.ScaleEndDetails details) {
+ final double magnitude = details.velocity.pixelsPerSecond.distance;
+ if (magnitude < _kMinFlingVelocity) return;
+ final Offset direction = details.velocity.pixelsPerSecond / magnitude;
+ final double distance = (Offset.zero & context.size).shortestSide;
+ _flingAnimation =
+ new Tween(begin: _offset, end: _clampOffset(_offset + direction * distance))
+ .animate(_controller);
+ _controller
+ ..value = 0.0
+ ..fling(velocity: magnitude / 1000.0);
+ }
+
+ void _onDoubleTap(gd.DoubleDetails details) {
+ _normalizedOffset = (details.pointerEvent.position - _offset) / _scale;
+ if (!widget.doubleTapStillScale && _scale != 1.0) {
+ setState(() {
+ _scale = 1.0;
+ _offset = Offset.zero;
+ });
+ ScaleChangedModel model = new ScaleChangedModel(scale: _scale, offset: _offset);
+ if (widget.scaleChangedCallback != null) widget.scaleChangedCallback(model);
+ return;
+ }
+ setState(() {
+ if (widget.doubleTapStillScale) {
+ _scale *= (1 + 0.5);
+ } else {
+ _scale *= (2);
+ }
+ // Ensure that image location under the focal point stays in the same place despite scaling.
+ // _offset = doubleDownPositon;
+ _offset = _clampOffset(details.pointerEvent.position - _normalizedOffset * _scale);
+ });
+
+ ScaleChangedModel model = new ScaleChangedModel(scale: _scale, offset: _offset);
+ if (widget.scaleChangedCallback != null) widget.scaleChangedCallback(model);
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return new gd.GestureDetector(
+ // onPanDown: _onPanDown,
+ onDoubleTap: _onDoubleTap,
+ onScaleStart: _handleOnScaleStart,
+ onScaleUpdate: _handleOnScaleUpdate,
+ pointerDownCallback: widget.pointerDownCallback,
+ pointerUpCallback: widget.pointerUpCallback,
+ // onScaleEnd: _handleOnScaleEnd,
+ child: Container(
+ margin: widget.margin,
+ constraints: const BoxConstraints(
+ minWidth: double.maxFinite,
+ minHeight: double.infinity,
+ ),
+ child: new Transform(
+ transform: new Matrix4.identity()
+ ..translate(_offset.dx, _offset.dy)
+ ..scale(_scale, _scale, 1.0),
+ child: widget.child),
+ ),
+ );
+ }
+}
diff --git a/lib/my_flutter_drag_scale/lib/flutter_drag_scale.dart b/lib/my_flutter_drag_scale/lib/flutter_drag_scale.dart
new file mode 100644
index 0000000..fdab3c9
--- /dev/null
+++ b/lib/my_flutter_drag_scale/lib/flutter_drag_scale.dart
@@ -0,0 +1,4 @@
+library flutter_drag_scale;
+
+export './core/drag_scale_widget.dart';
+export './core/touchable_container.dart' show ScaleChangedModel;
diff --git a/lib/my_flutter_drag_scale/pubspec.yaml b/lib/my_flutter_drag_scale/pubspec.yaml
new file mode 100644
index 0000000..079476c
--- /dev/null
+++ b/lib/my_flutter_drag_scale/pubspec.yaml
@@ -0,0 +1,60 @@
+name: flutter_drag_scale
+description: A new flutter plugin project.
+version: 0.0.1
+author: liucheng
+homepage: https://github.com/LiuC520/flutter_drag_scale
+
+environment:
+ sdk: ">=2.0.0 <3.0.0"
+
+dependencies:
+ flutter:
+ sdk: flutter
+
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
+
+# For information on the generic Dart part of this file, see the
+# following page: https://www.dartlang.org/tools/pub/pubspec
+
+# The following section is specific to Flutter.
+flutter:
+ # This section identifies this Flutter project as a plugin project.
+ # The androidPackage and pluginClass identifiers should not ordinarily
+ # be modified. They are used by the tooling to maintain consistency when
+ # adding or updating assets for this project.
+ plugin:
+ androidPackage: com.example.flutter_drag_scale
+ pluginClass: FlutterDragScalePlugin
+
+ # To add assets to your plugin package, add an assets section, like this:
+ # assets:
+ # - images/a_dot_burr.jpeg
+ # - images/a_dot_ham.jpeg
+ #
+ # For details regarding assets in packages, see
+ # https://flutter.io/assets-and-images/#from-packages
+ #
+ # An image asset can refer to one or more resolution-specific "variants", see
+ # https://flutter.io/assets-and-images/#resolution-aware.
+
+ # To add custom fonts to your plugin package, add a fonts section here,
+ # in this "flutter" section. Each entry in this list should have a
+ # "family" key with the font family name, and a "fonts" key with a
+ # list giving the asset and other descriptors for the font. For
+ # example:
+ # fonts:
+ # - family: Schyler
+ # fonts:
+ # - asset: fonts/Schyler-Regular.ttf
+ # - asset: fonts/Schyler-Italic.ttf
+ # style: italic
+ # - family: Trajan Pro
+ # fonts:
+ # - asset: fonts/TrajanPro.ttf
+ # - asset: fonts/TrajanPro_Bold.ttf
+ # weight: 700
+ #
+ # For details regarding fonts in packages, see
+ # https://flutter.io/custom-fonts/#from-packages
diff --git a/lib/my_flutter_drag_scale/sample.gif b/lib/my_flutter_drag_scale/sample.gif
new file mode 100644
index 0000000..8f41e60
Binary files /dev/null and b/lib/my_flutter_drag_scale/sample.gif differ
diff --git a/lib/my_flutter_drag_scale/test/flutter_drag_scale_test.dart b/lib/my_flutter_drag_scale/test/flutter_drag_scale_test.dart
new file mode 100644
index 0000000..81195b0
--- /dev/null
+++ b/lib/my_flutter_drag_scale/test/flutter_drag_scale_test.dart
@@ -0,0 +1,7 @@
+import 'package:flutter_test/flutter_test.dart';
+
+void main() {
+ test('getPlatformVersion', () async {
+ // expect(await FlutterDragScale.platformVersion, '42');
+ });
+}
diff --git a/lib/pages/Works/HYSH/hysh_content_new.dart b/lib/pages/Works/HYSH/hysh_content_new.dart
index f293a45..28db962 100644
--- a/lib/pages/Works/HYSH/hysh_content_new.dart
+++ b/lib/pages/Works/HYSH/hysh_content_new.dart
@@ -11,7 +11,7 @@ import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:hyzp_ybqx/widget/my_superplayer.dart';
import 'package:keyboard_avoider/keyboard_avoider.dart';
-import 'package:photo_view/photo_view.dart';
+// import 'package:photo_view/photo_view.dart';
import '../../../components/commonFun.dart';
import '../../../components/dioFun.dart';
@@ -254,29 +254,6 @@ class _HyshPageState extends State with SingleTickerProviderStat
}
}
- // 使用 cached_network_image 插件实现网络图片缓存
- // 使用 flutter_drag_scale 实现可缩放可拖拽双击放大的图片功能。PhotoView插件不好用,有问题
- Widget getNetworkImage(String url) {
- return CachedNetworkImage(
- imageUrl: url,
- alignment: Alignment.topCenter,
- imageBuilder: (context, imageProvider) => DragScaleContainer(
- doubleTapStillScale: true, child: Image(image: imageProvider, fit: BoxFit.fill)
- // child: Image(
- // image: NetworkImage(
- // 'http://h.hiphotos.baidu.com/zhidao/wh%3D450%2C600/sign=0d023672312ac65c67506e77cec29e27/9f2f070828381f30dea167bbad014c086e06f06c.jpg'),
- // ),
- ),
- // imageBuilder: (context, imageProvider) => PhotoView(
- // imageProvider: imageProvider,
- // ),
- //placeholder: (context, url) => CircularProgressIndicator(),
- placeholder: (context, url) =>
- getMoreWidget(color: Colors.black38, size: 20.0, strokeWidth: 2.0),
- errorWidget: (context, url, error) => Icon(Icons.error),
- );
- }
-
//3、得到违章图片说明信息组件
Widget getWztpSmxx(int index) {
return Container(
@@ -541,6 +518,127 @@ class _HyshPageState extends State with SingleTickerProviderStat
);
}
+ // 使用 cached_network_image 插件实现网络图片缓存
+ // 使用 flutter_drag_scale 实现可缩放可拖拽双击放大的图片功能。PhotoView插件不好用,有问题
+ // Widget getNetworkImage0(String url) {
+ // return CachedNetworkImage(
+ // imageUrl: url,
+ // alignment: Alignment.topCenter,
+ // imageBuilder: (context, imageProvider) => DragScaleContainer(
+ // doubleTapStillScale: true, child: Image(image: imageProvider, fit: BoxFit.fill)
+ // // child: Image(
+ // // image: NetworkImage(
+ // // 'http://h.hiphotos.baidu.com/zhidao/wh%3D450%2C600/sign=0d023672312ac65c67506e77cec29e27/9f2f070828381f30dea167bbad014c086e06f06c.jpg'),
+ // // ),
+ // ),
+ // // imageBuilder: (context, imageProvider) => PhotoView(
+ // // imageProvider: imageProvider,
+ // // ),
+ // //placeholder: (context, url) => CircularProgressIndicator(),
+ // placeholder: (context, url) =>
+ // getMoreWidget(color: Colors.black38, size: 20.0, strokeWidth: 2.0),
+ // errorWidget: (context, url, error) => Icon(Icons.error),
+ // );
+ // }
+
+ // 使用 Listener onPointerDown、onPointerUp方法,可以实现,但会消耗点击事件,导致缩放迟钝
+ // Widget getNetworkImage1(String url) {
+ // return ClipRect(
+ // child: Listener(
+ // child: PhotoView.customChild(
+ // // imageProvider: NetworkImage(widget.imageUrl),
+ // // imageProvider: CachedNetworkImageProvider(widget.imageUrl),
+ // minScale: 1.0,
+ // backgroundDecoration: BoxDecoration(color: Colors.white),
+ // child: CachedNetworkImage(
+ // alignment: Alignment.center,
+ // imageUrl: getMediaUrl(url),
+ // fit: BoxFit.fill,
+ // placeholder: (context, url) =>
+ // getMoreWidget(color: Colors.black38, size: 20.0, strokeWidth: 2.0),
+ // errorWidget: (context, url, error) => Icon(Icons.error),
+ // ),
+ // scaleStateChangedCallback: (PhotoViewScaleState statue) {
+ // print('PhotoViewScaleState 状态:$statue');
+ // switch (statue) {
+ // case PhotoViewScaleState.originalSize:
+ // case PhotoViewScaleState.zoomedOut:
+ // case PhotoViewScaleState.initial:
+ // bZoomedInit = false;
+ // break;
+ // default:
+ // bZoomedInit = true;
+ // break;
+ // }
+ // print("bZoomedIn = $bZoomedInit");
+ // },
+ // ),
+ // onPointerDown: (event) {
+ // scrollPhysics = null;
+ // if (bZoomedInit) {
+ // scrollPhysics = NeverScrollableScrollPhysics();
+ // setState(() {});
+ // print("down $event");
+ // }
+ // },
+ // // onPointerMove: (event) => print("move $event"),
+ // onPointerUp: (event) {
+ // scrollPhysics = null;
+ // if (bZoomedInit) {
+ // scrollPhysics = null;
+ // setState(() {});
+ // print("up $event");
+ // }
+ // },
+ // ),
+ // );
+ // }
+
+ // 使用自定义 my_flutter_drag_scale 插件,完美解决Listview滚动与图片缩放拖动之间的手势冲突,不会消耗点击事件,滚动很灵敏
+ Widget getNetworkImage(String url) {
+ return CachedNetworkImage(
+ imageUrl: url,
+ alignment: Alignment.topCenter,
+ placeholder: (context, url) =>
+ getMoreWidget(color: Colors.black38, size: 20.0, strokeWidth: 2.0),
+ errorWidget: (context, url, error) => Icon(Icons.error),
+ imageBuilder: (context, imageProvider) {
+ return DragScaleContainer(
+ doubleTapStillScale: false,
+ child: Image(image: imageProvider, fit: BoxFit.fill),
+ scaleChangedCallback: (ScaleChangedModel model) {
+ print("model.scale = ${model.scale}");
+ if (1.0 == model.scale) {
+ bZoomedInit = true;
+ scrollPhysics = PageScrollPhysics();
+ } else {
+ bZoomedInit = false;
+ scrollPhysics = NeverScrollableScrollPhysics();
+ }
+ setState(() {});
+ },
+ pointerDownCallback: () {
+ if (bZoomedInit) {
+ if (scrollPhysics != PageScrollPhysics()) {
+ scrollPhysics = PageScrollPhysics();
+ setState(() {});
+ }
+ } else {
+ if (scrollPhysics != NeverScrollableScrollPhysics()) {
+ scrollPhysics = NeverScrollableScrollPhysics();
+ setState(() {});
+ }
+ }
+ },
+ pointerUpCallback: () {
+ scrollPhysics = PageScrollPhysics();
+ setState(() {});
+ },
+ );
+ },
+ );
+ }
+
//2、得到违章图片组件
Widget getWztp(int index) {
print('ratioList[index] = ${ratioList[index]}');
@@ -553,12 +651,12 @@ class _HyshPageState extends State with SingleTickerProviderStat
height: ScreenUtil().setHeight(22 + 1022 * g_radioImage), // 兴文县的违章图片无法获取,暂时调整
// height: ScreenUtil()
// .setHeight(22 + 1022 * (ratioList.isNotEmpty ? ratioList[index] : 9 / 16)),
- decoration: BoxDecoration(
- //color: Colors.white,
- borderRadius: BorderRadius.all(
- Radius.circular(12),
- ),
- ),
+ // decoration: BoxDecoration(
+ // color: Colors.white,
+ // borderRadius: BorderRadius.all(
+ // Radius.circular(12),
+ // ),
+ // ),
),
Positioned(
//left: ScreenUtil().setWidth(25),
@@ -569,9 +667,10 @@ class _HyshPageState extends State with SingleTickerProviderStat
// ScreenUtil().setHeight(1022 * (ratioList.isNotEmpty ? ratioList[index] : 9 / 16)),
height: ScreenUtil().setHeight(1022 * g_radioImage),
// 兴文县的违章图片无法获取,暂时调整
+ // child: getNetworkImage(getMediaUrl(listGetZpjl[index]['pic_url'])),
child: getNetworkImage(getMediaUrl(listGetZpjl[index]['pic_url'])),
),
- )
+ ),
],
);
}
@@ -597,6 +696,9 @@ class _HyshPageState extends State with SingleTickerProviderStat
double _marginVertical5 = 10;
double _marginVertical6 = 20;
+ ScrollPhysics scrollPhysics;
+ bool bZoomedInit = true; // 图片是否为原样大小
+
Future getTopTabsMap() async {
//map遍历,清空数据
topTabs_map.forEach((key, value) {
@@ -628,48 +730,48 @@ class _HyshPageState extends State with SingleTickerProviderStat
.add(CarNumberAndCpysItems(index, topTabs_map['cpysText_List'][index]));
//Tab页面
- topTabs_map['listView_List'].add(
- //flutter开发弹起键盘出现Overflow问题的解决方法,我出现的情况,这三种方法就可以解决。
- KeyboardAvoider(
- autoScroll: true,
- child: Container(
- decoration: new BoxDecoration(
- color: Color.fromRGBO(244, 244, 244, 1),
- ),
- child: Column(
- children: [
- //1、得到格林曼黑度标准和视频播放按钮组件
- getHdAndPlay(index),
- //2、得到违章图片组件
- getWztp(index),
- SizedBox(height: ScreenUtil().setHeight(_marginVer)),
- //3、得到违章图片说明信息组件
- getWztpSmxx(index),
- SizedBox(height: ScreenUtil().setHeight(_marginVer)),
- //4、得到黑烟初审结果组件,在复审页面需要
- widget.hyshlx == 'hyfh' ? getHycsResult(index) : SizedBox.shrink(),
- //为了用户在切换审核结果Radio时显示不同图片,必须将以下组件都移入到RadioListItems类中
- //5-6、得到黑烟审核组件、审核确认组件
- HyshGroup(
- index: index,
- hyshlx: hyshlx,
- fontSize: _fontSize,
- size: Size(_listTileHeight, _listTileHeight),
- id: widget.id,
- selectedRadio:
- hyshlx == 'hyfh' && mapGetHycsShenheData['title'] == "非黑烟车" ? 1 : 0,
- ),
- //为了用户在切换审核结果Radio时显示不同图片,必须将以下组件都移入到RadioListItems类中
- // SizedBox(height: 6),
- // Divider(height: 1.0, color: Colors.blue),
- // SizedBox(height: 10),
- // //9、得到审核确认组件
- // getShqr(index),
- ],
- ),
- ),
- ),
- );
+ topTabs_map['listView_List'].add(SizedBox.shrink()
+ //flutter开发弹起键盘出现Overflow问题的解决方法,我出现的情况,这三种方法就可以解决。
+ // KeyboardAvoider(
+ // autoScroll: true,
+ // child: Container(
+ // decoration: new BoxDecoration(
+ // color: Color.fromRGBO(244, 244, 244, 1),
+ // ),
+ // child: Column(
+ // children: [
+ // //1、得到格林曼黑度标准和视频播放按钮组件
+ // getHdAndPlay(index),
+ // //2、得到违章图片组件
+ // getWztp(index),
+ // SizedBox(height: ScreenUtil().setHeight(_marginVer)),
+ // //3、得到违章图片说明信息组件
+ // getWztpSmxx(index),
+ // SizedBox(height: ScreenUtil().setHeight(_marginVer)),
+ // //4、得到黑烟初审结果组件,在复审页面需要
+ // widget.hyshlx == 'hyfh' ? getHycsResult(index) : SizedBox.shrink(),
+ // //为了用户在切换审核结果Radio时显示不同图片,必须将以下组件都移入到RadioListItems类中
+ // //5-6、得到黑烟审核组件、审核确认组件
+ // HyshGroup(
+ // index: index,
+ // hyshlx: hyshlx,
+ // fontSize: _fontSize,
+ // size: Size(_listTileHeight, _listTileHeight),
+ // id: widget.id,
+ // selectedRadio:
+ // hyshlx == 'hyfh' && mapGetHycsShenheData['title'] == "非黑烟车" ? 1 : 0,
+ // ),
+ // //为了用户在切换审核结果Radio时显示不同图片,必须将以下组件都移入到RadioListItems类中
+ // // SizedBox(height: 6),
+ // // Divider(height: 1.0, color: Colors.blue),
+ // // SizedBox(height: 10),
+ // // //9、得到审核确认组件
+ // // getShqr(index),
+ // ],
+ // ),
+ // ),
+ // ),
+ );
}
}
@@ -830,10 +932,66 @@ class _HyshPageState extends State with SingleTickerProviderStat
//type 'Container' is not a subtype of type 'PreferredSizeWidget'
body: listGetZpjl.isNotEmpty
? TabBarView(
- controller: _tabController, //注意:用TabController实现顶部tab切换,必须添加该行
- physics: NeverScrollableScrollPhysics(), //必须放到TabBarView下面,禁止TabBarView左右滑动-OK
- children:
- (topTabs_map['listView_List'].isNotEmpty) ? topTabs_map['listView_List'] : [],
+ controller: _tabController,
+ //注意:用TabController实现顶部tab切换,必须添加该行
+ physics: NeverScrollableScrollPhysics(),
+ //必须放到TabBarView下面,禁止TabBarView左右滑动-OK
+ // children:
+ // (topTabs_map['listView_List'].isNotEmpty) ? topTabs_map['listView_List'] : [],
+
+ // https://blog.csdn.net/shulianghan/article/details/104953053
+ // 集合的生成函数
+ // int length 参数 : 集合的长度
+ // E generator(int index) : 集合的回调函数 , 调用该函数获取集合的 index 位置的元素
+ // List list_generate = List.generate(3, ( index ) => index * 3);
+ // children: List.generate(3, (index) => Container()),
+ children: List.generate(topTabs_map['listView_List'].length, (index) {
+ return ListView(
+ // physics: NeverScrollableScrollPhysics(), // 允许ListView滚动
+ // physics: null, // 允许ListView滚动
+ physics: scrollPhysics,
+ children: [
+ Container(
+ decoration: new BoxDecoration(
+ color: Color.fromRGBO(244, 244, 244, 1),
+ ),
+ child: Column(
+ children: [
+ //1、得到格林曼黑度标准和视频播放按钮组件
+ getHdAndPlay(index),
+ //2、得到违章图片组件
+ getWztp(index),
+ SizedBox(height: ScreenUtil().setHeight(_marginVer)),
+ //3、得到违章图片说明信息组件
+ getWztpSmxx(index),
+ SizedBox(height: ScreenUtil().setHeight(_marginVer)),
+ //4、得到黑烟初审结果组件,在复审页面需要
+ widget.hyshlx == 'hyfh' ? getHycsResult(index) : SizedBox.shrink(),
+ //为了用户在切换审核结果Radio时显示不同图片,必须将以下组件都移入到RadioListItems类中
+ //5-6、得到黑烟审核组件、审核确认组件
+ HyshGroup(
+ index: index,
+ hyshlx: hyshlx,
+ fontSize: _fontSize,
+ size: Size(_listTileHeight, _listTileHeight),
+ id: widget.id,
+ selectedRadio:
+ hyshlx == 'hyfh' && mapGetHycsShenheData['title'] == "非黑烟车"
+ ? 1
+ : 0,
+ ),
+ //为了用户在切换审核结果Radio时显示不同图片,必须将以下组件都移入到RadioListItems类中
+ // SizedBox(height: 6),
+ // Divider(height: 1.0, color: Colors.blue),
+ // SizedBox(height: 10),
+ // //9、得到审核确认组件
+ // getShqr(index),
+ ],
+ ),
+ ),
+ ],
+ );
+ }),
)
: getMoreWidget(color: Colors.black38, size: 20.0, strokeWidth: 2.0), //显示加载中的圈圈
),
diff --git a/pubspec.lock b/pubspec.lock
index 307c4de..517c399 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -317,11 +317,9 @@ packages:
flutter_drag_scale:
dependency: "direct main"
description:
- path: "."
- ref: HEAD
- resolved-ref: "562ac559370da547783c2c5b1c18c931adddb8a4"
- url: "https://github.com/mjl0602/flutter_drag_scale.git"
- source: git
+ path: "lib/my_flutter_drag_scale"
+ relative: true
+ source: path
version: "0.0.1"
flutter_easyrefresh:
dependency: "direct main"
@@ -620,13 +618,6 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.5.8"
- photo_view:
- dependency: "direct main"
- description:
- name: photo_view
- url: "https://pub.flutter-io.cn"
- source: hosted
- version: "0.10.3"
platform:
dependency: transitive
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index 7f3cdc6..bf0ec85 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -129,13 +129,14 @@ dependencies:
image_picker: ^0.6.7+22
cached_network_image: ^2.4.1
flutter_easyrefresh: ^2.1.8
- photo_view: ^0.10.3
+# photo_view: ^0.10.3
flustars: ^0.3.3
keyboard_avoider: ^0.1.2
scroll_to_index: ^1.0.6
flutter_drag_scale:
+ path: ./lib/my_flutter_drag_scale
#git: https://github.com/LiuC520/flutter_drag_scale.git
- git: https://github.com/mjl0602/flutter_drag_scale.git
+ #git: https://github.com/mjl0602/flutter_drag_scale.git
# flutter_bmfmap: ^1.0.2
# 准备使用自定义 flutter_bmfmap: ^1.0.2,以适应IOS版加载文本标识