├── .gitignore ├── .metadata ├── .vscode └── settings.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── example ├── .gitignore ├── .metadata ├── LICENSE ├── README.md ├── analysis_options.yaml ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── example │ │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle ├── ios │ ├── Flutter │ │ ├── .last_build_id │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ ├── Flutter.podspec │ │ └── Release.xcconfig │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── Runner │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── main.m ├── lib │ └── main.dart ├── pubspec.yaml ├── test │ └── widget_test.dart └── web │ ├── favicon.png │ ├── icons │ ├── Icon-192.png │ ├── Icon-512.png │ ├── Icon-maskable-192.png │ └── Icon-maskable-512.png │ ├── index.html │ └── manifest.json ├── lib └── flutter_reorderable_list.dart ├── pubspec.yaml └── test └── flutter_reorderable_list_test.dart /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | 4 | .packages 5 | .pub/ 6 | 7 | pubspec.lock 8 | 9 | build/ 10 | ios/.generated/ 11 | ios/Flutter/Generated.xcconfig 12 | ios/Flutter/flutter_export_environment.sh 13 | ios/Runner/GeneratedPluginRegistrant.* 14 | 15 | # IntelliJ 16 | *.iml 17 | *.ipr 18 | *.iws 19 | .idea/ 20 | 21 | example/ios/.generated/ 22 | example/ios/Flutter/Generated.xcconfig 23 | example/ios/Flutter/flutter_export_environment.sh 24 | example/ios/Runner/GeneratedPluginRegistrant.* 25 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: d9ad220bbb17972913fb687f8d673d79b9f66dab 8 | channel: master 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "dart.flutterSdkPath": "/Users/Matej/Projects/flutter/flutter" 3 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [0.1.0] 2 | 3 | * Initial release 4 | 5 | ## [0.1.2] 6 | 7 | * Add delayed (android-like) reordering 8 | 9 | ## [0.1.3] 10 | 11 | * Fix regression for CancellationToken 12 | 13 | ## [0.1.4] 14 | 15 | * Added `decoratePlaceholder` constructor property to allow customizing placeholder decoration 16 | * Haptic feedback on reorder is only done on iOS 17 | * Support SliverPadding 18 | 19 | ## [0.1.5] 20 | 21 | * Fix compatibility with Flutter 1.22.0 22 | 23 | ## [1.0.0] 24 | 25 | * Migrate to NNBD 26 | 27 | ## [1.1.0] 28 | 29 | * Migration to Flutter 2.5 30 | 31 | ## [1.2.0] 32 | 33 | * Migration to Flutter 2.8 34 | 35 | ## [1.3.0] 36 | 37 | * Migration to Flutter 3.0 38 | 39 | ## [1.3.1] 40 | 41 | * Fix compatibility with latest Dart SDK 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 Matej Knopp. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following 11 | disclaimer in the documentation and/or other materials provided 12 | with the distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived 15 | from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reorderable List in Flutter 2 | 3 | iOS-like proof of concept reorderable list with animations 4 | 5 | ## Preview 6 | 7 | 8 | 9 | ## Getting Started 10 | 11 | See `example/lib/main.dart` for example usage 12 | 13 | ## Highlights 14 | 15 | Unlike flutter's `ReorderableListView` this one 16 | * Works with slivers so it can be placed in `CustomScrollView` and used with `SliverAppBar` 17 | * Supports large lists (thousands of items) without any issues 18 | 19 | Other features 20 | 21 | * Smooth reordering animations 22 | * Supports different item heights 23 | * iOS like reordering with drag handle 24 | * Android like (long touch) reordering 25 | 26 | ## Caveats 27 | 28 | There are no API stability guarantees. 29 | 30 | If you used previous version of reorderable list keep in mind that `ReorderableListener` now needs to be placed somewhere in `ReorderableItem` hierarchy in order to detect touch and trigger actual reordering (see the example). 31 | 32 | Alternatively, you can wrap entire row in `DelayedReorderableListener` to get material like long-press reordering behavior. 33 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.lock 4 | *.log 5 | *.pyc 6 | *.swp 7 | .DS_Store 8 | .atom/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # Visual Studio Code related 20 | .vscode/ 21 | 22 | # Flutter/Dart/Pub related 23 | **/doc/api/ 24 | .dart_tool/ 25 | .flutter-plugins 26 | .packages 27 | .pub-cache/ 28 | .pub/ 29 | build/ 30 | 31 | # Android related 32 | **/android/**/gradle-wrapper.jar 33 | **/android/.gradle 34 | **/android/captures/ 35 | **/android/gradlew 36 | **/android/gradlew.bat 37 | **/android/local.properties 38 | **/android/**/GeneratedPluginRegistrant.java 39 | 40 | # iOS/XCode related 41 | **/ios/**/*.mode1v3 42 | **/ios/**/*.mode2v3 43 | **/ios/**/*.moved-aside 44 | **/ios/**/*.pbxuser 45 | **/ios/**/*.perspectivev3 46 | **/ios/**/*sync/ 47 | **/ios/**/.sconsign.dblite 48 | **/ios/**/.tags* 49 | **/ios/**/.vagrant/ 50 | **/ios/**/DerivedData/ 51 | **/ios/**/Icon? 52 | **/ios/**/Pods/ 53 | **/ios/**/.symlinks/ 54 | **/ios/**/profile 55 | **/ios/**/xcuserdata 56 | **/ios/.generated/ 57 | **/ios/Flutter/App.framework 58 | **/ios/Flutter/Flutter.framework 59 | **/ios/Flutter/Generated.xcconfig 60 | **/ios/Flutter/app.flx 61 | **/ios/Flutter/app.zip 62 | **/ios/Flutter/flutter_assets/ 63 | **/ios/ServiceDefinitions.json 64 | **/ios/Runner/GeneratedPluginRegistrant.* 65 | 66 | # Exceptions to above rules. 67 | !**/ios/**/default.mode1v3 68 | !**/ios/**/default.mode2v3 69 | !**/ios/**/default.pbxuser 70 | !**/ios/**/default.perspectivev3 71 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 72 | -------------------------------------------------------------------------------- /example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 06e4ccc9a978ec236f99a43680a067a4da85cff2 8 | channel: master 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 Matej Knopp. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following 11 | disclaimer in the documentation and/or other materials provided 12 | with the distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived 15 | from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Reorderable List in Flutter 2 | 3 | iOS-like proof of concept reorderable list with animations 4 | 5 | ## Preview 6 | 7 | 8 | 9 | ## Getting Started 10 | 11 | See `lib/main.dart` for example usage 12 | -------------------------------------------------------------------------------- /example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | 28 | # Additional information about this file can be found at 29 | # https://dart.dev/guides/language/analysis-options 30 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 29 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | 35 | lintOptions { 36 | disable 'InvalidPackage' 37 | } 38 | 39 | defaultConfig { 40 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 41 | applicationId "com.example.example" 42 | minSdkVersion 16 43 | targetSdkVersion 29 44 | versionCode flutterVersionCode.toInteger() 45 | versionName flutterVersionName 46 | } 47 | 48 | buildTypes { 49 | release { 50 | // TODO: Add your own signing config for the release build. 51 | // Signing with the debug keys for now, so `flutter run --release` works. 52 | signingConfig signingConfigs.debug 53 | } 54 | } 55 | } 56 | 57 | flutter { 58 | source '../..' 59 | } 60 | 61 | dependencies { 62 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 63 | } 64 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 12 | 19 | 23 | 27 | 32 | 36 | 37 | 38 | 39 | 40 | 41 | 43 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/com/example/example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.example 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knopp/flutter_reorderable_list/07197525bf9833071e08debf6c5675c5aa71a26b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knopp/flutter_reorderable_list/07197525bf9833071e08debf6c5675c5aa71a26b/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knopp/flutter_reorderable_list/07197525bf9833071e08debf6c5675c5aa71a26b/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knopp/flutter_reorderable_list/07197525bf9833071e08debf6c5675c5aa71a26b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knopp/flutter_reorderable_list/07197525bf9833071e08debf6c5675c5aa71a26b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.5.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | jcenter() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | android.enableR8=true 5 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip 7 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /example/ios/Flutter/.last_build_id: -------------------------------------------------------------------------------- 1 | 2e78c5207977ff4395facf701b7198ae -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/ios/Flutter/Flutter.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # NOTE: This podspec is NOT to be published. It is only used as a local source! 3 | # 4 | 5 | Pod::Spec.new do |s| 6 | s.name = 'Flutter' 7 | s.version = '1.0.0' 8 | s.summary = 'High-performance, high-fidelity mobile apps.' 9 | s.description = <<-DESC 10 | Flutter provides an easy and productive way to build and deploy high-performance mobile apps for Android and iOS. 11 | DESC 12 | s.homepage = 'https://flutter.io' 13 | s.license = { :type => 'MIT' } 14 | s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } 15 | s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s } 16 | s.ios.deployment_target = '8.0' 17 | s.vendored_frameworks = 'Flutter.framework' 18 | end 19 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; 13 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 14 | 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 15 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 16 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 17 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXCopyFilesBuildPhase section */ 21 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 22 | isa = PBXCopyFilesBuildPhase; 23 | buildActionMask = 2147483647; 24 | dstPath = ""; 25 | dstSubfolderSpec = 10; 26 | files = ( 27 | ); 28 | name = "Embed Frameworks"; 29 | runOnlyForDeploymentPostprocessing = 0; 30 | }; 31 | /* End PBXCopyFilesBuildPhase section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 35 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 36 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 37 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 38 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 39 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 40 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 41 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 42 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 43 | 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 44 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 45 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 46 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 47 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 48 | /* End PBXFileReference section */ 49 | 50 | /* Begin PBXFrameworksBuildPhase section */ 51 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 52 | isa = PBXFrameworksBuildPhase; 53 | buildActionMask = 2147483647; 54 | files = ( 55 | ); 56 | runOnlyForDeploymentPostprocessing = 0; 57 | }; 58 | /* End PBXFrameworksBuildPhase section */ 59 | 60 | /* Begin PBXGroup section */ 61 | 9740EEB11CF90186004384FC /* Flutter */ = { 62 | isa = PBXGroup; 63 | children = ( 64 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 65 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 66 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 67 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 68 | ); 69 | name = Flutter; 70 | sourceTree = ""; 71 | }; 72 | 97C146E51CF9000F007C117D = { 73 | isa = PBXGroup; 74 | children = ( 75 | 9740EEB11CF90186004384FC /* Flutter */, 76 | 97C146F01CF9000F007C117D /* Runner */, 77 | 97C146EF1CF9000F007C117D /* Products */, 78 | ); 79 | sourceTree = ""; 80 | }; 81 | 97C146EF1CF9000F007C117D /* Products */ = { 82 | isa = PBXGroup; 83 | children = ( 84 | 97C146EE1CF9000F007C117D /* Runner.app */, 85 | ); 86 | name = Products; 87 | sourceTree = ""; 88 | }; 89 | 97C146F01CF9000F007C117D /* Runner */ = { 90 | isa = PBXGroup; 91 | children = ( 92 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, 93 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, 94 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 95 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 96 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 97 | 97C147021CF9000F007C117D /* Info.plist */, 98 | 97C146F11CF9000F007C117D /* Supporting Files */, 99 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 100 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 101 | ); 102 | path = Runner; 103 | sourceTree = ""; 104 | }; 105 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 97C146F21CF9000F007C117D /* main.m */, 109 | ); 110 | name = "Supporting Files"; 111 | sourceTree = ""; 112 | }; 113 | /* End PBXGroup section */ 114 | 115 | /* Begin PBXNativeTarget section */ 116 | 97C146ED1CF9000F007C117D /* Runner */ = { 117 | isa = PBXNativeTarget; 118 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 119 | buildPhases = ( 120 | 9740EEB61CF901F6004384FC /* Run Script */, 121 | 97C146EA1CF9000F007C117D /* Sources */, 122 | 97C146EB1CF9000F007C117D /* Frameworks */, 123 | 97C146EC1CF9000F007C117D /* Resources */, 124 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 125 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 126 | ); 127 | buildRules = ( 128 | ); 129 | dependencies = ( 130 | ); 131 | name = Runner; 132 | productName = Runner; 133 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 134 | productType = "com.apple.product-type.application"; 135 | }; 136 | /* End PBXNativeTarget section */ 137 | 138 | /* Begin PBXProject section */ 139 | 97C146E61CF9000F007C117D /* Project object */ = { 140 | isa = PBXProject; 141 | attributes = { 142 | LastUpgradeCheck = 0910; 143 | ORGANIZATIONNAME = "The Chromium Authors"; 144 | TargetAttributes = { 145 | 97C146ED1CF9000F007C117D = { 146 | CreatedOnToolsVersion = 7.3.1; 147 | DevelopmentTeam = DL9X48QE4H; 148 | }; 149 | }; 150 | }; 151 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 152 | compatibilityVersion = "Xcode 3.2"; 153 | developmentRegion = English; 154 | hasScannedForEncodings = 0; 155 | knownRegions = ( 156 | English, 157 | en, 158 | Base, 159 | ); 160 | mainGroup = 97C146E51CF9000F007C117D; 161 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 162 | projectDirPath = ""; 163 | projectRoot = ""; 164 | targets = ( 165 | 97C146ED1CF9000F007C117D /* Runner */, 166 | ); 167 | }; 168 | /* End PBXProject section */ 169 | 170 | /* Begin PBXResourcesBuildPhase section */ 171 | 97C146EC1CF9000F007C117D /* Resources */ = { 172 | isa = PBXResourcesBuildPhase; 173 | buildActionMask = 2147483647; 174 | files = ( 175 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 176 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 177 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, 178 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 179 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 180 | ); 181 | runOnlyForDeploymentPostprocessing = 0; 182 | }; 183 | /* End PBXResourcesBuildPhase section */ 184 | 185 | /* Begin PBXShellScriptBuildPhase section */ 186 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 187 | isa = PBXShellScriptBuildPhase; 188 | buildActionMask = 2147483647; 189 | files = ( 190 | ); 191 | inputPaths = ( 192 | ); 193 | name = "Thin Binary"; 194 | outputPaths = ( 195 | ); 196 | runOnlyForDeploymentPostprocessing = 0; 197 | shellPath = /bin/sh; 198 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 199 | }; 200 | 9740EEB61CF901F6004384FC /* Run Script */ = { 201 | isa = PBXShellScriptBuildPhase; 202 | buildActionMask = 2147483647; 203 | files = ( 204 | ); 205 | inputPaths = ( 206 | ); 207 | name = "Run Script"; 208 | outputPaths = ( 209 | ); 210 | runOnlyForDeploymentPostprocessing = 0; 211 | shellPath = /bin/sh; 212 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 213 | }; 214 | /* End PBXShellScriptBuildPhase section */ 215 | 216 | /* Begin PBXSourcesBuildPhase section */ 217 | 97C146EA1CF9000F007C117D /* Sources */ = { 218 | isa = PBXSourcesBuildPhase; 219 | buildActionMask = 2147483647; 220 | files = ( 221 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, 222 | 97C146F31CF9000F007C117D /* main.m in Sources */, 223 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 224 | ); 225 | runOnlyForDeploymentPostprocessing = 0; 226 | }; 227 | /* End PBXSourcesBuildPhase section */ 228 | 229 | /* Begin PBXVariantGroup section */ 230 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 231 | isa = PBXVariantGroup; 232 | children = ( 233 | 97C146FB1CF9000F007C117D /* Base */, 234 | ); 235 | name = Main.storyboard; 236 | sourceTree = ""; 237 | }; 238 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 239 | isa = PBXVariantGroup; 240 | children = ( 241 | 97C147001CF9000F007C117D /* Base */, 242 | ); 243 | name = LaunchScreen.storyboard; 244 | sourceTree = ""; 245 | }; 246 | /* End PBXVariantGroup section */ 247 | 248 | /* Begin XCBuildConfiguration section */ 249 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 250 | isa = XCBuildConfiguration; 251 | buildSettings = { 252 | ALWAYS_SEARCH_USER_PATHS = NO; 253 | CLANG_ANALYZER_NONNULL = YES; 254 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 255 | CLANG_CXX_LIBRARY = "libc++"; 256 | CLANG_ENABLE_MODULES = YES; 257 | CLANG_ENABLE_OBJC_ARC = YES; 258 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 259 | CLANG_WARN_BOOL_CONVERSION = YES; 260 | CLANG_WARN_COMMA = YES; 261 | CLANG_WARN_CONSTANT_CONVERSION = YES; 262 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 263 | CLANG_WARN_EMPTY_BODY = YES; 264 | CLANG_WARN_ENUM_CONVERSION = YES; 265 | CLANG_WARN_INFINITE_RECURSION = YES; 266 | CLANG_WARN_INT_CONVERSION = YES; 267 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 268 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 269 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 270 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 271 | CLANG_WARN_STRICT_PROTOTYPES = YES; 272 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 273 | CLANG_WARN_UNREACHABLE_CODE = YES; 274 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 275 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 276 | COPY_PHASE_STRIP = NO; 277 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 278 | ENABLE_NS_ASSERTIONS = NO; 279 | ENABLE_STRICT_OBJC_MSGSEND = YES; 280 | GCC_C_LANGUAGE_STANDARD = gnu99; 281 | GCC_NO_COMMON_BLOCKS = YES; 282 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 283 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 284 | GCC_WARN_UNDECLARED_SELECTOR = YES; 285 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 286 | GCC_WARN_UNUSED_FUNCTION = YES; 287 | GCC_WARN_UNUSED_VARIABLE = YES; 288 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 289 | MTL_ENABLE_DEBUG_INFO = NO; 290 | SDKROOT = iphoneos; 291 | TARGETED_DEVICE_FAMILY = "1,2"; 292 | VALIDATE_PRODUCT = YES; 293 | }; 294 | name = Profile; 295 | }; 296 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 297 | isa = XCBuildConfiguration; 298 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 299 | buildSettings = { 300 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 301 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 302 | DEVELOPMENT_TEAM = DL9X48QE4H; 303 | ENABLE_BITCODE = NO; 304 | FRAMEWORK_SEARCH_PATHS = ( 305 | "$(inherited)", 306 | "$(PROJECT_DIR)/Flutter", 307 | ); 308 | INFOPLIST_FILE = Runner/Info.plist; 309 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 310 | LIBRARY_SEARCH_PATHS = ( 311 | "$(inherited)", 312 | "$(PROJECT_DIR)/Flutter", 313 | ); 314 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example; 315 | PRODUCT_NAME = "$(TARGET_NAME)"; 316 | VERSIONING_SYSTEM = "apple-generic"; 317 | }; 318 | name = Profile; 319 | }; 320 | 97C147031CF9000F007C117D /* Debug */ = { 321 | isa = XCBuildConfiguration; 322 | buildSettings = { 323 | ALWAYS_SEARCH_USER_PATHS = NO; 324 | CLANG_ANALYZER_NONNULL = YES; 325 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 326 | CLANG_CXX_LIBRARY = "libc++"; 327 | CLANG_ENABLE_MODULES = YES; 328 | CLANG_ENABLE_OBJC_ARC = YES; 329 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 330 | CLANG_WARN_BOOL_CONVERSION = YES; 331 | CLANG_WARN_COMMA = YES; 332 | CLANG_WARN_CONSTANT_CONVERSION = YES; 333 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 334 | CLANG_WARN_EMPTY_BODY = YES; 335 | CLANG_WARN_ENUM_CONVERSION = YES; 336 | CLANG_WARN_INFINITE_RECURSION = YES; 337 | CLANG_WARN_INT_CONVERSION = YES; 338 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 339 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 340 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 341 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 342 | CLANG_WARN_STRICT_PROTOTYPES = YES; 343 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 344 | CLANG_WARN_UNREACHABLE_CODE = YES; 345 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 346 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 347 | COPY_PHASE_STRIP = NO; 348 | DEBUG_INFORMATION_FORMAT = dwarf; 349 | ENABLE_STRICT_OBJC_MSGSEND = YES; 350 | ENABLE_TESTABILITY = YES; 351 | GCC_C_LANGUAGE_STANDARD = gnu99; 352 | GCC_DYNAMIC_NO_PIC = NO; 353 | GCC_NO_COMMON_BLOCKS = YES; 354 | GCC_OPTIMIZATION_LEVEL = 0; 355 | GCC_PREPROCESSOR_DEFINITIONS = ( 356 | "DEBUG=1", 357 | "$(inherited)", 358 | ); 359 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 360 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 361 | GCC_WARN_UNDECLARED_SELECTOR = YES; 362 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 363 | GCC_WARN_UNUSED_FUNCTION = YES; 364 | GCC_WARN_UNUSED_VARIABLE = YES; 365 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 366 | MTL_ENABLE_DEBUG_INFO = YES; 367 | ONLY_ACTIVE_ARCH = YES; 368 | SDKROOT = iphoneos; 369 | TARGETED_DEVICE_FAMILY = "1,2"; 370 | }; 371 | name = Debug; 372 | }; 373 | 97C147041CF9000F007C117D /* Release */ = { 374 | isa = XCBuildConfiguration; 375 | buildSettings = { 376 | ALWAYS_SEARCH_USER_PATHS = NO; 377 | CLANG_ANALYZER_NONNULL = YES; 378 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 379 | CLANG_CXX_LIBRARY = "libc++"; 380 | CLANG_ENABLE_MODULES = YES; 381 | CLANG_ENABLE_OBJC_ARC = YES; 382 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 383 | CLANG_WARN_BOOL_CONVERSION = YES; 384 | CLANG_WARN_COMMA = YES; 385 | CLANG_WARN_CONSTANT_CONVERSION = YES; 386 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 387 | CLANG_WARN_EMPTY_BODY = YES; 388 | CLANG_WARN_ENUM_CONVERSION = YES; 389 | CLANG_WARN_INFINITE_RECURSION = YES; 390 | CLANG_WARN_INT_CONVERSION = YES; 391 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 392 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 393 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 394 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 395 | CLANG_WARN_STRICT_PROTOTYPES = YES; 396 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 397 | CLANG_WARN_UNREACHABLE_CODE = YES; 398 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 399 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 400 | COPY_PHASE_STRIP = NO; 401 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 402 | ENABLE_NS_ASSERTIONS = NO; 403 | ENABLE_STRICT_OBJC_MSGSEND = YES; 404 | GCC_C_LANGUAGE_STANDARD = gnu99; 405 | GCC_NO_COMMON_BLOCKS = YES; 406 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 407 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 408 | GCC_WARN_UNDECLARED_SELECTOR = YES; 409 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 410 | GCC_WARN_UNUSED_FUNCTION = YES; 411 | GCC_WARN_UNUSED_VARIABLE = YES; 412 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 413 | MTL_ENABLE_DEBUG_INFO = NO; 414 | SDKROOT = iphoneos; 415 | TARGETED_DEVICE_FAMILY = "1,2"; 416 | VALIDATE_PRODUCT = YES; 417 | }; 418 | name = Release; 419 | }; 420 | 97C147061CF9000F007C117D /* Debug */ = { 421 | isa = XCBuildConfiguration; 422 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 423 | buildSettings = { 424 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 425 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 426 | DEVELOPMENT_TEAM = DL9X48QE4H; 427 | ENABLE_BITCODE = NO; 428 | FRAMEWORK_SEARCH_PATHS = ( 429 | "$(inherited)", 430 | "$(PROJECT_DIR)/Flutter", 431 | ); 432 | INFOPLIST_FILE = Runner/Info.plist; 433 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 434 | LIBRARY_SEARCH_PATHS = ( 435 | "$(inherited)", 436 | "$(PROJECT_DIR)/Flutter", 437 | ); 438 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example; 439 | PRODUCT_NAME = "$(TARGET_NAME)"; 440 | VERSIONING_SYSTEM = "apple-generic"; 441 | }; 442 | name = Debug; 443 | }; 444 | 97C147071CF9000F007C117D /* Release */ = { 445 | isa = XCBuildConfiguration; 446 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 447 | buildSettings = { 448 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 449 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 450 | DEVELOPMENT_TEAM = DL9X48QE4H; 451 | ENABLE_BITCODE = NO; 452 | FRAMEWORK_SEARCH_PATHS = ( 453 | "$(inherited)", 454 | "$(PROJECT_DIR)/Flutter", 455 | ); 456 | INFOPLIST_FILE = Runner/Info.plist; 457 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 458 | LIBRARY_SEARCH_PATHS = ( 459 | "$(inherited)", 460 | "$(PROJECT_DIR)/Flutter", 461 | ); 462 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example; 463 | PRODUCT_NAME = "$(TARGET_NAME)"; 464 | VERSIONING_SYSTEM = "apple-generic"; 465 | }; 466 | name = Release; 467 | }; 468 | /* End XCBuildConfiguration section */ 469 | 470 | /* Begin XCConfigurationList section */ 471 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 472 | isa = XCConfigurationList; 473 | buildConfigurations = ( 474 | 97C147031CF9000F007C117D /* Debug */, 475 | 97C147041CF9000F007C117D /* Release */, 476 | 249021D3217E4FDB00AE95B9 /* Profile */, 477 | ); 478 | defaultConfigurationIsVisible = 0; 479 | defaultConfigurationName = Release; 480 | }; 481 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 482 | isa = XCConfigurationList; 483 | buildConfigurations = ( 484 | 97C147061CF9000F007C117D /* Debug */, 485 | 97C147071CF9000F007C117D /* Release */, 486 | 249021D4217E4FDB00AE95B9 /* Profile */, 487 | ); 488 | defaultConfigurationIsVisible = 0; 489 | defaultConfigurationName = Release; 490 | }; 491 | /* End XCConfigurationList section */ 492 | }; 493 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 494 | } 495 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 75 | 77 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : FlutterAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #include "AppDelegate.h" 2 | #include "GeneratedPluginRegistrant.h" 3 | 4 | @implementation AppDelegate 5 | 6 | - (BOOL)application:(UIApplication *)application 7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 8 | [GeneratedPluginRegistrant registerWithRegistry:self]; 9 | // Override point for customization after application launch. 10 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 11 | } 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knopp/flutter_reorderable_list/07197525bf9833071e08debf6c5675c5aa71a26b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knopp/flutter_reorderable_list/07197525bf9833071e08debf6c5675c5aa71a26b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knopp/flutter_reorderable_list/07197525bf9833071e08debf6c5675c5aa71a26b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knopp/flutter_reorderable_list/07197525bf9833071e08debf6c5675c5aa71a26b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knopp/flutter_reorderable_list/07197525bf9833071e08debf6c5675c5aa71a26b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knopp/flutter_reorderable_list/07197525bf9833071e08debf6c5675c5aa71a26b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knopp/flutter_reorderable_list/07197525bf9833071e08debf6c5675c5aa71a26b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knopp/flutter_reorderable_list/07197525bf9833071e08debf6c5675c5aa71a26b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knopp/flutter_reorderable_list/07197525bf9833071e08debf6c5675c5aa71a26b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knopp/flutter_reorderable_list/07197525bf9833071e08debf6c5675c5aa71a26b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knopp/flutter_reorderable_list/07197525bf9833071e08debf6c5675c5aa71a26b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knopp/flutter_reorderable_list/07197525bf9833071e08debf6c5675c5aa71a26b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knopp/flutter_reorderable_list/07197525bf9833071e08debf6c5675c5aa71a26b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knopp/flutter_reorderable_list/07197525bf9833071e08debf6c5675c5aa71a26b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knopp/flutter_reorderable_list/07197525bf9833071e08debf6c5675c5aa71a26b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knopp/flutter_reorderable_list/07197525bf9833071e08debf6c5675c5aa71a26b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knopp/flutter_reorderable_list/07197525bf9833071e08debf6c5675c5aa71a26b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knopp/flutter_reorderable_list/07197525bf9833071e08debf6c5675c5aa71a26b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | 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. -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | example 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /example/ios/Runner/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char* argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart' hide ReorderableList; 2 | import 'package:flutter_reorderable_list/flutter_reorderable_list.dart'; 3 | 4 | void main() => runApp(const MyApp()); 5 | 6 | class MyApp extends StatelessWidget { 7 | const MyApp({Key? key}) : super(key: key); 8 | 9 | // This widget is the root of your application. 10 | @override 11 | Widget build(BuildContext context) { 12 | return MaterialApp( 13 | title: 'Flutter Rerderable List', 14 | theme: ThemeData( 15 | dividerColor: const Color(0x50000000), 16 | primarySwatch: Colors.blue, 17 | ), 18 | home: const MyHomePage(title: 'Flutter Reorderable List'), 19 | ); 20 | } 21 | } 22 | 23 | class MyHomePage extends StatefulWidget { 24 | const MyHomePage({Key? key, required this.title}) : super(key: key); 25 | 26 | final String title; 27 | 28 | @override 29 | _MyHomePageState createState() => _MyHomePageState(); 30 | } 31 | 32 | class ItemData { 33 | ItemData(this.title, this.key); 34 | 35 | final String title; 36 | 37 | // Each item in reorderable list needs stable and unique key 38 | final Key key; 39 | } 40 | 41 | enum DraggingMode { 42 | iOS, 43 | android, 44 | } 45 | 46 | class _MyHomePageState extends State { 47 | late List _items; 48 | _MyHomePageState() { 49 | _items = []; 50 | for (int i = 0; i < 500; ++i) { 51 | String label = "List item $i"; 52 | if (i == 5) { 53 | label += ". This item has a long label and will be wrapped."; 54 | } 55 | _items.add(ItemData(label, ValueKey(i))); 56 | } 57 | } 58 | 59 | // Returns index of item with given key 60 | int _indexOfKey(Key key) { 61 | return _items.indexWhere((ItemData d) => d.key == key); 62 | } 63 | 64 | bool _reorderCallback(Key item, Key newPosition) { 65 | int draggingIndex = _indexOfKey(item); 66 | int newPositionIndex = _indexOfKey(newPosition); 67 | 68 | // Uncomment to allow only even target reorder possition 69 | // if (newPositionIndex % 2 == 1) 70 | // return false; 71 | 72 | final draggedItem = _items[draggingIndex]; 73 | setState(() { 74 | debugPrint("Reordering $item -> $newPosition"); 75 | _items.removeAt(draggingIndex); 76 | _items.insert(newPositionIndex, draggedItem); 77 | }); 78 | return true; 79 | } 80 | 81 | void _reorderDone(Key item) { 82 | final draggedItem = _items[_indexOfKey(item)]; 83 | debugPrint("Reordering finished for ${draggedItem.title}}"); 84 | } 85 | 86 | // 87 | // Reordering works by having ReorderableList widget in hierarchy 88 | // containing ReorderableItems widgets 89 | // 90 | 91 | DraggingMode _draggingMode = DraggingMode.iOS; 92 | 93 | @override 94 | Widget build(BuildContext context) { 95 | return Scaffold( 96 | body: ReorderableList( 97 | onReorder: _reorderCallback, 98 | onReorderDone: _reorderDone, 99 | child: CustomScrollView( 100 | // cacheExtent: 3000, 101 | slivers: [ 102 | SliverAppBar( 103 | actions: [ 104 | PopupMenuButton( 105 | child: Container( 106 | alignment: Alignment.center, 107 | padding: const EdgeInsets.symmetric(horizontal: 30.0), 108 | child: const Text("Options"), 109 | ), 110 | initialValue: _draggingMode, 111 | onSelected: (DraggingMode mode) { 112 | setState(() { 113 | _draggingMode = mode; 114 | }); 115 | }, 116 | itemBuilder: (BuildContext context) => 117 | >[ 118 | const PopupMenuItem( 119 | value: DraggingMode.iOS, 120 | child: Text('iOS-like dragging')), 121 | const PopupMenuItem( 122 | value: DraggingMode.android, 123 | child: Text('Android-like dragging')), 124 | ], 125 | ), 126 | ], 127 | pinned: true, 128 | expandedHeight: 150.0, 129 | flexibleSpace: const FlexibleSpaceBar( 130 | title: Text('Demo'), 131 | ), 132 | ), 133 | SliverPadding( 134 | padding: EdgeInsets.only( 135 | bottom: MediaQuery.of(context).padding.bottom), 136 | sliver: SliverList( 137 | delegate: SliverChildBuilderDelegate( 138 | (BuildContext context, int index) { 139 | return Item( 140 | data: _items[index], 141 | // first and last attributes affect border drawn during dragging 142 | isFirst: index == 0, 143 | isLast: index == _items.length - 1, 144 | draggingMode: _draggingMode, 145 | ); 146 | }, 147 | childCount: _items.length, 148 | ), 149 | )), 150 | ], 151 | ), 152 | ), 153 | ); 154 | } 155 | } 156 | 157 | class Item extends StatelessWidget { 158 | const Item({ 159 | Key? key, 160 | required this.data, 161 | required this.isFirst, 162 | required this.isLast, 163 | required this.draggingMode, 164 | }) : super(key: key); 165 | 166 | final ItemData data; 167 | final bool isFirst; 168 | final bool isLast; 169 | final DraggingMode draggingMode; 170 | 171 | Widget _buildChild(BuildContext context, ReorderableItemState state) { 172 | BoxDecoration decoration; 173 | 174 | if (state == ReorderableItemState.dragProxy || 175 | state == ReorderableItemState.dragProxyFinished) { 176 | // slightly transparent background white dragging (just like on iOS) 177 | decoration = const BoxDecoration(color: Color(0xD0FFFFFF)); 178 | } else { 179 | bool placeholder = state == ReorderableItemState.placeholder; 180 | decoration = BoxDecoration( 181 | border: Border( 182 | top: isFirst && !placeholder 183 | ? Divider.createBorderSide(context) // 184 | : BorderSide.none, 185 | bottom: isLast && placeholder 186 | ? BorderSide.none // 187 | : Divider.createBorderSide(context)), 188 | color: placeholder ? null : Colors.white); 189 | } 190 | 191 | // For iOS dragging mode, there will be drag handle on the right that triggers 192 | // reordering; For android mode it will be just an empty container 193 | Widget dragHandle = draggingMode == DraggingMode.iOS 194 | ? ReorderableListener( 195 | child: Container( 196 | padding: const EdgeInsets.only(right: 18.0, left: 18.0), 197 | color: const Color(0x08000000), 198 | child: const Center( 199 | child: Icon(Icons.reorder, color: Color(0xFF888888)), 200 | ), 201 | ), 202 | ) 203 | : Container(); 204 | 205 | Widget content = Container( 206 | decoration: decoration, 207 | child: SafeArea( 208 | top: false, 209 | bottom: false, 210 | child: Opacity( 211 | // hide content for placeholder 212 | opacity: state == ReorderableItemState.placeholder ? 0.0 : 1.0, 213 | child: IntrinsicHeight( 214 | child: Row( 215 | crossAxisAlignment: CrossAxisAlignment.stretch, 216 | children: [ 217 | Expanded( 218 | child: Padding( 219 | padding: const EdgeInsets.symmetric( 220 | vertical: 14.0, horizontal: 14.0), 221 | child: Text(data.title, 222 | style: Theme.of(context).textTheme.titleMedium), 223 | )), 224 | // Triggers the reordering 225 | dragHandle, 226 | ], 227 | ), 228 | ), 229 | )), 230 | ); 231 | 232 | // For android dragging mode, wrap the entire content in DelayedReorderableListener 233 | if (draggingMode == DraggingMode.android) { 234 | content = DelayedReorderableListener( 235 | child: content, 236 | ); 237 | } 238 | 239 | return content; 240 | } 241 | 242 | @override 243 | Widget build(BuildContext context) { 244 | return ReorderableItem( 245 | key: data.key, // 246 | childBuilder: _buildChild); 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: reorderable_list 2 | description: Reorderable list view example 3 | homepage: https://github.com/knopp/flutter_reorderable_list 4 | publish_to: none 5 | 6 | version: 1.0.0+1 7 | 8 | environment: 9 | sdk: ">=2.12.0 <3.0.0" 10 | 11 | dependencies: 12 | flutter: 13 | sdk: flutter 14 | flutter_lints: ^1.0.4 15 | 16 | flutter_reorderable_list: 17 | path: ../ 18 | 19 | # The following adds the Cupertino Icons font to your application. 20 | # Use with the CupertinoIcons class for iOS style icons. 21 | cupertino_icons: ^1.0.5 22 | 23 | dev_dependencies: 24 | flutter_test: 25 | sdk: flutter 26 | 27 | # The following section is specific to Flutter. 28 | flutter: 29 | uses-material-design: true 30 | -------------------------------------------------------------------------------- /example/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | void main() { 2 | } 3 | -------------------------------------------------------------------------------- /example/web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knopp/flutter_reorderable_list/07197525bf9833071e08debf6c5675c5aa71a26b/example/web/favicon.png -------------------------------------------------------------------------------- /example/web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knopp/flutter_reorderable_list/07197525bf9833071e08debf6c5675c5aa71a26b/example/web/icons/Icon-192.png -------------------------------------------------------------------------------- /example/web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knopp/flutter_reorderable_list/07197525bf9833071e08debf6c5675c5aa71a26b/example/web/icons/Icon-512.png -------------------------------------------------------------------------------- /example/web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knopp/flutter_reorderable_list/07197525bf9833071e08debf6c5675c5aa71a26b/example/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /example/web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knopp/flutter_reorderable_list/07197525bf9833071e08debf6c5675c5aa71a26b/example/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /example/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | example 30 | 31 | 32 | 33 | 36 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /example/web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "short_name": "example", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /lib/flutter_reorderable_list.dart: -------------------------------------------------------------------------------- 1 | library flutter_reorderable_list; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/gestures.dart'; 5 | import 'package:flutter/scheduler.dart'; 6 | import 'package:flutter/services.dart'; 7 | 8 | import 'dart:collection'; 9 | import 'dart:math'; 10 | import 'dart:async'; 11 | import 'dart:ui' show lerpDouble; 12 | 13 | typedef bool ReorderItemCallback(Key draggedItem, Key newPosition); 14 | typedef void ReorderCompleteCallback(Key draggedItem); 15 | 16 | // Represents placeholder for currently dragged row including decorations 17 | // (i.e. before and after shadow) 18 | class DecoratedPlaceholder { 19 | DecoratedPlaceholder({ 20 | required this.offset, 21 | required this.widget, 22 | }); 23 | 24 | // Height of decoration before widget 25 | final double offset; 26 | final Widget widget; 27 | } 28 | 29 | // Decorates current placeholder widget 30 | typedef DecoratedPlaceholder DecoratePlaceholder( 31 | Widget widget, 32 | double decorationOpacity, 33 | ); 34 | 35 | // Can be used to cancel reordering (i.e. when underlying data changed) 36 | class CancellationToken { 37 | void cancelDragging() { 38 | for (final callback in _callbacks) { 39 | callback(); 40 | } 41 | } 42 | 43 | final _callbacks = []; 44 | } 45 | 46 | class ReorderableList extends StatefulWidget { 47 | ReorderableList({ 48 | Key? key, 49 | required this.child, 50 | required this.onReorder, 51 | this.onReorderDone, 52 | this.cancellationToken, 53 | this.decoratePlaceholder = _defaultDecoratePlaceholder, 54 | }) : super(key: key); 55 | 56 | final Widget child; 57 | 58 | final ReorderItemCallback onReorder; 59 | final ReorderCompleteCallback? onReorderDone; 60 | final DecoratePlaceholder decoratePlaceholder; 61 | 62 | final CancellationToken? cancellationToken; 63 | 64 | @override 65 | State createState() => _ReorderableListState(); 66 | } 67 | 68 | enum ReorderableItemState { 69 | /// Normal item inside list 70 | normal, 71 | 72 | /// Placeholder, used at position of currently dragged item; 73 | /// Should have same dimensions as [normal] but hidden content 74 | placeholder, 75 | 76 | // Proxy item displayed during dragging 77 | dragProxy, 78 | 79 | // Proxy item displayed during finishing animation 80 | dragProxyFinished 81 | } 82 | 83 | typedef Widget ReorderableItemChildBuilder( 84 | BuildContext context, 85 | ReorderableItemState state, 86 | ); 87 | 88 | class ReorderableItem extends StatefulWidget { 89 | /// [key] must be unique key for every item. It must be stable and not change 90 | /// when items are reordered 91 | ReorderableItem({ 92 | required Key key, 93 | required this.childBuilder, 94 | }) : super(key: key); 95 | 96 | final ReorderableItemChildBuilder childBuilder; 97 | 98 | @override 99 | createState() => _ReorderableItemState(); 100 | } 101 | 102 | typedef ReorderableListenerCallback = bool Function(); 103 | 104 | class ReorderableListener extends StatelessWidget { 105 | ReorderableListener({ 106 | Key? key, 107 | this.child, 108 | this.canStart, 109 | }) : super(key: key); 110 | final Widget? child; 111 | 112 | final ReorderableListenerCallback? canStart; 113 | 114 | @override 115 | Widget build(BuildContext context) { 116 | return Listener( 117 | onPointerDown: (PointerEvent event) => _routePointer(event, context), 118 | child: child, 119 | ); 120 | } 121 | 122 | void _routePointer(PointerEvent event, BuildContext context) { 123 | if (canStart == null || canStart!()) { 124 | _startDragging(context: context, event: event); 125 | } 126 | } 127 | 128 | @protected 129 | MultiDragGestureRecognizer createRecognizer({ 130 | required Object? debugOwner, 131 | Set? supportedDevices, 132 | }) { 133 | return _Recognizer( 134 | debugOwner: debugOwner, 135 | supportedDevices: supportedDevices, 136 | ); 137 | } 138 | 139 | void _startDragging({required BuildContext context, PointerEvent? event}) { 140 | _ReorderableItemState? state = 141 | context.findAncestorStateOfType<_ReorderableItemState>(); 142 | 143 | final scrollable = Scrollable.of(context); 144 | 145 | final listState = _ReorderableListState.of(context)!; 146 | 147 | if (listState.dragging == null) { 148 | listState._startDragging( 149 | key: state!.key, 150 | event: event!, 151 | scrollable: scrollable, 152 | recognizer: createRecognizer( 153 | debugOwner: this, 154 | supportedDevices: { 155 | PointerDeviceKind.touch, 156 | PointerDeviceKind.mouse, 157 | }, 158 | ), 159 | ); 160 | } 161 | } 162 | } 163 | 164 | class DelayedReorderableListener extends ReorderableListener { 165 | DelayedReorderableListener({ 166 | Key? key, 167 | Widget? child, 168 | ReorderableListenerCallback? canStart, 169 | this.delay = kLongPressTimeout, 170 | }) : super(key: key, child: child, canStart: canStart); 171 | 172 | final Duration delay; 173 | 174 | @protected 175 | MultiDragGestureRecognizer createRecognizer({ 176 | required Object? debugOwner, 177 | Set? supportedDevices, 178 | }) { 179 | return DelayedMultiDragGestureRecognizer( 180 | delay: delay, 181 | debugOwner: debugOwner, 182 | supportedDevices: supportedDevices, 183 | ); 184 | } 185 | } 186 | 187 | class _ReorderableListState extends State 188 | with TickerProviderStateMixin 189 | implements Drag { 190 | @override 191 | Widget build(BuildContext context) { 192 | return Stack( 193 | fit: StackFit.passthrough, 194 | children: [widget.child, _DragProxy(widget.decoratePlaceholder)], 195 | ); 196 | } 197 | 198 | @override 199 | void initState() { 200 | super.initState(); 201 | if (widget.cancellationToken != null) { 202 | widget.cancellationToken!._callbacks.add(this._cancel); 203 | } 204 | } 205 | 206 | @override 207 | void dispose() { 208 | if (widget.cancellationToken != null) { 209 | widget.cancellationToken!._callbacks.remove(this._cancel); 210 | } 211 | _finalAnimation?.dispose(); 212 | for (final c in _itemTranslations.values) { 213 | c.dispose(); 214 | } 215 | _scrolling = false; 216 | _recognizer?.dispose(); 217 | super.dispose(); 218 | } 219 | 220 | void _cancel() { 221 | if (_dragging != null) { 222 | if (_finalAnimation != null) { 223 | _finalAnimation!.dispose(); 224 | _finalAnimation = null; 225 | } 226 | 227 | final dragging = _dragging!; 228 | _dragging = null; 229 | _dragProxy!.hide(); 230 | 231 | var current = _items[_dragging]; 232 | current?.update(); 233 | 234 | if (widget.onReorderDone != null) { 235 | widget.onReorderDone!(dragging); 236 | } 237 | } 238 | } 239 | 240 | // Returns currently dragged key 241 | Key? get dragging => _dragging; 242 | 243 | Key? _dragging; 244 | MultiDragGestureRecognizer? _recognizer; 245 | _DragProxyState? _dragProxy; 246 | 247 | void _startDragging({ 248 | Key? key, 249 | required PointerEvent event, 250 | MultiDragGestureRecognizer? recognizer, 251 | ScrollableState? scrollable, 252 | }) { 253 | _scrollable = scrollable; 254 | 255 | _finalAnimation?.stop(canceled: true); 256 | _finalAnimation?.dispose(); 257 | _finalAnimation = null; 258 | 259 | if (_dragging != null) { 260 | var current = _items[_dragging]; 261 | _dragging = null; 262 | current?.update(); 263 | } 264 | 265 | _maybeDragging = key; 266 | _lastReportedKey = null; 267 | _recognizer?.dispose(); 268 | _recognizer = recognizer; 269 | _recognizer!.onStart = _dragStart; 270 | _recognizer!.addPointer(event as PointerDownEvent); 271 | } 272 | 273 | Key? _maybeDragging; 274 | 275 | Drag _dragStart(Offset position) { 276 | if (_dragging == null && _maybeDragging != null) { 277 | _dragging = _maybeDragging; 278 | _maybeDragging = null; 279 | } 280 | _hapticFeedback(); 281 | final draggedItem = _items[_dragging]!; 282 | draggedItem.update(); 283 | _dragProxy!.setWidget( 284 | draggedItem.widget 285 | .childBuilder(draggedItem.context, ReorderableItemState.dragProxy), 286 | draggedItem.context.findRenderObject() as RenderBox); 287 | this._scrollable!.position.addListener(this._scrolled); 288 | 289 | return this; 290 | } 291 | 292 | void _draggedItemWidgetUpdated() { 293 | final draggedItem = _items[_dragging]; 294 | if (draggedItem != null) { 295 | _dragProxy!.updateWidget(draggedItem.widget 296 | .childBuilder(draggedItem.context, ReorderableItemState.dragProxy)); 297 | } 298 | } 299 | 300 | void _scrolled() { 301 | checkDragPosition(); 302 | } 303 | 304 | void update(DragUpdateDetails details) { 305 | _dragProxy!.offset += details.delta.dy; 306 | checkDragPosition(); 307 | maybeScroll(); 308 | } 309 | 310 | ScrollableState? _scrollable; 311 | 312 | void maybeScroll() async { 313 | if (!_scrolling && _scrollable != null && _dragging != null) { 314 | final position = _scrollable!.position; 315 | 316 | double newOffset; 317 | int duration = 14; // in ms 318 | double step = 1.0; 319 | double overdragMax = 20.0; 320 | double overdragCoef = 10.0; 321 | 322 | MediaQueryData d = MediaQuery.of(context); 323 | 324 | double top = d.padding.top; 325 | double bottom = 326 | this._scrollable!.position.viewportDimension - d.padding.bottom; 327 | 328 | if (_dragProxy!.offset < top && 329 | position.pixels > position.minScrollExtent) { 330 | final overdrag = max(top - _dragProxy!.offset, overdragMax); 331 | 332 | newOffset = max( 333 | position.minScrollExtent, 334 | position.pixels - step * overdrag / overdragCoef, 335 | ); 336 | } else if (_dragProxy!.offset + _dragProxy!.height > bottom && 337 | position.pixels < position.maxScrollExtent) { 338 | final overdrag = max( 339 | _dragProxy!.offset + _dragProxy!.height - bottom, overdragMax); 340 | newOffset = min(position.maxScrollExtent, 341 | position.pixels + step * overdrag / overdragCoef); 342 | } else { 343 | return; 344 | } 345 | 346 | if ((newOffset - position.pixels).abs() >= 1.0) { 347 | _scrolling = true; 348 | await this._scrollable!.position.animateTo(newOffset, 349 | duration: Duration(milliseconds: duration), curve: Curves.linear); 350 | _scrolling = false; 351 | if (_dragging != null) { 352 | checkDragPosition(); 353 | maybeScroll(); 354 | } 355 | } 356 | } 357 | } 358 | 359 | bool _scrolling = false; 360 | 361 | void cancel() { 362 | end(null); 363 | } 364 | 365 | end(DragEndDetails? details) async { 366 | if (_dragging == null) { 367 | return; 368 | } 369 | 370 | _hapticFeedback(); 371 | if (_scrolling) { 372 | var prevDragging = _dragging; 373 | _dragging = null; 374 | SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) { 375 | _dragging = prevDragging; 376 | end(details); 377 | }); 378 | return; 379 | } 380 | 381 | if (_scheduledRebuild) { 382 | SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) { 383 | if (mounted) end(details); 384 | }); 385 | return; 386 | } 387 | 388 | this._scrollable!.position.removeListener(this._scrolled); 389 | 390 | var current = _items[_dragging]; 391 | if (current == null) return; 392 | 393 | final originalOffset = _itemOffset(current); 394 | final dragProxyOffset = _dragProxy!.offset; 395 | 396 | _dragProxy!.updateWidget(current.widget 397 | .childBuilder(current.context, ReorderableItemState.dragProxyFinished)); 398 | 399 | _finalAnimation = AnimationController( 400 | vsync: this, 401 | lowerBound: 0.0, 402 | upperBound: 1.0, 403 | value: 0.0, 404 | duration: Duration(milliseconds: 300)); 405 | 406 | _finalAnimation!.addListener(() { 407 | _dragProxy!.offset = lerpDouble( 408 | dragProxyOffset, 409 | originalOffset, 410 | _finalAnimation!.value, 411 | )!; 412 | _dragProxy!.decorationOpacity = 1.0 - _finalAnimation!.value; 413 | }); 414 | 415 | _recognizer?.dispose(); 416 | _recognizer = null; 417 | 418 | await _finalAnimation!.animateTo(1.0, curve: Curves.easeOut); 419 | 420 | if (_finalAnimation != null) { 421 | _finalAnimation!.dispose(); 422 | _finalAnimation = null; 423 | 424 | final dragging = _dragging!; 425 | _dragging = null; 426 | _dragProxy!.hide(); 427 | current.update(); 428 | _scrollable = null; 429 | 430 | if (widget.onReorderDone != null) { 431 | widget.onReorderDone!(dragging); 432 | } 433 | } 434 | } 435 | 436 | void checkDragPosition() { 437 | if (_scheduledRebuild) { 438 | return; 439 | } 440 | final draggingState = _items[_dragging]; 441 | if (draggingState == null) { 442 | return; 443 | } 444 | 445 | final draggingTop = _itemOffset(draggingState); 446 | final draggingHeight = draggingState.context.size!.height; 447 | 448 | _ReorderableItemState? closest; 449 | double closestDistance = 0.0; 450 | 451 | // These callbacks will be invoked on successful reorder, they will ensure that 452 | // reordered items appear on their old position and animate to new one 453 | List onReorderApproved = []; 454 | 455 | if (_dragProxy!.offset < draggingTop) { 456 | for (_ReorderableItemState item in _items.values) { 457 | if (item.key == _dragging) continue; 458 | final itemTop = _itemOffset(item); 459 | if (itemTop > draggingTop) continue; 460 | final itemBottom = itemTop + 461 | (item.context.findRenderObject() as RenderBox).size.height / 2; 462 | 463 | if (_dragProxy!.offset < itemBottom) { 464 | onReorderApproved.add(() { 465 | _adjustItemTranslation(item.key, -draggingHeight, draggingHeight); 466 | }); 467 | if (closest == null || 468 | closestDistance > (itemBottom - _dragProxy!.offset)) { 469 | closest = item; 470 | closestDistance = (itemBottom - _dragProxy!.offset); 471 | } 472 | } 473 | } 474 | } else { 475 | double draggingBottom = _dragProxy!.offset + draggingHeight; 476 | 477 | for (_ReorderableItemState item in _items.values) { 478 | if (item.key == _dragging) continue; 479 | final itemTop = _itemOffset(item); 480 | if (itemTop < draggingTop) continue; 481 | 482 | final itemBottom = itemTop + 483 | (item.context.findRenderObject() as RenderBox).size.height / 2; 484 | if (draggingBottom > itemBottom) { 485 | onReorderApproved.add(() { 486 | _adjustItemTranslation(item.key, draggingHeight, draggingHeight); 487 | }); 488 | if (closest == null || 489 | closestDistance > (draggingBottom - itemBottom)) { 490 | closest = item; 491 | closestDistance = draggingBottom - itemBottom; 492 | } 493 | } 494 | } 495 | } 496 | 497 | // _lastReportedKey check is to ensure we don't keep spamming the callback when reorder 498 | // was rejected for this key; 499 | if (closest != null && 500 | closest.key != _dragging && 501 | closest.key != _lastReportedKey) { 502 | SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) { 503 | _scheduledRebuild = false; 504 | }); 505 | _scheduledRebuild = true; 506 | _lastReportedKey = closest.key; 507 | 508 | if (widget.onReorder(_dragging!, closest.key)) { 509 | bool isIOS = Theme.of(context).platform == TargetPlatform.iOS; 510 | 511 | if (isIOS) { 512 | _hapticFeedback(); 513 | } 514 | for (final f in onReorderApproved) { 515 | f(); 516 | } 517 | _lastReportedKey = null; 518 | } 519 | } 520 | } 521 | 522 | void _hapticFeedback() { 523 | HapticFeedback.lightImpact(); 524 | } 525 | 526 | bool _scheduledRebuild = false; 527 | Key? _lastReportedKey; 528 | 529 | final HashMap _items = 530 | HashMap(); 531 | 532 | void registerItem(_ReorderableItemState item) { 533 | _items[item.key] = item; 534 | } 535 | 536 | void unregisterItem(_ReorderableItemState item) { 537 | if (_items[item.key] == item) _items.remove(item.key); 538 | } 539 | 540 | double _itemOffset(_ReorderableItemState item) { 541 | final topRenderBox = context.findRenderObject() as RenderBox; 542 | return (item.context.findRenderObject() as RenderBox) 543 | .localToGlobal(Offset.zero, ancestor: topRenderBox) 544 | .dy; 545 | } 546 | 547 | static _ReorderableListState? of(BuildContext context) { 548 | return context.findAncestorStateOfType<_ReorderableListState>(); 549 | } 550 | 551 | // 552 | 553 | Map _itemTranslations = HashMap(); 554 | 555 | double itemTranslation(Key key) { 556 | if (!_itemTranslations.containsKey(key)) 557 | return 0.0; 558 | else 559 | return _itemTranslations[key]!.value; 560 | } 561 | 562 | void _adjustItemTranslation(Key key, double delta, double max) { 563 | double current = 0.0; 564 | final currentController = _itemTranslations[key]; 565 | if (currentController != null) { 566 | current = currentController.value; 567 | currentController.stop(canceled: true); 568 | currentController.dispose(); 569 | } 570 | 571 | current += delta; 572 | 573 | final newController = AnimationController( 574 | vsync: this, 575 | lowerBound: current < 0.0 ? -max : 0.0, 576 | upperBound: current < 0.0 ? 0.0 : max, 577 | value: current, 578 | duration: const Duration(milliseconds: 300)); 579 | newController.addListener(() { 580 | _items[key]?.setState(() {}); // update offset 581 | }); 582 | newController.addStatusListener((AnimationStatus s) { 583 | if (s == AnimationStatus.completed || s == AnimationStatus.dismissed) { 584 | newController.dispose(); 585 | if (_itemTranslations[key] == newController) { 586 | _itemTranslations.remove(key); 587 | } 588 | } 589 | }); 590 | _itemTranslations[key] = newController; 591 | 592 | newController.animateTo(0.0, curve: Curves.easeInOut); 593 | } 594 | 595 | AnimationController? _finalAnimation; 596 | } 597 | 598 | class _ReorderableItemState extends State { 599 | get key => widget.key; 600 | 601 | @override 602 | Widget build(BuildContext context) { 603 | // super.build(context); 604 | _listState = _ReorderableListState.of(context); 605 | 606 | _listState!.registerItem(this); 607 | bool dragging = _listState!.dragging == key; 608 | double translation = _listState!.itemTranslation(key); 609 | return Transform( 610 | transform: Matrix4.translationValues(0.0, translation, 0.0), 611 | child: widget.childBuilder( 612 | context, 613 | dragging 614 | ? ReorderableItemState.placeholder 615 | : ReorderableItemState.normal), 616 | ); 617 | } 618 | 619 | @override 620 | void didUpdateWidget(ReorderableItem oldWidget) { 621 | super.didUpdateWidget(oldWidget); 622 | 623 | _listState = _ReorderableListState.of(context); 624 | if (_listState!.dragging == this.key) { 625 | _listState!._draggedItemWidgetUpdated(); 626 | } 627 | } 628 | 629 | void update() { 630 | if (mounted) { 631 | setState(() {}); 632 | } 633 | } 634 | 635 | @override 636 | void deactivate() { 637 | _listState?.unregisterItem(this); 638 | _listState = null; 639 | super.deactivate(); 640 | } 641 | 642 | _ReorderableListState? _listState; 643 | } 644 | 645 | class _DragProxy extends StatefulWidget { 646 | final DecoratePlaceholder decoratePlaceholder; 647 | 648 | @override 649 | State createState() => _DragProxyState(); 650 | 651 | _DragProxy(this.decoratePlaceholder); 652 | } 653 | 654 | class _DragProxyState extends State<_DragProxy> { 655 | Widget? _widget; 656 | Size _size = Size.zero; 657 | double _offset = 0; 658 | double _offsetX = 0; 659 | 660 | _DragProxyState(); 661 | 662 | void setWidget(Widget widget, RenderBox position) { 663 | setState(() { 664 | _decorationOpacity = 1.0; 665 | _widget = widget; 666 | final state = _ReorderableListState.of(context)!; 667 | RenderBox renderBox = state.context.findRenderObject() as RenderBox; 668 | final offset = position.localToGlobal(Offset.zero, ancestor: renderBox); 669 | _offsetX = offset.dx; 670 | _offset = offset.dy; 671 | _size = position.size; 672 | }); 673 | } 674 | 675 | void updateWidget(Widget widget) { 676 | _widget = widget; 677 | } 678 | 679 | set offset(double newOffset) { 680 | setState(() { 681 | _offset = newOffset; 682 | }); 683 | } 684 | 685 | double get offset => _offset; 686 | 687 | double get height => _size.height; 688 | 689 | double _decorationOpacity = 0.0; 690 | 691 | set decorationOpacity(double val) { 692 | setState(() { 693 | _decorationOpacity = val; 694 | }); 695 | } 696 | 697 | void hide() { 698 | setState(() { 699 | _widget = null; 700 | }); 701 | } 702 | 703 | @override 704 | Widget build(BuildContext context) { 705 | final state = _ReorderableListState.of(context)!; 706 | state._dragProxy = this; 707 | 708 | if (_widget != null) { 709 | final w = IgnorePointer( 710 | child: MediaQuery.removePadding( 711 | context: context, 712 | child: _widget!, 713 | removeTop: true, 714 | removeBottom: true, 715 | ), 716 | ); 717 | 718 | final decoratedPlaceholder = 719 | widget.decoratePlaceholder(w, _decorationOpacity); 720 | return Positioned( 721 | child: decoratedPlaceholder.widget, 722 | left: _offsetX, 723 | width: _size.width, 724 | top: offset - decoratedPlaceholder.offset, 725 | ); 726 | } else { 727 | return Container(width: 0.0, height: 0.0); 728 | } 729 | } 730 | 731 | @override 732 | void deactivate() { 733 | _ReorderableListState.of(context)?._dragProxy = null; 734 | super.deactivate(); 735 | } 736 | } 737 | 738 | class _VerticalPointerState extends MultiDragPointerState { 739 | _VerticalPointerState(Offset initialPosition, PointerDeviceKind kind, 740 | DeviceGestureSettings? gestureSettings) 741 | : super(initialPosition, kind, gestureSettings) { 742 | _resolveTimer = Timer(Duration(milliseconds: 150), () { 743 | resolve(GestureDisposition.accepted); 744 | _resolveTimer = null; 745 | }); 746 | } 747 | 748 | @override 749 | void checkForResolutionAfterMove() { 750 | assert(pendingDelta != null); 751 | if (pendingDelta!.dy.abs() > pendingDelta!.dx.abs()) 752 | resolve(GestureDisposition.accepted); 753 | } 754 | 755 | @override 756 | void accepted(GestureMultiDragStartCallback starter) { 757 | starter(initialPosition); 758 | _resolveTimer?.cancel(); 759 | _resolveTimer = null; 760 | } 761 | 762 | void dispose() { 763 | _resolveTimer?.cancel(); 764 | _resolveTimer = null; 765 | super.dispose(); 766 | } 767 | 768 | Timer? _resolveTimer; 769 | } 770 | 771 | // 772 | // VerticalDragGestureRecognizer waits for kTouchSlop to be reached; We don't want that 773 | // when reordering items 774 | // 775 | class _Recognizer extends MultiDragGestureRecognizer { 776 | _Recognizer({ 777 | required Object? debugOwner, 778 | Set? supportedDevices, 779 | }) : super( 780 | debugOwner: debugOwner, 781 | supportedDevices: supportedDevices, 782 | ); 783 | 784 | @override 785 | _VerticalPointerState createNewPointerState(PointerDownEvent event) { 786 | return _VerticalPointerState(event.position, event.kind, gestureSettings); 787 | } 788 | 789 | @override 790 | String get debugDescription => "Vertical recognizer"; 791 | } 792 | 793 | DecoratedPlaceholder _defaultDecoratePlaceholder( 794 | Widget widget, double decorationOpacity) { 795 | final double decorationHeight = 10.0; 796 | 797 | final decoratedWidget = Builder(builder: (BuildContext context) { 798 | final mq = MediaQuery.of(context); 799 | return Column( 800 | crossAxisAlignment: CrossAxisAlignment.stretch, 801 | children: [ 802 | Opacity( 803 | opacity: decorationOpacity, 804 | child: Container( 805 | height: decorationHeight, 806 | decoration: BoxDecoration( 807 | border: Border( 808 | bottom: BorderSide( 809 | color: Color(0x50000000), 810 | width: 1.0 / mq.devicePixelRatio)), 811 | gradient: LinearGradient( 812 | begin: Alignment(0.0, -1.0), 813 | end: Alignment(0.0, 1.0), 814 | colors: [ 815 | Color(0x00000000), 816 | Color(0x10000000), 817 | Color(0x30000000) 818 | ], 819 | ), 820 | ), 821 | )), 822 | widget, 823 | Opacity( 824 | opacity: decorationOpacity, 825 | child: Container( 826 | height: decorationHeight, 827 | decoration: BoxDecoration( 828 | border: Border( 829 | top: BorderSide( 830 | color: Color(0x50000000), 831 | width: 1.0 / mq.devicePixelRatio)), 832 | gradient: LinearGradient( 833 | begin: Alignment(0.0, -1.0), 834 | end: Alignment(0.0, 1.0), 835 | colors: [ 836 | Color(0x30000000), 837 | Color(0x10000000), 838 | Color(0x00000000) 839 | ], 840 | ), 841 | ), 842 | )), 843 | ]); 844 | }); 845 | return DecoratedPlaceholder( 846 | offset: decorationHeight, 847 | widget: decoratedWidget, 848 | ); 849 | } 850 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_reorderable_list 2 | description: Reorderable list for flutter. 3 | version: 1.3.1 4 | homepage: https://github.com/knopp/flutter_reorderable_list 5 | 6 | environment: 7 | sdk: ">=2.17.0 <3.0.0" 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | 13 | dev_dependencies: 14 | flutter_test: 15 | sdk: flutter 16 | 17 | flutter: 18 | -------------------------------------------------------------------------------- /test/flutter_reorderable_list_test.dart: -------------------------------------------------------------------------------- 1 | void main() {} 2 | --------------------------------------------------------------------------------