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/ + + +我们知道官方提供了双击缩放,但是不支持拖拽的功能,我们要实现向百度地图那样可以缩放又可以拖拽的功能,官方的方法就不支持了。 +下面先演示下功能: +![sample.gif](https://upload-images.jianshu.io/upload_images/3463020-7823ae1e8d9bf0f9.gif?imageMogr2/auto-orient/strip) + +参数只有两个: +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版加载文本标识