├── .gitattributes ├── .gitignore ├── .idea ├── codeStyles │ └── Project.xml ├── libraries │ ├── Dart_Packages.xml │ ├── Dart_SDK.xml │ └── Flutter_Plugins.xml ├── markdown-navigator.xml ├── markdown-navigator │ └── profiles_settings.xml ├── misc.xml ├── modules.xml ├── vcs.xml └── workspace.xml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── demo.gif ├── draggable_flutter_list.iml ├── example ├── .gitignore ├── .idea │ ├── codeStyles │ │ └── Project.xml │ ├── libraries │ │ ├── Dart_Packages.xml │ │ ├── Dart_SDK.xml │ │ ├── Flutter_Plugins.xml │ │ └── Flutter_for_Android.xml │ ├── markdown-navigator.xml │ ├── markdown-navigator │ │ └── profiles_settings.xml │ ├── misc.xml │ ├── modules.xml │ ├── runConfigurations │ │ └── main_dart.xml │ └── workspace.xml ├── .metadata ├── README.md ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── com │ │ │ │ └── realank │ │ │ │ └── example │ │ │ │ └── MainActivity.java │ │ │ └── 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 │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle ├── example.iml ├── example_android.iml ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── WorkspaceSettings.xcsettings │ └── 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.lock ├── pubspec.yaml └── test │ └── widget_test.dart ├── lib ├── draggable_flutter_list.dart ├── draggable_list_item.dart ├── my_draggable.dart └── tools.dart ├── pubspec.lock ├── pubspec.yaml ├── screenshot.jpg └── test └── draggable_flutter_list_test.dart /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | 4 | .packages 5 | .pub/ 6 | 7 | build/ 8 | ios/.generated/ 9 | ios/Flutter/Generated.xcconfig 10 | ios/Runner/GeneratedPluginRegistrant.* 11 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.idea/libraries/Dart_Packages.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /.idea/libraries/Dart_SDK.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/libraries/Flutter_Plugins.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/markdown-navigator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 36 | 37 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /.idea/markdown-navigator/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 27 | 28 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [0.1.0] - release 2 | ## [0.1.1] - update readme 3 | ## [0.1.2] - add demo gif 4 | ## [0.1.3] - fix key issue 5 | ## [0.1.4] - fix hints 6 | ## [0.1.5] - add scroll controller property 7 | ## [0.1.6] - add scroll controller property 8 | ## [0.1.7] - fix bug when can't drop to an index 9 | ## [0.1.8] - fix dragging index bug 10 | ## [0.1.9] - pub.dartlang.org bug, can't analyze, re-submit 11 | ## [0.2.0] - improve performance 12 | ## [0.2.1] - fix dependencies 13 | ## [0.2.2] - format files 14 | ## [0.2.3] - format files 15 | ## [1.0.0] - release stable version 16 | ## [1.0.1] - fix out of bounds issue 17 | ## [1.0.2] - fix only one cell disappear when dragging issue 18 | ## [1.0.3] - typo packages name, dragable_flutter_list -> draggable_flutter_list -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Liu Yanbo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # draggable_flutter_list 2 | 3 | A flutter list view which can drag & move item to change order. 4 | 5 | - some codes come from [flutter_list_drag_and_drop](https://github.com/Norbert515/flutter_list_drag_and_drop) 6 | - fix flutter_list_drag_and_drop's dart version support 7 | - optimize data structure and api 8 | - drag and drop items in List View 9 | - drag and drop are optional for specific item 10 | - animate when dragging 11 | 12 | ### *Please use *draggable_flutter_list* for your dependency, 13 | The old name dragable_flutter_list (lack g) has been deprecated 14 | 15 | ![](./demo.gif) 16 | 17 | ## Usage 18 | 19 | [Pub package](https://pub.dartlang.org/packages/draggable_flutter_list) 20 | 21 | ```dart 22 | import 'package:flutter/material.dart'; 23 | import 'package:draggable_flutter_list/draggable_flutter_list.dart'; 24 | 25 | void main() { 26 | runApp(new TestApp()); 27 | } 28 | 29 | class TestApp extends StatelessWidget { 30 | TestApp({Key key}); 31 | 32 | // This widget is the root of your application. 33 | @override 34 | Widget build(BuildContext context) { 35 | return new MaterialApp( 36 | title: 'Flutter Demo', 37 | theme: new ThemeData(primarySwatch: Colors.blue), 38 | home: new MyHomePage( 39 | title: 'Flutter Demo Home Page', 40 | key: key, 41 | ), 42 | ); 43 | } 44 | } 45 | 46 | class MyHomePage extends StatefulWidget { 47 | MyHomePage({Key key, this.title}) : super(key: key); 48 | final String title; 49 | 50 | @override 51 | MyHomePageState createState() => new MyHomePageState(); 52 | } 53 | 54 | class MyHomePageState extends State { 55 | List items = [ 56 | '0', 57 | '1', 58 | 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.', 59 | '3', 60 | '4', 61 | 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.', 62 | '6', 63 | '7', 64 | '8', 65 | '9', 66 | '10', 67 | '11', 68 | '12', 69 | '13', 70 | '14', 71 | '15', 72 | ]; 73 | @override 74 | Widget build(BuildContext context) { 75 | return new Scaffold( 76 | appBar: new AppBar( 77 | title: new Text(widget.title), 78 | ), 79 | body: new DragAndDropList( 80 | items.length, 81 | itemBuilder: (BuildContext context, index) { 82 | return new SizedBox( 83 | child: new Card( 84 | child: new ListTile( 85 | title: new Text(items[index]), 86 | ), 87 | ), 88 | ); 89 | }, 90 | onDragFinish: (before, after) { 91 | print('on drag finish $before $after'); 92 | String data = items[before]; 93 | items.removeAt(before); 94 | items.insert(after, data); 95 | }, 96 | canDrag: (index) { 97 | print('can drag $index'); 98 | return index != 3; //disable drag for index 3 99 | }, 100 | canBeDraggedTo: (one, two) => true, 101 | dragElevation: 8.0, 102 | ), 103 | ); 104 | } 105 | } 106 | 107 | ``` 108 | 109 | ## Getting Started 110 | 111 | For help getting started with Flutter, view our online [documentation](https://flutter.io/). 112 | 113 | For help on editing package code, view the [documentation](https://flutter.io/developing-packages/). 114 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Realank/draggable_flutter_list/d91801cab7d398aff37af13a3dc207ddaa5f02c9/demo.gif -------------------------------------------------------------------------------- /draggable_flutter_list.iml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | 4 | .packages 5 | .pub/ 6 | 7 | build/ 8 | 9 | .flutter-plugins 10 | -------------------------------------------------------------------------------- /example/.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /example/.idea/libraries/Dart_Packages.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | -------------------------------------------------------------------------------- /example/.idea/libraries/Dart_SDK.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /example/.idea/libraries/Flutter_Plugins.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /example/.idea/libraries/Flutter_for_Android.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /example/.idea/markdown-navigator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 36 | 37 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /example/.idea/markdown-navigator/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /example/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 27 | 28 | -------------------------------------------------------------------------------- /example/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /example/.idea/runConfigurations/main_dart.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /example/.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 34 | 35 | 36 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 80 | 81 | 82 | 83 | 84 | 85 | 99 | 100 | 101 | 102 | 103 | 104 | 116 | 117 | 123 | 124 | 125 | 126 | 144 | 150 | 151 | 159 | 160 | 165 | 166 | 168 | 169 | 170 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 1535012481895 179 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | -------------------------------------------------------------------------------- /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: 5cd97f0004707872e5e12bdb83b62fbc855b80c8 8 | channel: master 9 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # example 2 | 3 | A new Flutter application. 4 | 5 | ## Getting Started 6 | 7 | For help getting started with Flutter, view our online 8 | [documentation](https://flutter.io/). 9 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | *.class 3 | .gradle 4 | /local.properties 5 | /.idea/workspace.xml 6 | /.idea/libraries 7 | .DS_Store 8 | /build 9 | /captures 10 | GeneratedPluginRegistrant.java 11 | -------------------------------------------------------------------------------- /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 from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 26 | 27 | android { 28 | compileSdkVersion 27 29 | 30 | lintOptions { 31 | disable 'InvalidPackage' 32 | } 33 | 34 | defaultConfig { 35 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 36 | applicationId "com.realank.example" 37 | minSdkVersion 16 38 | targetSdkVersion 27 39 | versionCode flutterVersionCode.toInteger() 40 | versionName flutterVersionName 41 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 42 | } 43 | 44 | buildTypes { 45 | release { 46 | // TODO: Add your own signing config for the release build. 47 | // Signing with the debug keys for now, so `flutter run --release` works. 48 | signingConfig signingConfigs.debug 49 | } 50 | } 51 | } 52 | 53 | flutter { 54 | source '../..' 55 | } 56 | 57 | dependencies { 58 | testImplementation 'junit:junit:4.12' 59 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 60 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 61 | } 62 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 15 | 19 | 26 | 30 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/realank/example/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.realank.example; 2 | 3 | import android.os.Bundle; 4 | import io.flutter.app.FlutterActivity; 5 | import io.flutter.plugins.GeneratedPluginRegistrant; 6 | 7 | public class MainActivity extends FlutterActivity { 8 | @Override 9 | protected void onCreate(Bundle savedInstanceState) { 10 | super.onCreate(savedInstanceState); 11 | GeneratedPluginRegistrant.registerWith(this); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /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/Realank/draggable_flutter_list/d91801cab7d398aff37af13a3dc207ddaa5f02c9/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/Realank/draggable_flutter_list/d91801cab7d398aff37af13a3dc207ddaa5f02c9/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/Realank/draggable_flutter_list/d91801cab7d398aff37af13a3dc207ddaa5f02c9/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/Realank/draggable_flutter_list/d91801cab7d398aff37af13a3dc207ddaa5f02c9/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/Realank/draggable_flutter_list/d91801cab7d398aff37af13a3dc207ddaa5f02c9/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:3.1.2' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | google() 15 | jcenter() 16 | } 17 | } 18 | 19 | rootProject.buildDir = '../build' 20 | subprojects { 21 | project.buildDir = "${rootProject.buildDir}/${project.name}" 22 | } 23 | subprojects { 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Realank/draggable_flutter_list/d91801cab7d398aff37af13a3dc207ddaa5f02c9/example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /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-4.4-all.zip 7 | -------------------------------------------------------------------------------- /example/android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /example/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /example/example.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /example/example_android.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | .generated/ 16 | 17 | *.pbxuser 18 | *.mode1v3 19 | *.mode2v3 20 | *.perspectivev3 21 | 22 | !default.pbxuser 23 | !default.mode1v3 24 | !default.mode2v3 25 | !default.perspectivev3 26 | 27 | xcuserdata 28 | 29 | *.moved-aside 30 | 31 | *.pyc 32 | *sync/ 33 | Icon? 34 | .tags* 35 | 36 | /Flutter/app.flx 37 | /Flutter/app.zip 38 | /Flutter/flutter_assets/ 39 | /Flutter/App.framework 40 | /Flutter/Flutter.framework 41 | /Flutter/Generated.xcconfig 42 | /ServiceDefinitions.json 43 | 44 | Pods/ 45 | .symlinks/ 46 | -------------------------------------------------------------------------------- /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/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 | 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */ = {isa = PBXBuildFile; fileRef = 2D5378251FAA1A9400D5DBA9 /* flutter_assets */; }; 12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 13 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; 14 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 15 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; 16 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 17 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; 18 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 19 | 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 20 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 21 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 22 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXCopyFilesBuildPhase section */ 26 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 27 | isa = PBXCopyFilesBuildPhase; 28 | buildActionMask = 2147483647; 29 | dstPath = ""; 30 | dstSubfolderSpec = 10; 31 | files = ( 32 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, 33 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, 34 | ); 35 | name = "Embed Frameworks"; 36 | runOnlyForDeploymentPostprocessing = 0; 37 | }; 38 | /* End PBXCopyFilesBuildPhase section */ 39 | 40 | /* Begin PBXFileReference section */ 41 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 42 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 43 | 2D5378251FAA1A9400D5DBA9 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = Flutter/flutter_assets; sourceTree = SOURCE_ROOT; }; 44 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 45 | 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 46 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 47 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 48 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 49 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 50 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 51 | 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 52 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 53 | 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 54 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 55 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 56 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 57 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 58 | /* End PBXFileReference section */ 59 | 60 | /* Begin PBXFrameworksBuildPhase section */ 61 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 62 | isa = PBXFrameworksBuildPhase; 63 | buildActionMask = 2147483647; 64 | files = ( 65 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, 66 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, 67 | ); 68 | runOnlyForDeploymentPostprocessing = 0; 69 | }; 70 | /* End PBXFrameworksBuildPhase section */ 71 | 72 | /* Begin PBXGroup section */ 73 | 9740EEB11CF90186004384FC /* Flutter */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | 2D5378251FAA1A9400D5DBA9 /* flutter_assets */, 77 | 3B80C3931E831B6300D905FE /* App.framework */, 78 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 79 | 9740EEBA1CF902C7004384FC /* Flutter.framework */, 80 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 81 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 82 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 83 | ); 84 | name = Flutter; 85 | sourceTree = ""; 86 | }; 87 | 97C146E51CF9000F007C117D = { 88 | isa = PBXGroup; 89 | children = ( 90 | 9740EEB11CF90186004384FC /* Flutter */, 91 | 97C146F01CF9000F007C117D /* Runner */, 92 | 97C146EF1CF9000F007C117D /* Products */, 93 | CF3B75C9A7D2FA2A4C99F110 /* Frameworks */, 94 | ); 95 | sourceTree = ""; 96 | }; 97 | 97C146EF1CF9000F007C117D /* Products */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 97C146EE1CF9000F007C117D /* Runner.app */, 101 | ); 102 | name = Products; 103 | sourceTree = ""; 104 | }; 105 | 97C146F01CF9000F007C117D /* Runner */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, 109 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, 110 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 111 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 112 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 113 | 97C147021CF9000F007C117D /* Info.plist */, 114 | 97C146F11CF9000F007C117D /* Supporting Files */, 115 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 116 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 117 | ); 118 | path = Runner; 119 | sourceTree = ""; 120 | }; 121 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | 97C146F21CF9000F007C117D /* main.m */, 125 | ); 126 | name = "Supporting Files"; 127 | sourceTree = ""; 128 | }; 129 | /* End PBXGroup section */ 130 | 131 | /* Begin PBXNativeTarget section */ 132 | 97C146ED1CF9000F007C117D /* Runner */ = { 133 | isa = PBXNativeTarget; 134 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 135 | buildPhases = ( 136 | 9740EEB61CF901F6004384FC /* Run Script */, 137 | 97C146EA1CF9000F007C117D /* Sources */, 138 | 97C146EB1CF9000F007C117D /* Frameworks */, 139 | 97C146EC1CF9000F007C117D /* Resources */, 140 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 141 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 142 | ); 143 | buildRules = ( 144 | ); 145 | dependencies = ( 146 | ); 147 | name = Runner; 148 | productName = Runner; 149 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 150 | productType = "com.apple.product-type.application"; 151 | }; 152 | /* End PBXNativeTarget section */ 153 | 154 | /* Begin PBXProject section */ 155 | 97C146E61CF9000F007C117D /* Project object */ = { 156 | isa = PBXProject; 157 | attributes = { 158 | LastUpgradeCheck = 0910; 159 | ORGANIZATIONNAME = "The Chromium Authors"; 160 | TargetAttributes = { 161 | 97C146ED1CF9000F007C117D = { 162 | CreatedOnToolsVersion = 7.3.1; 163 | }; 164 | }; 165 | }; 166 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 167 | compatibilityVersion = "Xcode 3.2"; 168 | developmentRegion = English; 169 | hasScannedForEncodings = 0; 170 | knownRegions = ( 171 | en, 172 | Base, 173 | ); 174 | mainGroup = 97C146E51CF9000F007C117D; 175 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 176 | projectDirPath = ""; 177 | projectRoot = ""; 178 | targets = ( 179 | 97C146ED1CF9000F007C117D /* Runner */, 180 | ); 181 | }; 182 | /* End PBXProject section */ 183 | 184 | /* Begin PBXResourcesBuildPhase section */ 185 | 97C146EC1CF9000F007C117D /* Resources */ = { 186 | isa = PBXResourcesBuildPhase; 187 | buildActionMask = 2147483647; 188 | files = ( 189 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 190 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 191 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, 192 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 193 | 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */, 194 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 195 | ); 196 | runOnlyForDeploymentPostprocessing = 0; 197 | }; 198 | /* End PBXResourcesBuildPhase section */ 199 | 200 | /* Begin PBXShellScriptBuildPhase section */ 201 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 202 | isa = PBXShellScriptBuildPhase; 203 | buildActionMask = 2147483647; 204 | files = ( 205 | ); 206 | inputPaths = ( 207 | ); 208 | name = "Thin Binary"; 209 | outputPaths = ( 210 | ); 211 | runOnlyForDeploymentPostprocessing = 0; 212 | shellPath = /bin/sh; 213 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; 214 | }; 215 | 9740EEB61CF901F6004384FC /* Run Script */ = { 216 | isa = PBXShellScriptBuildPhase; 217 | buildActionMask = 2147483647; 218 | files = ( 219 | ); 220 | inputPaths = ( 221 | ); 222 | name = "Run Script"; 223 | outputPaths = ( 224 | ); 225 | runOnlyForDeploymentPostprocessing = 0; 226 | shellPath = /bin/sh; 227 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 228 | }; 229 | /* End PBXShellScriptBuildPhase section */ 230 | 231 | /* Begin PBXSourcesBuildPhase section */ 232 | 97C146EA1CF9000F007C117D /* Sources */ = { 233 | isa = PBXSourcesBuildPhase; 234 | buildActionMask = 2147483647; 235 | files = ( 236 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, 237 | 97C146F31CF9000F007C117D /* main.m in Sources */, 238 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 239 | ); 240 | runOnlyForDeploymentPostprocessing = 0; 241 | }; 242 | /* End PBXSourcesBuildPhase section */ 243 | 244 | /* Begin PBXVariantGroup section */ 245 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 246 | isa = PBXVariantGroup; 247 | children = ( 248 | 97C146FB1CF9000F007C117D /* Base */, 249 | ); 250 | name = Main.storyboard; 251 | sourceTree = ""; 252 | }; 253 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 254 | isa = PBXVariantGroup; 255 | children = ( 256 | 97C147001CF9000F007C117D /* Base */, 257 | ); 258 | name = LaunchScreen.storyboard; 259 | sourceTree = ""; 260 | }; 261 | /* End PBXVariantGroup section */ 262 | 263 | /* Begin XCBuildConfiguration section */ 264 | 97C147031CF9000F007C117D /* Debug */ = { 265 | isa = XCBuildConfiguration; 266 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 267 | buildSettings = { 268 | ALWAYS_SEARCH_USER_PATHS = NO; 269 | CLANG_ANALYZER_NONNULL = YES; 270 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 271 | CLANG_CXX_LIBRARY = "libc++"; 272 | CLANG_ENABLE_MODULES = YES; 273 | CLANG_ENABLE_OBJC_ARC = YES; 274 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 275 | CLANG_WARN_BOOL_CONVERSION = YES; 276 | CLANG_WARN_COMMA = YES; 277 | CLANG_WARN_CONSTANT_CONVERSION = YES; 278 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 279 | CLANG_WARN_EMPTY_BODY = YES; 280 | CLANG_WARN_ENUM_CONVERSION = YES; 281 | CLANG_WARN_INFINITE_RECURSION = YES; 282 | CLANG_WARN_INT_CONVERSION = YES; 283 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 284 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 285 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 286 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 287 | CLANG_WARN_STRICT_PROTOTYPES = YES; 288 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 289 | CLANG_WARN_UNREACHABLE_CODE = YES; 290 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 291 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 292 | COPY_PHASE_STRIP = NO; 293 | DEBUG_INFORMATION_FORMAT = dwarf; 294 | ENABLE_STRICT_OBJC_MSGSEND = YES; 295 | ENABLE_TESTABILITY = YES; 296 | GCC_C_LANGUAGE_STANDARD = gnu99; 297 | GCC_DYNAMIC_NO_PIC = NO; 298 | GCC_NO_COMMON_BLOCKS = YES; 299 | GCC_OPTIMIZATION_LEVEL = 0; 300 | GCC_PREPROCESSOR_DEFINITIONS = ( 301 | "DEBUG=1", 302 | "$(inherited)", 303 | ); 304 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 305 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 306 | GCC_WARN_UNDECLARED_SELECTOR = YES; 307 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 308 | GCC_WARN_UNUSED_FUNCTION = YES; 309 | GCC_WARN_UNUSED_VARIABLE = YES; 310 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 311 | MTL_ENABLE_DEBUG_INFO = YES; 312 | ONLY_ACTIVE_ARCH = YES; 313 | SDKROOT = iphoneos; 314 | TARGETED_DEVICE_FAMILY = "1,2"; 315 | }; 316 | name = Debug; 317 | }; 318 | 97C147041CF9000F007C117D /* Release */ = { 319 | isa = XCBuildConfiguration; 320 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 321 | buildSettings = { 322 | ALWAYS_SEARCH_USER_PATHS = NO; 323 | CLANG_ANALYZER_NONNULL = YES; 324 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 325 | CLANG_CXX_LIBRARY = "libc++"; 326 | CLANG_ENABLE_MODULES = YES; 327 | CLANG_ENABLE_OBJC_ARC = YES; 328 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 329 | CLANG_WARN_BOOL_CONVERSION = YES; 330 | CLANG_WARN_COMMA = YES; 331 | CLANG_WARN_CONSTANT_CONVERSION = YES; 332 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 333 | CLANG_WARN_EMPTY_BODY = YES; 334 | CLANG_WARN_ENUM_CONVERSION = YES; 335 | CLANG_WARN_INFINITE_RECURSION = YES; 336 | CLANG_WARN_INT_CONVERSION = YES; 337 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 338 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 339 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 340 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 341 | CLANG_WARN_STRICT_PROTOTYPES = YES; 342 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 343 | CLANG_WARN_UNREACHABLE_CODE = YES; 344 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 345 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 346 | COPY_PHASE_STRIP = NO; 347 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 348 | ENABLE_NS_ASSERTIONS = NO; 349 | ENABLE_STRICT_OBJC_MSGSEND = YES; 350 | GCC_C_LANGUAGE_STANDARD = gnu99; 351 | GCC_NO_COMMON_BLOCKS = YES; 352 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 353 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 354 | GCC_WARN_UNDECLARED_SELECTOR = YES; 355 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 356 | GCC_WARN_UNUSED_FUNCTION = YES; 357 | GCC_WARN_UNUSED_VARIABLE = YES; 358 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 359 | MTL_ENABLE_DEBUG_INFO = NO; 360 | SDKROOT = iphoneos; 361 | TARGETED_DEVICE_FAMILY = "1,2"; 362 | VALIDATE_PRODUCT = YES; 363 | }; 364 | name = Release; 365 | }; 366 | 97C147061CF9000F007C117D /* Debug */ = { 367 | isa = XCBuildConfiguration; 368 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 369 | buildSettings = { 370 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 371 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 372 | ENABLE_BITCODE = NO; 373 | FRAMEWORK_SEARCH_PATHS = ( 374 | "$(inherited)", 375 | "$(PROJECT_DIR)/Flutter", 376 | ); 377 | INFOPLIST_FILE = Runner/Info.plist; 378 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 379 | LIBRARY_SEARCH_PATHS = ( 380 | "$(inherited)", 381 | "$(PROJECT_DIR)/Flutter", 382 | ); 383 | PRODUCT_BUNDLE_IDENTIFIER = com.realank.example; 384 | PRODUCT_NAME = "$(TARGET_NAME)"; 385 | VERSIONING_SYSTEM = "apple-generic"; 386 | }; 387 | name = Debug; 388 | }; 389 | 97C147071CF9000F007C117D /* Release */ = { 390 | isa = XCBuildConfiguration; 391 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 392 | buildSettings = { 393 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 394 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 395 | ENABLE_BITCODE = NO; 396 | FRAMEWORK_SEARCH_PATHS = ( 397 | "$(inherited)", 398 | "$(PROJECT_DIR)/Flutter", 399 | ); 400 | INFOPLIST_FILE = Runner/Info.plist; 401 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 402 | LIBRARY_SEARCH_PATHS = ( 403 | "$(inherited)", 404 | "$(PROJECT_DIR)/Flutter", 405 | ); 406 | PRODUCT_BUNDLE_IDENTIFIER = com.realank.example; 407 | PRODUCT_NAME = "$(TARGET_NAME)"; 408 | VERSIONING_SYSTEM = "apple-generic"; 409 | }; 410 | name = Release; 411 | }; 412 | /* End XCBuildConfiguration section */ 413 | 414 | /* Begin XCConfigurationList section */ 415 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 416 | isa = XCConfigurationList; 417 | buildConfigurations = ( 418 | 97C147031CF9000F007C117D /* Debug */, 419 | 97C147041CF9000F007C117D /* Release */, 420 | ); 421 | defaultConfigurationIsVisible = 0; 422 | defaultConfigurationName = Release; 423 | }; 424 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 425 | isa = XCConfigurationList; 426 | buildConfigurations = ( 427 | 97C147061CF9000F007C117D /* Debug */, 428 | 97C147071CF9000F007C117D /* Release */, 429 | ); 430 | defaultConfigurationIsVisible = 0; 431 | defaultConfigurationName = Release; 432 | }; 433 | /* End XCConfigurationList section */ 434 | }; 435 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 436 | } 437 | -------------------------------------------------------------------------------- /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/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildSystemType 6 | Original 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/Realank/draggable_flutter_list/d91801cab7d398aff37af13a3dc207ddaa5f02c9/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/Realank/draggable_flutter_list/d91801cab7d398aff37af13a3dc207ddaa5f02c9/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/Realank/draggable_flutter_list/d91801cab7d398aff37af13a3dc207ddaa5f02c9/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/Realank/draggable_flutter_list/d91801cab7d398aff37af13a3dc207ddaa5f02c9/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/Realank/draggable_flutter_list/d91801cab7d398aff37af13a3dc207ddaa5f02c9/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/Realank/draggable_flutter_list/d91801cab7d398aff37af13a3dc207ddaa5f02c9/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/Realank/draggable_flutter_list/d91801cab7d398aff37af13a3dc207ddaa5f02c9/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/Realank/draggable_flutter_list/d91801cab7d398aff37af13a3dc207ddaa5f02c9/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/Realank/draggable_flutter_list/d91801cab7d398aff37af13a3dc207ddaa5f02c9/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/Realank/draggable_flutter_list/d91801cab7d398aff37af13a3dc207ddaa5f02c9/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/Realank/draggable_flutter_list/d91801cab7d398aff37af13a3dc207ddaa5f02c9/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/Realank/draggable_flutter_list/d91801cab7d398aff37af13a3dc207ddaa5f02c9/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/Realank/draggable_flutter_list/d91801cab7d398aff37af13a3dc207ddaa5f02c9/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/Realank/draggable_flutter_list/d91801cab7d398aff37af13a3dc207ddaa5f02c9/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/Realank/draggable_flutter_list/d91801cab7d398aff37af13a3dc207ddaa5f02c9/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/Realank/draggable_flutter_list/d91801cab7d398aff37af13a3dc207ddaa5f02c9/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Realank/draggable_flutter_list/d91801cab7d398aff37af13a3dc207ddaa5f02c9/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Realank/draggable_flutter_list/d91801cab7d398aff37af13a3dc207ddaa5f02c9/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'; 2 | import 'package:draggable_flutter_list/draggable_flutter_list.dart'; 3 | 4 | void main() { 5 | runApp(new TestApp()); 6 | } 7 | 8 | class TestApp extends StatelessWidget { 9 | TestApp({Key key}); 10 | 11 | // This widget is the root of your application. 12 | @override 13 | Widget build(BuildContext context) { 14 | return new MaterialApp( 15 | title: 'Flutter Demo', 16 | theme: new ThemeData(primarySwatch: Colors.blue), 17 | home: new MyHomePage( 18 | title: 'Flutter Demo Home Page', 19 | key: key, 20 | ), 21 | ); 22 | } 23 | } 24 | 25 | class MyHomePage extends StatefulWidget { 26 | MyHomePage({Key key, this.title}) : super(key: key); 27 | final String title; 28 | 29 | @override 30 | MyHomePageState createState() => new MyHomePageState(); 31 | } 32 | 33 | class MyHomePageState extends State { 34 | List items = [ 35 | '0', 36 | '1sfsdfdsfdsfsafafsafdsafsadfdsfdsfsafafsafdsafsadfdsfdsfsafafsafdsafsadfdsfdsfsafafsafdsafsadfdsfdsfsafafsafdsafsadfdsfdsfsafafsafdsafsadfdsfdsfsafafsafdsafsadfdsfdsfsafafsafdsafsadfdsfdsfsafafsafdsafsadfdsfdsfsafafsafdsafsadfdsfdsfsafafsafdsafsadfsadf', 37 | 'd' 38 | ]; 39 | @override 40 | Widget build(BuildContext context) { 41 | return new Scaffold( 42 | appBar: new AppBar( 43 | title: new Text(widget.title), 44 | ), 45 | body: new DragAndDropList( 46 | items.length, 47 | itemBuilder: (BuildContext context, index) { 48 | return new SizedBox( 49 | child: new Card( 50 | child: new ListTile( 51 | title: new Text(items[index]), 52 | ), 53 | ), 54 | ); 55 | }, 56 | onDragFinish: (before, after) { 57 | String data = items[before]; 58 | items.removeAt(before); 59 | items.insert(after, data); 60 | }, 61 | canDrag: (index) { 62 | return index < 5; //disable drag for index 3 63 | }, 64 | canBeDraggedTo: (from, to) { 65 | return to < 5; 66 | }, 67 | dragElevation: 8.0, 68 | ), 69 | ); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /example/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://www.dartlang.org/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.0.8" 11 | boolean_selector: 12 | dependency: transitive 13 | description: 14 | name: boolean_selector 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "1.0.4" 18 | charcode: 19 | dependency: transitive 20 | description: 21 | name: charcode 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.1.2" 25 | collection: 26 | dependency: transitive 27 | description: 28 | name: collection 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.14.11" 32 | cupertino_icons: 33 | dependency: "direct main" 34 | description: 35 | name: cupertino_icons 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "0.1.2" 39 | draggable_flutter_list: 40 | dependency: "direct main" 41 | description: 42 | path: ".." 43 | relative: true 44 | source: path 45 | version: "1.0.2" 46 | flutter: 47 | dependency: "direct main" 48 | description: flutter 49 | source: sdk 50 | version: "0.0.0" 51 | flutter_test: 52 | dependency: "direct dev" 53 | description: flutter 54 | source: sdk 55 | version: "0.0.0" 56 | matcher: 57 | dependency: transitive 58 | description: 59 | name: matcher 60 | url: "https://pub.dartlang.org" 61 | source: hosted 62 | version: "0.12.3+1" 63 | meta: 64 | dependency: transitive 65 | description: 66 | name: meta 67 | url: "https://pub.dartlang.org" 68 | source: hosted 69 | version: "1.1.6" 70 | path: 71 | dependency: transitive 72 | description: 73 | name: path 74 | url: "https://pub.dartlang.org" 75 | source: hosted 76 | version: "1.6.2" 77 | quiver: 78 | dependency: transitive 79 | description: 80 | name: quiver 81 | url: "https://pub.dartlang.org" 82 | source: hosted 83 | version: "2.0.1" 84 | sky_engine: 85 | dependency: transitive 86 | description: flutter 87 | source: sdk 88 | version: "0.0.99" 89 | source_span: 90 | dependency: transitive 91 | description: 92 | name: source_span 93 | url: "https://pub.dartlang.org" 94 | source: hosted 95 | version: "1.4.1" 96 | stack_trace: 97 | dependency: transitive 98 | description: 99 | name: stack_trace 100 | url: "https://pub.dartlang.org" 101 | source: hosted 102 | version: "1.9.3" 103 | stream_channel: 104 | dependency: transitive 105 | description: 106 | name: stream_channel 107 | url: "https://pub.dartlang.org" 108 | source: hosted 109 | version: "1.6.8" 110 | string_scanner: 111 | dependency: transitive 112 | description: 113 | name: string_scanner 114 | url: "https://pub.dartlang.org" 115 | source: hosted 116 | version: "1.0.4" 117 | term_glyph: 118 | dependency: transitive 119 | description: 120 | name: term_glyph 121 | url: "https://pub.dartlang.org" 122 | source: hosted 123 | version: "1.0.1" 124 | test_api: 125 | dependency: transitive 126 | description: 127 | name: test_api 128 | url: "https://pub.dartlang.org" 129 | source: hosted 130 | version: "0.2.1" 131 | typed_data: 132 | dependency: transitive 133 | description: 134 | name: typed_data 135 | url: "https://pub.dartlang.org" 136 | source: hosted 137 | version: "1.1.6" 138 | vector_math: 139 | dependency: transitive 140 | description: 141 | name: vector_math 142 | url: "https://pub.dartlang.org" 143 | source: hosted 144 | version: "2.0.8" 145 | sdks: 146 | dart: ">=2.0.0 <3.0.0" 147 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: example 2 | description: A new Flutter application. 3 | 4 | # The following defines the version and build number for your application. 5 | # A version number is three numbers separated by dots, like 1.2.43 6 | # followed by an optional build number separated by a +. 7 | # Both the version and the builder number may be overridden in flutter 8 | # build by specifying --build-name and --build-number, respectively. 9 | # Read more about versioning at semver.org. 10 | version: 1.0.0+1 11 | 12 | environment: 13 | sdk: ">=2.0.0-dev.68.0 <3.0.0" 14 | 15 | dependencies: 16 | flutter: 17 | sdk: flutter 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: ^0.1.2 22 | draggable_flutter_list: 23 | path: ../ 24 | 25 | dev_dependencies: 26 | flutter_test: 27 | sdk: flutter 28 | 29 | 30 | # For information on the generic Dart part of this file, see the 31 | # following page: https://www.dartlang.org/tools/pub/pubspec 32 | 33 | # The following section is specific to Flutter. 34 | flutter: 35 | 36 | # The following line ensures that the Material Icons font is 37 | # included with your application, so that you can use the icons in 38 | # the material Icons class. 39 | uses-material-design: true 40 | 41 | # To add assets to your application, add an assets section, like this: 42 | # assets: 43 | # - images/a_dot_burr.jpeg 44 | # - images/a_dot_ham.jpeg 45 | 46 | # An image asset can refer to one or more resolution-specific "variants", see 47 | # https://flutter.io/assets-and-images/#resolution-aware. 48 | 49 | # For details regarding adding assets from package dependencies, see 50 | # https://flutter.io/assets-and-images/#from-packages 51 | 52 | # To add custom fonts to your application, add a fonts section here, 53 | # in this "flutter" section. Each entry in this list should have a 54 | # "family" key with the font family name, and a "fonts" key with a 55 | # list giving the asset and other descriptors for the font. For 56 | # example: 57 | # fonts: 58 | # - family: Schyler 59 | # fonts: 60 | # - asset: fonts/Schyler-Regular.ttf 61 | # - asset: fonts/Schyler-Italic.ttf 62 | # style: italic 63 | # - family: Trajan Pro 64 | # fonts: 65 | # - asset: fonts/TrajanPro.ttf 66 | # - asset: fonts/TrajanPro_Bold.ttf 67 | # weight: 700 68 | # 69 | # For details regarding fonts from package dependencies, 70 | # see https://flutter.io/custom-fonts/#from-packages 71 | -------------------------------------------------------------------------------- /example/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // To perform an interaction with a widget in your test, use the WidgetTester utility that Flutter 3 | // provides. For example, you can send tap and scroll gestures. You can also use WidgetTester to 4 | // find child widgets in the widget tree, read text, and verify that the values of widget properties 5 | // are correct. 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:flutter_test/flutter_test.dart'; 9 | 10 | import 'package:example/main.dart'; 11 | 12 | void main() { 13 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 14 | // Build our app and trigger a frame. 15 | await tester.pumpWidget(new TestApp()); 16 | 17 | // Verify that our counter starts at 0. 18 | expect(find.text('0'), findsOneWidget); 19 | expect(find.text('1'), findsNothing); 20 | 21 | // Tap the '+' icon and trigger a frame. 22 | await tester.tap(find.byIcon(Icons.add)); 23 | await tester.pump(); 24 | 25 | // Verify that our counter has incremented. 26 | expect(find.text('0'), findsNothing); 27 | expect(find.text('1'), findsOneWidget); 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /lib/draggable_flutter_list.dart: -------------------------------------------------------------------------------- 1 | library draggable_flutter_list; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/animation.dart'; 5 | import 'package:flutter/rendering.dart'; 6 | import 'draggable_list_item.dart'; 7 | import 'tools.dart'; 8 | 9 | typedef Widget WidgetMaker(BuildContext context, int index); 10 | 11 | class DragAndDropList extends StatefulWidget { 12 | final int rowsCount; 13 | 14 | final WidgetMaker itemBuilder; 15 | 16 | final CanDrag canDrag; 17 | 18 | final OnDragFinish onDragFinish; 19 | 20 | final CanAccept canBeDraggedTo; 21 | 22 | // dragElevation is only used if isItemsHaveCustomDraggableBehavior=false. 23 | // Otherwise, draggable items provide their own elevation/shadow. 24 | final double dragElevation; 25 | 26 | final ScrollController scrollController; 27 | 28 | DragAndDropList(this.rowsCount, 29 | {Key key, 30 | @required this.itemBuilder, 31 | this.onDragFinish, 32 | @required this.canBeDraggedTo, 33 | this.dragElevation = 0.0, 34 | this.canDrag, 35 | scrollController}) 36 | : this.scrollController = scrollController ?? ScrollController(), 37 | super(key: key); 38 | 39 | @override 40 | State createState() => new _DragAndDropListState(); 41 | } 42 | 43 | class _DragAndDropListState extends State { 44 | final double _kScrollThreshold = 160.0; 45 | 46 | bool shouldScrollUp = false; 47 | bool shouldScrollDown = false; 48 | 49 | double _currentScrollPos = 0.0; 50 | 51 | List rows = new List(); 52 | 53 | //Index of the item dragged 54 | int _currentDraggingIndex; 55 | 56 | // The height of the item being dragged 57 | double dragHeight; 58 | 59 | SliverMultiBoxAdaptorElement renderSliverContext; 60 | 61 | Offset _currentMiddle; 62 | 63 | //Index of the item currently accepting 64 | int _currentIndex; 65 | 66 | bool isScrolling = false; 67 | 68 | double offsetToStartOfItem = 0.0; 69 | 70 | double sliverStartPos = 0.0; 71 | 72 | bool didJustStartDragging = false; 73 | 74 | // This corrects the case when the user grabs the card at the bottom, the system will always handle like grabbed on the middle to ensure correct behvior 75 | double middleOfItemInGlobalPosition = 0.0; 76 | 77 | @override 78 | void initState() { 79 | super.initState(); 80 | populateRowList(); 81 | } 82 | 83 | void populateRowList() { 84 | rows = []; 85 | for (int i = 0; i < widget.rowsCount; i++) { 86 | rows.add(Data(i)); 87 | } 88 | } 89 | 90 | void _maybeScroll() { 91 | if (isScrolling) return; 92 | 93 | if (shouldScrollUp) { 94 | if (widget.scrollController.position.pixels == 0.0) return; 95 | isScrolling = true; 96 | var scrollTo = widget.scrollController.offset - 12.0; 97 | widget.scrollController 98 | .animateTo(scrollTo, duration: new Duration(milliseconds: 74), curve: Curves.linear) 99 | .then((it) { 100 | updatePlaceholder(); 101 | isScrolling = false; 102 | _maybeScroll(); 103 | }); 104 | //TODO implement 105 | // Scrollable.ensureVisible(context, ); 106 | } 107 | if (shouldScrollDown) { 108 | if (widget.scrollController.position.pixels == 109 | widget.scrollController.position.maxScrollExtent) return; 110 | isScrolling = true; 111 | var scrollTo = widget.scrollController.offset + 12.0; 112 | widget.scrollController 113 | .animateTo(scrollTo, duration: new Duration(milliseconds: 75), curve: Curves.linear) 114 | .then((it) { 115 | updatePlaceholder(); 116 | isScrolling = false; 117 | _maybeScroll(); 118 | }); 119 | } 120 | } 121 | 122 | @override 123 | void didUpdateWidget(DragAndDropList oldWidget) { 124 | super.didUpdateWidget(oldWidget); 125 | populateRowList(); 126 | } 127 | 128 | @override 129 | Widget build(BuildContext context) { 130 | return new LayoutBuilder( 131 | builder: (BuildContext context3, constr) { 132 | return new ListView.builder( 133 | itemBuilder: (BuildContext context2, int index) { 134 | return _getDraggableListItem(context2, index, context3); 135 | }, 136 | controller: widget.scrollController, 137 | itemCount: rows.length, 138 | ); 139 | }, 140 | ); 141 | } 142 | 143 | Widget _getDraggableListItem(BuildContext context2, int index, BuildContext context3) { 144 | var draggableListItem = new DraggableListItem( 145 | child: widget.itemBuilder(context2, rows[index].index), 146 | key: new ValueKey(rows[index]), 147 | data: rows[index], 148 | index: index, 149 | dragElevation: widget.dragElevation, 150 | draggedHeight: dragHeight, 151 | canDrag: widget.canDrag, 152 | onDragStarted: (double draggedHeight, double globalTopPositionOfDraggedItem) { 153 | _currentDraggingIndex = index; 154 | RenderBox rend = context3.findRenderObject(); 155 | double start = rend.localToGlobal(new Offset(0.0, 0.0)).dy; 156 | // double end = rend.localToGlobal(new Offset(0.0, rend.semanticBounds.height)).dy; 157 | 158 | didJustStartDragging = true; 159 | _currentScrollPos = start; 160 | 161 | middleOfItemInGlobalPosition = globalTopPositionOfDraggedItem + draggedHeight / 2; 162 | 163 | sliverStartPos = start; 164 | 165 | // _buildOverlay(context2, start, end); 166 | 167 | renderSliverContext = context2; 168 | updatePlaceholder(); 169 | dragHeight = draggedHeight; 170 | 171 | setState(() { 172 | rows.removeAt(index); 173 | }); 174 | }, 175 | onDragCompleted: () { 176 | _accept(index, _currentDraggingIndex); 177 | }, 178 | onAccept: (int index) { 179 | _accept(_currentIndex, index); 180 | }, 181 | onMove: (Offset offset) { 182 | if (didJustStartDragging) { 183 | didJustStartDragging = false; 184 | offsetToStartOfItem = offset.dy - middleOfItemInGlobalPosition; 185 | _currentScrollPos = offset.dy - offsetToStartOfItem; 186 | } 187 | _currentScrollPos = offset.dy - offsetToStartOfItem; 188 | double screenHeight = MediaQuery.of(context2).size.height; 189 | 190 | if (_currentScrollPos < _kScrollThreshold) { 191 | shouldScrollUp = true; 192 | } else { 193 | shouldScrollUp = false; 194 | } 195 | if (_currentScrollPos > screenHeight - _kScrollThreshold) { 196 | shouldScrollDown = true; 197 | } else { 198 | shouldScrollDown = false; 199 | } 200 | _maybeScroll(); 201 | updatePlaceholder(); 202 | }, 203 | cancelCallback: () { 204 | _accept(_currentIndex, _currentDraggingIndex); 205 | }, 206 | ); 207 | return draggableListItem; 208 | } 209 | 210 | void _complete() { 211 | shouldScrollUp = false; 212 | shouldScrollDown = false; 213 | _currentIndex = null; 214 | _currentScrollPos = 0.0; 215 | _currentMiddle = null; 216 | _currentDraggingIndex = null; 217 | didJustStartDragging = false; 218 | offsetToStartOfItem = 0.0; 219 | middleOfItemInGlobalPosition = 0.0; 220 | } 221 | 222 | void _accept(int toIndex, int fromIndex) { 223 | if (_currentIndex == null || _currentMiddle == null) { 224 | setState(() { 225 | populateRowList(); 226 | }); 227 | _complete(); 228 | return; 229 | } 230 | setState(() { 231 | shouldScrollDown = false; 232 | shouldScrollUp = false; 233 | if (fromIndex < rows.length) { 234 | rows[fromIndex].extraHeight = 0.0; 235 | } 236 | 237 | if (_currentMiddle.dy >= _currentScrollPos || rows.length == 0) { 238 | widget.onDragFinish(_currentDraggingIndex, toIndex); 239 | } else { 240 | widget.onDragFinish(_currentDraggingIndex, toIndex + 1); 241 | } 242 | populateRowList(); 243 | }); 244 | _complete(); 245 | } 246 | 247 | void updatePlaceholder() { 248 | if (renderSliverContext == null) return; 249 | if (_currentDraggingIndex == null) return; 250 | RenderSliverList it = renderSliverContext.findRenderObject(); 251 | double buffer = sliverStartPos; 252 | RenderBox currentChild = it.firstChild; 253 | print('current child $currentChild'); 254 | if (currentChild == null) { 255 | return; 256 | } 257 | buffer += it.childMainAxisPosition(currentChild) + currentChild.size.height; 258 | while (_currentScrollPos > buffer) { 259 | if (currentChild != null) { 260 | var bufferChild = it.childAfter(currentChild); 261 | if (bufferChild == null) break; 262 | currentChild = bufferChild; 263 | buffer = it.childMainAxisPosition(currentChild) + currentChild.size.height + sliverStartPos; 264 | } 265 | } 266 | double middle = buffer - currentChild.size.height / 2; 267 | 268 | int index = it.indexOf(currentChild); 269 | 270 | if (!widget.canBeDraggedTo(_currentDraggingIndex, index)) return; 271 | 272 | _currentMiddle = new Offset(0.0, middle); 273 | 274 | final previousIndex = _currentIndex; 275 | final nextIndex = index; 276 | _currentIndex = index; 277 | final atTop = _currentScrollPos <= _currentMiddle.dy; 278 | 279 | if (nextIndex < rows.length && 280 | previousIndex == nextIndex && 281 | rows[nextIndex].isExtraAtTop == atTop && 282 | rows[nextIndex].extraHeight == dragHeight) { 283 | return; 284 | } 285 | 286 | bool needUpdate = false; 287 | 288 | if (previousIndex != null && previousIndex < rows.length) { 289 | if (rows[previousIndex].extraHeight > 0.1) { 290 | rows[previousIndex].extraHeight = 0.0; 291 | needUpdate = true; 292 | } 293 | } 294 | 295 | if (nextIndex < rows.length) { 296 | if (dragHeight != null && 297 | (absMinus(rows[nextIndex].extraHeight, dragHeight) > 0.1 || 298 | rows[nextIndex].isExtraAtTop != atTop)) { 299 | rows[nextIndex].extraHeight = dragHeight; 300 | rows[nextIndex].isExtraAtTop = atTop; 301 | needUpdate = true; 302 | } 303 | } 304 | 305 | if (needUpdate) { 306 | setState(() {}); 307 | } 308 | } 309 | } 310 | -------------------------------------------------------------------------------- /lib/draggable_list_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'my_draggable.dart'; 3 | //import 'tools.dart'; 4 | 5 | typedef void OnDragStarted(double height, double topPosition); 6 | typedef void OnDragFinish(int oldIndex, int newIndex); 7 | typedef bool CanAccept(int oldIndex, int newIndex); 8 | typedef bool CanDrag(int index); 9 | 10 | class DraggableListItem extends StatelessWidget { 11 | final Data data; 12 | final int index; 13 | 14 | final double draggedHeight; 15 | final CanDrag canDrag; 16 | final OnDragStarted onDragStarted; 17 | final VoidCallback onDragCompleted; 18 | final MyDragTargetAccept onAccept; 19 | final ValueChanged onMove; 20 | final VoidCallback cancelCallback; 21 | 22 | final double dragElevation; 23 | 24 | final Widget child; 25 | 26 | DraggableListItem({ 27 | Key key, 28 | this.data, 29 | this.index, 30 | this.canDrag, 31 | this.onDragStarted, 32 | this.onDragCompleted, 33 | this.onAccept, 34 | this.onMove, 35 | this.cancelCallback, 36 | this.draggedHeight, 37 | this.child, 38 | this.dragElevation, 39 | }) : super(key: key); 40 | 41 | @override 42 | Widget build(BuildContext context) { 43 | if (canDrag != null && !(canDrag(index))) { 44 | return _getListChild(context); 45 | } else { 46 | return LongPressMyDraggable( 47 | key: key, 48 | child: _getListChild(context), 49 | feedback: _getFeedback(index, context), 50 | data: data, 51 | onMove: onMove, 52 | onDragStarted: () { 53 | RenderBox it = context.findRenderObject() as RenderBox; 54 | onDragStarted(it.size.height, 55 | it.localToGlobal(it.semanticBounds.topCenter).dy); 56 | }, 57 | onDragCompleted: onDragCompleted, 58 | onMyDraggableCanceled: (_, _2) { 59 | cancelCallback(); 60 | }); 61 | } 62 | } 63 | 64 | Widget _getListChild(BuildContext context) { 65 | double nextTop = 0.0; 66 | double nextBot = 0.0; 67 | if (data.isExtraAtTop) { 68 | nextTop = data.extraHeight; 69 | } else { 70 | nextBot = data.extraHeight; 71 | } 72 | 73 | return MyDragTarget( 74 | builder: (BuildContext context, List candidateData, List rejectedData) { 75 | return Column( 76 | children: [ 77 | SizedBox( 78 | height: nextTop, 79 | ), 80 | child, 81 | SizedBox( 82 | height: nextBot, 83 | ), 84 | ], 85 | ); 86 | }, 87 | onAccept: onAccept, 88 | onWillAccept: (data) { 89 | return true; 90 | }, 91 | ); 92 | } 93 | 94 | Widget _getFeedback(int index, BuildContext context) { 95 | var maxWidth = MediaQuery.of(context).size.width; 96 | return ConstrainedBox( 97 | constraints: BoxConstraints(maxWidth: maxWidth), 98 | child: Transform( 99 | transform: Matrix4.rotationZ(0.0), 100 | alignment: FractionalOffset.bottomRight, 101 | child: Material( 102 | child: child, 103 | elevation: dragElevation, 104 | color: Colors.transparent, 105 | borderRadius: BorderRadius.zero, 106 | ), 107 | ), 108 | ); 109 | } 110 | } 111 | 112 | class Data { 113 | int index; 114 | double extraHeight; 115 | bool isExtraAtTop; 116 | 117 | Data( 118 | this.index, { 119 | this.isExtraAtTop = true, 120 | this.extraHeight = 0.0, 121 | }); 122 | } 123 | -------------------------------------------------------------------------------- /lib/my_draggable.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | import 'package:flutter/foundation.dart'; 6 | import 'package:flutter/gestures.dart'; 7 | import 'package:flutter/material.dart'; 8 | import 'package:flutter/rendering.dart'; 9 | //import 'package:flutter/services.dart'; 10 | 11 | /// Signature for determining whether the given data will be accepted by a [MyDragTarget]. 12 | /// 13 | /// Used by [MyDragTarget.onWillAccept]. 14 | typedef bool MyDragTargetWillAccept(T data); 15 | 16 | /// Signature for causing a [MyDragTarget] to accept the given data. 17 | /// 18 | /// Used by [MyDragTarget.onAccept]. 19 | typedef void MyDragTargetAccept(int index); 20 | 21 | /// Signature for building children of a [MyDragTarget]. 22 | /// 23 | /// The `candidateData` argument contains the list of drag data that is hovering 24 | /// over this [MyDragTarget] and that has passed [MyDragTarget.onWillAccept]. The 25 | /// `rejectedData` argument contains the list of drag data that is hovering over 26 | /// this [MyDragTarget] and that will not be accepted by the [MyDragTarget]. 27 | /// 28 | /// Used by [MyDragTarget.builder]. 29 | typedef Widget MyDragTargetBuilder( 30 | BuildContext context, List candidateData, List rejectedData); 31 | 32 | /// Signature for when a [MyDraggable] is dropped without being accepted by a [MyDragTarget]. 33 | /// 34 | /// Used by [MyDraggable.onMyDraggableCanceled]. 35 | typedef void MyDraggableCanceledCallback(Velocity velocity, Offset offset); 36 | 37 | /// Signature for when a [MyDraggable] leaves a [MyDragTarget]. 38 | /// 39 | /// Used by [MyDragTarget.onLeave]. 40 | typedef void MyDragTargetLeave(T data); 41 | 42 | /// Where the [MyDraggable] should be anchored during a drag. 43 | enum DragAnchor { 44 | /// Display the feedback anchored at the position of the original child. If 45 | /// feedback is identical to the child, then this means the feedback will 46 | /// exactly overlap the original child when the drag starts. 47 | child, 48 | 49 | /// Display the feedback anchored at the position of the touch that started 50 | /// the drag. If feedback is identical to the child, then this means the top 51 | /// left of the feedback will be under the finger when the drag starts. This 52 | /// will likely not exactly overlap the original child, e.g. if the child is 53 | /// big and the touch was not centered. This mode is useful when the feedback 54 | /// is transformed so as to move the feedback to the left by half its width, 55 | /// and up by half its width plus the height of the finger, since then it 56 | /// appears as if putting the finger down makes the touch feedback appear 57 | /// above the finger. (It feels weird for it to appear offset from the 58 | /// original child if it's anchored to the child and not the finger.) 59 | pointer, 60 | } 61 | 62 | /// A widget that can be dragged from to a [MyDragTarget]. 63 | /// 64 | /// When a MyDraggable widget recognizes the start of a drag gesture, it displays 65 | /// a [feedback] widget that tracks the user's finger across the screen. If the 66 | /// user lifts their finger while on top of a [MyDragTarget], that target is given 67 | /// the opportunity to accept the [data] carried by the MyDraggable. 68 | /// 69 | /// On multitouch devices, multiple drags can occur simultaneously because there 70 | /// can be multiple pointers in contact with the device at once. To limit the 71 | /// number of simultaneous drags, use the [maxSimultaneousDrags] property. The 72 | /// default is to allow an unlimited number of simultaneous drags. 73 | /// 74 | /// This widget displays [child] when zero drags are under way. If 75 | /// [childWhenDragging] is non-null, this widget instead displays 76 | /// [childWhenDragging] when one or more drags are underway. Otherwise, this 77 | /// widget always displays [child]. 78 | /// 79 | /// See also: 80 | /// 81 | /// * [MyDragTarget] 82 | /// * [LongPressMyDraggable] 83 | class MyDraggable extends StatefulWidget { 84 | /// Creates a widget that can be dragged to a [MyDragTarget]. 85 | /// 86 | /// The [child] and [feedback] arguments must not be null. If 87 | /// [maxSimultaneousDrags] is non-null, it must be non-negative. 88 | const MyDraggable( 89 | {Key key, 90 | @required this.child, 91 | @required this.feedback, 92 | this.data, 93 | this.childWhenDragging, 94 | this.feedbackOffset: Offset.zero, 95 | this.dragAnchor: DragAnchor.child, 96 | this.affinity, 97 | this.maxSimultaneousDrags, 98 | this.onDragStarted, 99 | this.onMyDraggableCanceled, 100 | this.onDragCompleted, 101 | this.onMove}) 102 | : assert(child != null), 103 | assert(feedback != null), 104 | assert(maxSimultaneousDrags == null || maxSimultaneousDrags >= 0), 105 | super(key: key); 106 | 107 | /// The data that will be dropped by this MyDraggable. 108 | final T data; 109 | 110 | /// The widget below this widget in the tree. 111 | /// 112 | /// This widget displays [child] when zero drags are under way. If 113 | /// [childWhenDragging] is non-null, this widget instead displays 114 | /// [childWhenDragging] when one or more drags are underway. Otherwise, this 115 | /// widget always displays [child]. 116 | /// 117 | /// The [feedback] widget is shown under the pointer when a drag is under way. 118 | /// 119 | /// To limit the number of simultaneous drags on multitouch devices, see 120 | /// [maxSimultaneousDrags]. 121 | /// 122 | /// {@macro flutter.widgets.child} 123 | final Widget child; 124 | 125 | /// The widget to display instead of [child] when one or more drags are under way. 126 | /// 127 | /// If this is null, then this widget will always display [child] (and so the 128 | /// drag source representation will not change while a drag is under 129 | /// way). 130 | /// 131 | /// The [feedback] widget is shown under the pointer when a drag is under way. 132 | /// 133 | /// To limit the number of simultaneous drags on multitouch devices, see 134 | /// [maxSimultaneousDrags]. 135 | final Widget childWhenDragging; 136 | 137 | /// The widget to show under the pointer when a drag is under way. 138 | /// 139 | /// See [child] and [childWhenDragging] for information about what is shown 140 | /// at the location of the [MyDraggable] itself when a drag is under way. 141 | final Widget feedback; 142 | 143 | /// The feedbackOffset can be used to set the hit test target point for the 144 | /// purposes of finding a drag target. It is especially useful if the feedback 145 | /// is transformed compared to the child. 146 | final Offset feedbackOffset; 147 | 148 | /// Where this widget should be anchored during a drag. 149 | final DragAnchor dragAnchor; 150 | 151 | /// Controls how this widget competes with other gestures to initiate a drag. 152 | /// 153 | /// If affinity is null, this widget initiates a drag as soon as it recognizes 154 | /// a tap down gesture, regardless of any directionality. If affinity is 155 | /// horizontal (or vertical), then this widget will compete with other 156 | /// horizontal (or vertical, respectively) gestures. 157 | /// 158 | /// For example, if this widget is placed in a vertically scrolling region and 159 | /// has horizontal affinity, pointer motion in the vertical direction will 160 | /// result in a scroll and pointer motion in the horizontal direction will 161 | /// result in a drag. Conversely, if the widget has a null or vertical 162 | /// affinity, pointer motion in any direction will result in a drag rather 163 | /// than in a scroll because the MyDraggable widget, being the more specific 164 | /// widget, will out-compete the [Scrollable] for vertical gestures. 165 | final Axis affinity; 166 | 167 | /// How many simultaneous drags to support. 168 | /// 169 | /// When null, no limit is applied. Set this to 1 if you want to only allow 170 | /// the drag source to have one item dragged at a time. Set this to 0 if you 171 | /// want to prevent the MyDraggable from actually being dragged. 172 | /// 173 | /// If you set this property to 1, consider supplying an "empty" widget for 174 | /// [childWhenDragging] to create the illusion of actually moving [child]. 175 | final int maxSimultaneousDrags; 176 | 177 | /// Called when the MyDraggable starts being dragged. 178 | final VoidCallback onDragStarted; 179 | 180 | /// Called when the MyDraggable is dropped without being accepted by a [MyDragTarget]. 181 | /// 182 | /// This function might be called after this widget has been removed from the 183 | /// tree. For example, if a drag was in progress when this widget was removed 184 | /// from the tree and the drag ended up being canceled, this callback will 185 | /// still be called. For this reason, implementations of this callback might 186 | /// need to check [State.mounted] to check whether the state receiving the 187 | /// callback is still in the tree. 188 | final MyDraggableCanceledCallback onMyDraggableCanceled; 189 | 190 | /// Called when the MyDraggable is dropped and accepted by a [MyDragTarget]. 191 | /// 192 | /// This function might be called after this widget has been removed from the 193 | /// tree. For example, if a drag was in progress when this widget was removed 194 | /// from the tree and the drag ended up completing, this callback will 195 | /// still be called. For this reason, implementations of this callback might 196 | /// need to check [State.mounted] to check whether the state receiving the 197 | /// callback is still in the tree. 198 | final VoidCallback onDragCompleted; 199 | 200 | final ValueChanged onMove; 201 | 202 | /// Creates a gesture recognizer that recognizes the start of the drag. 203 | /// 204 | /// Subclasses can override this function to customize when they start 205 | /// recognizing a drag. 206 | @protected 207 | GestureRecognizer createRecognizer(GestureMultiDragStartCallback onStart) { 208 | switch (affinity) { 209 | case Axis.horizontal: 210 | return new HorizontalMultiDragGestureRecognizer()..onStart = onStart; 211 | case Axis.vertical: 212 | return new VerticalMultiDragGestureRecognizer()..onStart = onStart; 213 | } 214 | return new ImmediateMultiDragGestureRecognizer()..onStart = onStart; 215 | } 216 | 217 | @override 218 | MyDraggableState createState() => new MyDraggableState(); 219 | } 220 | 221 | /// Makes its child MyDraggable starting from long press. 222 | class LongPressMyDraggable extends MyDraggable { 223 | /// Creates a widget that can be dragged starting from long press. 224 | /// 225 | /// The [child] and [feedback] arguments must not be null. If 226 | /// [maxSimultaneousDrags] is non-null, it must be non-negative. 227 | const LongPressMyDraggable({ 228 | Key key, 229 | @required Widget child, 230 | @required Widget feedback, 231 | T data, 232 | Widget childWhenDragging, 233 | Offset feedbackOffset: Offset.zero, 234 | DragAnchor dragAnchor: DragAnchor.child, 235 | int maxSimultaneousDrags, 236 | VoidCallback onDragStarted, 237 | MyDraggableCanceledCallback onMyDraggableCanceled, 238 | VoidCallback onDragCompleted, 239 | ValueChanged onMove, 240 | this.delay = const Duration(milliseconds: 500), 241 | }) : super( 242 | key: key, 243 | child: child, 244 | feedback: feedback, 245 | data: data, 246 | childWhenDragging: childWhenDragging, 247 | feedbackOffset: feedbackOffset, 248 | dragAnchor: dragAnchor, 249 | maxSimultaneousDrags: maxSimultaneousDrags, 250 | onDragStarted: onDragStarted, 251 | onMyDraggableCanceled: onMyDraggableCanceled, 252 | onDragCompleted: onDragCompleted, 253 | onMove: onMove); 254 | 255 | final Duration delay; 256 | 257 | @override 258 | DelayedMultiDragGestureRecognizer createRecognizer( 259 | GestureMultiDragStartCallback onStart) { 260 | return new DelayedMultiDragGestureRecognizer(delay: delay) 261 | ..onStart = (Offset position) { 262 | final Drag result = onStart(position); 263 | // if (result != null) HapticFeedback.selectionClick(); 264 | return result; 265 | }; 266 | } 267 | } 268 | 269 | class MyDraggableState extends State> { 270 | DragAvatar avatar; 271 | @override 272 | void initState() { 273 | super.initState(); 274 | _recognizer = widget.createRecognizer(_startDrag); 275 | // _recognizer = new DelayedMultiDragGestureRecognizer()..onStart = (Offset pos){_startDrag(pos);}; 276 | } 277 | 278 | @override 279 | void dispose() { 280 | _disposeRecognizerIfInactive(); 281 | super.dispose(); 282 | } 283 | 284 | // This gesture recognizer has an unusual lifetime. We want to support the use 285 | // case of removing the MyDraggable from the tree in the middle of a drag. That 286 | // means we need to keep this recognizer alive after this state object has 287 | // been disposed because it's the one listening to the pointer events that are 288 | // driving the drag. 289 | // 290 | // We achieve that by keeping count of the number of active drags and only 291 | // disposing the gesture recognizer after (a) this state object has been 292 | // disposed and (b) there are no more active drags. 293 | GestureRecognizer _recognizer; 294 | int _activeCount = 0; 295 | 296 | void _disposeRecognizerIfInactive() { 297 | if (_activeCount > 0) return; 298 | _recognizer.dispose(); 299 | _recognizer = null; 300 | } 301 | 302 | void _routePointer(PointerEvent event) { 303 | if (widget.maxSimultaneousDrags != null && 304 | _activeCount >= widget.maxSimultaneousDrags) return; 305 | _recognizer.addPointer(event); 306 | } 307 | 308 | DragAvatar _startDrag(Offset position) { 309 | if (widget.maxSimultaneousDrags != null && 310 | _activeCount >= widget.maxSimultaneousDrags) return null; 311 | Offset dragStartPoint; 312 | switch (widget.dragAnchor) { 313 | case DragAnchor.child: 314 | final RenderBox renderObject = context.findRenderObject(); 315 | dragStartPoint = renderObject.globalToLocal(position); 316 | break; 317 | case DragAnchor.pointer: 318 | dragStartPoint = Offset.zero; 319 | break; 320 | } 321 | setState(() { 322 | _activeCount += 1; 323 | }); 324 | final DragAvatar avatar = new DragAvatar( 325 | overlayState: Overlay.of(context, debugRequiredFor: widget), 326 | data: widget.data, 327 | initialPosition: position, 328 | dragStartPoint: dragStartPoint, 329 | feedback: widget.feedback, 330 | feedbackOffset: widget.feedbackOffset, 331 | onMove: widget.onMove, 332 | onDragEnd: (Velocity velocity, Offset offset, bool wasAccepted) { 333 | if (mounted) { 334 | setState(() { 335 | _activeCount -= 1; 336 | }); 337 | } else { 338 | _activeCount -= 1; 339 | _disposeRecognizerIfInactive(); 340 | } 341 | if (wasAccepted && widget.onDragCompleted != null) 342 | widget.onDragCompleted(); 343 | if (!wasAccepted && widget.onMyDraggableCanceled != null) 344 | widget.onMyDraggableCanceled(velocity, offset); 345 | }); 346 | //TODO mine 347 | this.avatar = avatar; 348 | if (widget.onDragStarted != null) widget.onDragStarted(); 349 | return avatar; 350 | } 351 | 352 | @override 353 | Widget build(BuildContext context) { 354 | assert(Overlay.of(context, debugRequiredFor: widget) != null); 355 | final bool canDrag = widget.maxSimultaneousDrags == null || 356 | _activeCount < widget.maxSimultaneousDrags; 357 | final bool showChild = 358 | _activeCount == 0 || widget.childWhenDragging == null; 359 | return new Listener( 360 | onPointerDown: canDrag ? _routePointer : null, 361 | child: showChild ? widget.child : widget.childWhenDragging); 362 | } 363 | } 364 | 365 | /// A widget that receives data when a [MyDraggable] widget is dropped. 366 | /// 367 | /// When a MyDraggable is dragged on top of a drag target, the drag target is 368 | /// asked whether it will accept the data the MyDraggable is carrying. If the user 369 | /// does drop the MyDraggable on top of the drag target (and the drag target has 370 | /// indicated that it will accept the MyDraggable's data), then the drag target is 371 | /// asked to accept the MyDraggable's data. 372 | /// 373 | /// See also: 374 | /// 375 | /// * [MyDraggable] 376 | /// * [LongPressMyDraggable] 377 | class MyDragTarget extends StatefulWidget { 378 | /// Creates a widget that receives drags. 379 | /// 380 | /// The [builder] argument must not be null. 381 | const MyDragTarget({ 382 | Key key, 383 | @required this.builder, 384 | this.onWillAccept, 385 | this.onAccept, 386 | this.onLeave, 387 | }) : super(key: key); 388 | 389 | /// Called to build the contents of this widget. 390 | /// 391 | /// The builder can build different widgets depending on what is being dragged 392 | /// into this drag target. 393 | final MyDragTargetBuilder builder; 394 | 395 | /// Called to determine whether this widget is interested in receiving a given 396 | /// piece of data being dragged over this drag target. 397 | /// 398 | /// Called when a piece of data enters the target. This will be followed by 399 | /// either [onAccept], if the data is dropped, or [onLeave], if the drag 400 | /// leaves the target. 401 | final MyDragTargetWillAccept onWillAccept; 402 | 403 | /// Called when an acceptable piece of data was dropped over this drag target. 404 | final MyDragTargetAccept onAccept; 405 | 406 | /// Called when a given piece of data being dragged over this target leaves 407 | /// the target. 408 | final MyDragTargetLeave onLeave; 409 | 410 | @override 411 | _MyDragTargetState createState() => new _MyDragTargetState(); 412 | } 413 | 414 | List _mapAvatarsToData(List> avatars) { 415 | return avatars.map((DragAvatar avatar) => avatar.data).toList(); 416 | } 417 | 418 | class _MyDragTargetState extends State> { 419 | final List> _candidateAvatars = >[]; 420 | final List> _rejectedAvatars = >[]; 421 | 422 | bool didEnter(DragAvatar avatar) { 423 | assert(!_candidateAvatars.contains(avatar)); 424 | assert(!_rejectedAvatars.contains(avatar)); 425 | if (avatar.data is T && 426 | (widget.onWillAccept == null || widget.onWillAccept(avatar.data))) { 427 | setState(() { 428 | _candidateAvatars.add(avatar); 429 | }); 430 | return true; 431 | } 432 | _rejectedAvatars.add(avatar); 433 | return false; 434 | } 435 | 436 | void didLeave(DragAvatar avatar) { 437 | assert(_candidateAvatars.contains(avatar) || 438 | _rejectedAvatars.contains(avatar)); 439 | if (!mounted) return; 440 | setState(() { 441 | _candidateAvatars.remove(avatar); 442 | _rejectedAvatars.remove(avatar); 443 | }); 444 | if (widget.onLeave != null) widget.onLeave(avatar.data); 445 | } 446 | 447 | void didDrop(DragAvatar avatar) { 448 | assert(_candidateAvatars.contains(avatar)); 449 | if (!mounted) return; 450 | setState(() { 451 | _candidateAvatars.remove(avatar); 452 | }); 453 | if (widget.onAccept != null) widget.onAccept(avatar.data); 454 | } 455 | 456 | @override 457 | Widget build(BuildContext context) { 458 | assert(widget.builder != null); 459 | return new MetaData( 460 | metaData: this, 461 | behavior: HitTestBehavior.translucent, 462 | child: widget.builder(context, _mapAvatarsToData(_candidateAvatars), 463 | _mapAvatarsToData(_rejectedAvatars))); 464 | } 465 | } 466 | 467 | enum _DragEndKind { dropped, canceled } 468 | typedef void _OnDragEnd(Velocity velocity, Offset offset, bool wasAccepted); 469 | 470 | // The lifetime of this object is a little dubious right now. Specifically, it 471 | // lives as long as the pointer is down. Arguably it should self-immolate if the 472 | // overlay goes away. _MyDraggableState has some delicate logic to continue 473 | // eeding this object pointer events even after it has been disposed. 474 | class DragAvatar extends Drag { 475 | DragAvatar({ 476 | @required this.overlayState, 477 | this.data, 478 | Offset initialPosition, 479 | this.dragStartPoint: Offset.zero, 480 | this.feedback, 481 | this.feedbackOffset: Offset.zero, 482 | this.onDragEnd, 483 | this.onMove, 484 | }) : assert(overlayState != null), 485 | assert(dragStartPoint != null), 486 | assert(feedbackOffset != null) { 487 | _entry = new OverlayEntry(builder: _build); 488 | overlayState.insert(_entry); 489 | _position = initialPosition; 490 | updateDrag(initialPosition); 491 | } 492 | 493 | final T data; 494 | final Offset dragStartPoint; 495 | final Widget feedback; 496 | final Offset feedbackOffset; 497 | final _OnDragEnd onDragEnd; 498 | final OverlayState overlayState; 499 | 500 | _MyDragTargetState _activeTarget; 501 | final List<_MyDragTargetState> _enteredTargets = <_MyDragTargetState>[]; 502 | Offset _position; 503 | Offset _lastOffset; 504 | OverlayEntry _entry; 505 | 506 | final ValueChanged onMove; 507 | 508 | double startClamp = -1.0; 509 | double endClamp = -1.0; 510 | 511 | void updateZero() { 512 | updateDrag(_position); 513 | } 514 | 515 | @override 516 | void update(DragUpdateDetails details) { 517 | _position += details.delta; 518 | updateDrag(_position); 519 | } 520 | 521 | @override 522 | void end(DragEndDetails details) { 523 | finishDrag(_DragEndKind.dropped, details.velocity); 524 | } 525 | 526 | @override 527 | void cancel() { 528 | finishDrag(_DragEndKind.canceled); 529 | } 530 | 531 | void updateDrag(Offset globalPosition) { 532 | _lastOffset = globalPosition - dragStartPoint; 533 | _entry.markNeedsBuild(); 534 | 535 | //TODO norbert it's here :) 536 | onMove(globalPosition); 537 | final HitTestResult result = new HitTestResult(); 538 | WidgetsBinding.instance.hitTest(result, globalPosition + feedbackOffset); 539 | 540 | final List<_MyDragTargetState> targets = 541 | _getMyDragTargets(result.path).toList(); 542 | 543 | bool listsMatch = false; 544 | if (targets.length >= _enteredTargets.length && 545 | _enteredTargets.isNotEmpty) { 546 | listsMatch = true; 547 | final Iterator<_MyDragTargetState> iterator = targets.iterator; 548 | for (int i = 0; i < _enteredTargets.length; i += 1) { 549 | iterator.moveNext(); 550 | if (iterator.current != _enteredTargets[i]) { 551 | listsMatch = false; 552 | break; 553 | } 554 | } 555 | } 556 | 557 | // If everything's the same, bail early. 558 | if (listsMatch) return; 559 | 560 | // Leave old targets. 561 | _leaveAllEntered(); 562 | 563 | // Enter new targets. 564 | final _MyDragTargetState newTarget = 565 | targets.firstWhere((_MyDragTargetState target) { 566 | _enteredTargets.add(target); 567 | return target.didEnter(this); 568 | }, orElse: _null); 569 | 570 | _activeTarget = newTarget; 571 | } 572 | 573 | static Null _null() => null; 574 | 575 | Iterable<_MyDragTargetState> _getMyDragTargets( 576 | List path) sync* { 577 | // Look for the RenderBoxes that corresponds to the hit target (the hit target 578 | // widgets build RenderMetaData boxes for us for this purpose). 579 | for (HitTestEntry entry in path) { 580 | if (entry.target is RenderMetaData) { 581 | final RenderMetaData renderMetaData = entry.target; 582 | if (renderMetaData.metaData is _MyDragTargetState) 583 | yield renderMetaData.metaData; 584 | } 585 | } 586 | } 587 | 588 | void _leaveAllEntered() { 589 | for (int i = 0; i < _enteredTargets.length; i += 1) 590 | _enteredTargets[i].didLeave(this); 591 | _enteredTargets.clear(); 592 | } 593 | 594 | void finishDrag(_DragEndKind endKind, [Velocity velocity]) { 595 | bool wasAccepted = false; 596 | if (endKind == _DragEndKind.dropped && _activeTarget != null) { 597 | _activeTarget.didDrop(this); 598 | wasAccepted = true; 599 | _enteredTargets.remove(_activeTarget); 600 | } 601 | _leaveAllEntered(); 602 | _activeTarget = null; 603 | _entry.remove(); 604 | _entry = null; 605 | // TODO(ianh): consider passing _entry as well so the client can perform an animation. 606 | if (onDragEnd != null) 607 | onDragEnd(velocity ?? Velocity.zero, _lastOffset, wasAccepted); 608 | } 609 | 610 | Widget _build(BuildContext context) { 611 | final RenderBox box = overlayState.context.findRenderObject(); 612 | final Offset overlayTopLeft = box.localToGlobal(Offset.zero); 613 | return new Positioned( 614 | //left: _lastOffset.dx - overlayTopLeft.dx, 615 | left: 0.0, 616 | top: _lastOffset.dy - overlayTopLeft.dy, 617 | child: new IgnorePointer(child: feedback)); 618 | } 619 | } 620 | -------------------------------------------------------------------------------- /lib/tools.dart: -------------------------------------------------------------------------------- 1 | double absMinus(double a, double b) { 2 | double result = a - b; 3 | if (result < 0) { 4 | return -result; 5 | } else { 6 | return result; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://www.dartlang.org/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.0.8" 11 | boolean_selector: 12 | dependency: transitive 13 | description: 14 | name: boolean_selector 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "1.0.4" 18 | charcode: 19 | dependency: transitive 20 | description: 21 | name: charcode 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.1.2" 25 | collection: 26 | dependency: transitive 27 | description: 28 | name: collection 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.14.11" 32 | flutter: 33 | dependency: "direct main" 34 | description: flutter 35 | source: sdk 36 | version: "0.0.0" 37 | flutter_test: 38 | dependency: "direct dev" 39 | description: flutter 40 | source: sdk 41 | version: "0.0.0" 42 | matcher: 43 | dependency: transitive 44 | description: 45 | name: matcher 46 | url: "https://pub.dartlang.org" 47 | source: hosted 48 | version: "0.12.3+1" 49 | meta: 50 | dependency: transitive 51 | description: 52 | name: meta 53 | url: "https://pub.dartlang.org" 54 | source: hosted 55 | version: "1.1.6" 56 | path: 57 | dependency: transitive 58 | description: 59 | name: path 60 | url: "https://pub.dartlang.org" 61 | source: hosted 62 | version: "1.6.2" 63 | quiver: 64 | dependency: transitive 65 | description: 66 | name: quiver 67 | url: "https://pub.dartlang.org" 68 | source: hosted 69 | version: "2.0.1" 70 | sky_engine: 71 | dependency: transitive 72 | description: flutter 73 | source: sdk 74 | version: "0.0.99" 75 | source_span: 76 | dependency: transitive 77 | description: 78 | name: source_span 79 | url: "https://pub.dartlang.org" 80 | source: hosted 81 | version: "1.4.1" 82 | stack_trace: 83 | dependency: transitive 84 | description: 85 | name: stack_trace 86 | url: "https://pub.dartlang.org" 87 | source: hosted 88 | version: "1.9.3" 89 | stream_channel: 90 | dependency: transitive 91 | description: 92 | name: stream_channel 93 | url: "https://pub.dartlang.org" 94 | source: hosted 95 | version: "1.6.8" 96 | string_scanner: 97 | dependency: transitive 98 | description: 99 | name: string_scanner 100 | url: "https://pub.dartlang.org" 101 | source: hosted 102 | version: "1.0.4" 103 | term_glyph: 104 | dependency: transitive 105 | description: 106 | name: term_glyph 107 | url: "https://pub.dartlang.org" 108 | source: hosted 109 | version: "1.0.1" 110 | test_api: 111 | dependency: transitive 112 | description: 113 | name: test_api 114 | url: "https://pub.dartlang.org" 115 | source: hosted 116 | version: "0.2.1" 117 | typed_data: 118 | dependency: transitive 119 | description: 120 | name: typed_data 121 | url: "https://pub.dartlang.org" 122 | source: hosted 123 | version: "1.1.6" 124 | vector_math: 125 | dependency: transitive 126 | description: 127 | name: vector_math 128 | url: "https://pub.dartlang.org" 129 | source: hosted 130 | version: "2.0.8" 131 | sdks: 132 | dart: ">=2.0.0 <3.0.0" 133 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: draggable_flutter_list 2 | description: a flutter list view which can drag & move item to change order. 3 | version: 1.0.3 4 | author: Realank 5 | homepage: https://github.com/Realank/draggable_flutter_list 6 | 7 | environment: 8 | sdk: ">=2.0.0-dev.28.0 <3.0.0" 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | 14 | dev_dependencies: 15 | flutter_test: 16 | sdk: flutter 17 | 18 | # For information on the generic Dart part of this file, see the 19 | # following page: https://www.dartlang.org/tools/pub/pubspec 20 | 21 | # The following section is specific to Flutter. 22 | flutter: 23 | 24 | # To add assets to your package, add an assets section, like this: 25 | # assets: 26 | # - images/a_dot_burr.jpeg 27 | # - images/a_dot_ham.jpeg 28 | # 29 | # For details regarding assets in packages, see 30 | # https://flutter.io/assets-and-images/#from-packages 31 | # 32 | # An image asset can refer to one or more resolution-specific "variants", see 33 | # https://flutter.io/assets-and-images/#resolution-aware. 34 | 35 | # To add custom fonts to your package, add a fonts section here, 36 | # in this "flutter" section. Each entry in this list should have a 37 | # "family" key with the font family name, and a "fonts" key with a 38 | # list giving the asset and other descriptors for the font. For 39 | # example: 40 | # fonts: 41 | # - family: Schyler 42 | # fonts: 43 | # - asset: fonts/Schyler-Regular.ttf 44 | # - asset: fonts/Schyler-Italic.ttf 45 | # style: italic 46 | # - family: Trajan Pro 47 | # fonts: 48 | # - asset: fonts/TrajanPro.ttf 49 | # - asset: fonts/TrajanPro_Bold.ttf 50 | # weight: 700 51 | # 52 | # For details regarding fonts in packages, see 53 | # https://flutter.io/custom-fonts/#from-packages 54 | -------------------------------------------------------------------------------- /screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Realank/draggable_flutter_list/d91801cab7d398aff37af13a3dc207ddaa5f02c9/screenshot.jpg -------------------------------------------------------------------------------- /test/draggable_flutter_list_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/gestures.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:draggable_flutter_list/draggable_flutter_list.dart'; 5 | import 'package:flutter_test/flutter_test.dart'; 6 | 7 | void main() { 8 | testSwap(); 9 | } 10 | 11 | void testSwap() { 12 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 13 | List items = [ 14 | '0', 15 | '1', 16 | '2', 17 | '3', 18 | '4', 19 | '5', 20 | '6', 21 | '7', 22 | '8', 23 | '9', 24 | '10', 25 | '11', 26 | '12', 27 | '13', 28 | '14', 29 | '15', 30 | ]; 31 | 32 | double tileSize = 64.0; 33 | 34 | StatefulBuilder app = new StatefulBuilder( 35 | builder: (BuildContext context, StateSetter setState) { 36 | return new MaterialApp( 37 | home: new DragAndDropList( 38 | items.length, 39 | itemBuilder: (BuildContext context, index) { 40 | return new SizedBox( 41 | height: tileSize, 42 | child: new Card( 43 | child: new ListTile( 44 | title: new Text(items[index]), 45 | ), 46 | ), 47 | ); 48 | }, 49 | onDragFinish: (before, after) { 50 | setState(() { 51 | String data = items[before]; 52 | items.removeAt(before); 53 | items.insert(after, data); 54 | }); 55 | }, 56 | canBeDraggedTo: (one, two) => true, 57 | ), 58 | ); 59 | }, 60 | ); 61 | // Build our app and trigger a frame. 62 | await tester.pumpWidget(app); 63 | 64 | // Verify that our counter starts at 0. 65 | expect(find.text('0'), findsOneWidget); 66 | expect(find.text('1'), findsOneWidget); 67 | expect(find.text('2'), findsOneWidget); 68 | expect(find.text('3'), findsOneWidget); 69 | expect(find.text('4'), findsOneWidget); 70 | expect(find.text('5'), findsOneWidget); 71 | expect(find.text('6'), findsOneWidget); 72 | expect(find.text('7'), findsOneWidget); 73 | expect(find.text('8'), findsOneWidget); 74 | expect(find.text('16'), findsNothing); 75 | 76 | await longPressDrag(find.text('0'), tester, new Offset(0.0, tileSize * 5)); 77 | 78 | print(items); 79 | expect(items[0], '1'); 80 | expect(items[1], '2'); 81 | expect(items[2], '3'); 82 | expect(items[4], '5'); 83 | expect(items[5], '0'); 84 | expect(items[6], '6'); 85 | expect(items[7], '7'); 86 | expect(items[8], '8'); 87 | 88 | await longPressDragOffset( 89 | new Offset(100.0, tileSize * 5), tester, new Offset(0.0, tileSize * 2)); 90 | expect(items[0], '1'); 91 | expect(items[1], '2'); 92 | expect(items[2], '3'); 93 | expect(items[3], '4'); 94 | expect(items[4], '0'); 95 | expect(items[5], '6'); 96 | expect(items[6], '7'); 97 | expect(items[7], '5'); 98 | print(items); 99 | 100 | await longPressDragOffset( 101 | new Offset(100.0, tileSize * 6), tester, new Offset(0.0, tileSize * 2)); 102 | expect(items[0], '1'); 103 | expect(items[1], '2'); 104 | expect(items[2], '3'); 105 | expect(items[3], '4'); 106 | expect(items[4], '0'); 107 | expect(items[5], '7'); 108 | expect(items[6], '5'); 109 | expect(items[7], '6'); 110 | print(items); 111 | 112 | await longPressDragOffset( 113 | new Offset(100.0, tileSize * 5), tester, new Offset(0.0, tileSize * 2)); 114 | expect(items[0], '1'); 115 | expect(items[1], '2'); 116 | expect(items[2], '3'); 117 | expect(items[3], '4'); 118 | expect(items[4], '7'); 119 | expect(items[5], '5'); 120 | expect(items[6], '6'); 121 | expect(items[7], '0'); 122 | print(items); 123 | }); 124 | } 125 | 126 | /// Dispatch a pointer down / pointer up sequence at the given location with 127 | /// a delay of [kLongPressTimeout] + [kPressTimeout] between the two events. 128 | Future longPressDrag(Finder finder, WidgetTester tester, Offset dragOffset) { 129 | Offset location = tester.getCenter(finder); 130 | return TestAsyncUtils.guard(() async { 131 | final TestGesture gesture = await tester.startGesture(location); 132 | await tester.pump(kLongPressTimeout + kPressTimeout); 133 | await gesture.moveBy(dragOffset); 134 | await tester.pump(kLongPressTimeout + kPressTimeout); 135 | await gesture.up(); 136 | return null; 137 | }); 138 | } 139 | 140 | /// Dispatch a pointer down / pointer up sequence at the given location with 141 | /// a delay of [kLongPressTimeout] + [kPressTimeout] between the two events. 142 | Future longPressDragOffset(Offset location, WidgetTester tester, Offset dragOffset) { 143 | return TestAsyncUtils.guard(() async { 144 | final TestGesture gesture = await tester.startGesture(location); 145 | await tester.pump(kLongPressTimeout + kPressTimeout); 146 | await gesture.moveBy(dragOffset, timeStamp: new Duration(seconds: 2)); 147 | await tester.pump(kLongPressTimeout + kPressTimeout); 148 | await gesture.up(); 149 | return null; 150 | }); 151 | } 152 | --------------------------------------------------------------------------------