diff --git a/.github/workflows/quality_checks.yaml b/.github/workflows/quality_checks.yaml new file mode 100644 index 0000000..b3f8b93 --- /dev/null +++ b/.github/workflows/quality_checks.yaml @@ -0,0 +1,43 @@ +name: Quality control checks + +on: + push: + branches: + - master + pull_request: + branches: + - '*' + workflow_dispatch: + branches: + - '*' + +jobs: + analyze: + runs-on: ubuntu-latest + + steps: + - name: Checkout Repo + uses: actions/checkout@v2 + + - uses: subosito/flutter-action@v1 + with: + channel: 'stable' + + - run: flutter pub get + + - run: flutter analyze + + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout Repo + uses: actions/checkout@v2 + + - uses: subosito/flutter-action@v1 + with: + channel: 'stable' + + - run: flutter pub get + + - run: flutter test \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index c92975e..b320b42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,52 @@ # Changelog +[0.4.2] - 26 November 2024 + +* Add key getter to drag and drop list interface (thanks [@juancdominici](https://github.com/juancdominici)) + +[0.4.1] - 12 June 2024 + +* Add key to DragAndDropItem and DragAndDropList (thanks [@saileshbro](https://github.com/saileshbro)) + +[0.4.0] - 11 June 2024 + +* Update to be compatible with Flutter 3.22.0 (thanks [@dmitry-comet](https://github.com/dmitry-comet)) + +[0.3.3] - 4 August 2022 + +* Update to flutter 3 (thanks [@mauriceraguseinit](https://github.com/mauriceraguseinit)) + +[0.3.2+2] - 21 October 2021 + +* Replace flutter deprecated elements + +[0.3.2+1] - 21 October 2021 + +* Fix last list target for horizontal lists (thanks [@nvloc120](https://github.com/nvloc120)). +* Add ability to remove top padding when there is a widget before the DragAndDropLists (See [flutter/flutter#14842](https://github.com/flutter/flutter/issues/14842), thanks [@aliasgarlabs](https://github.com/aliasgarlabs)) + +## [0.3.2] - 20 April 2021 + +* Add optional feedback widget to items (thanks [@svoza10](https://github.com/svoza10)). + +## [0.3.1] - 15 April 2021 + +* Fix scrolling in wrong direction when text direction is right-to-left. +* Fix drag-and-drop feedback widget alignment when text direction is right-to-left. + +## [0.3.0+1] - 2 April 2021 + +* Fix null crash and wrong drag handle used (thanks [@vbuberen](https://github.com/vbuberen)). + +## [0.3.0] - 30 March 2021 + +* DragHandle moved to own widget. To create any drag handle, use the new properties `listDragHandle` and `itemDragHandle` in `DragAndDropLists`. +* Support null safety, see [details on migration](https://dart.dev/null-safety/migration-guide). + ## [0.2.10] - 14 December 2020 -* Bug fix where `listDecorationWhileDragging` wasn't always being applied -* Allow DragAndDropLists to be contained in an external ListView when `disableScrolling` is set to `true` +* Bug fix where `listDecorationWhileDragging` wasn't always being applied. +* Allow DragAndDropLists to be contained in an external ListView when `disableScrolling` is set to `true`. ## [0.2.9+2] - 17 November 2020 diff --git a/README.md b/README.md index e1f6362..31f938f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # drag\_and\_drop\_lists Two-level drag and drop reorderable lists. +[!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/pbpbpb) + ## Features - Reorder elements between multiple lists - Reorder lists @@ -13,33 +15,22 @@ Two-level drag and drop reorderable lists. - Easy to extend with custom layouts

- - - - - - - +Basic list +Drag and drop expansion tiles +Drag and drop using drag handles +Drag and drop new lists and new items +Drag and drop horizontal lists +Drag and drop list tiles +Drag and drop lists using slivers

-## Known Issues -There is currently (as of flutter v. 1.24.0-1.0.pre) an issue only on web where dragging an item with some descendant that includes an InkWell widget with an onTap method will throw an exception. This includes having a ListTile with an onTap method defined. - -This seems to be resolved by using a GestureDetector and its onTap method instead of the InkWell. - -See the following issues: -* [#14](https://github.com/philip-brink/DragAndDropLists/issues/14) -* [Flutter #69774](https://github.com/flutter/flutter/issues/69774) -* [Flutter #67044](https://github.com/flutter/flutter/issues/67044) -* [Flutter #66887](https://github.com/flutter/flutter/issues/66887) - ## Usage To use this plugin, add `drag_and_drop_lists` as a [dependency in your pubspec.yaml file.](https://flutter.dev/docs/development/packages-and-plugins/using-packages) For example: ``` dependencies: - drag_and_drop_lists: ^0.2.1 + drag_and_drop_lists: ^0.4.0 ``` Now in your Dart code, you can use: `import 'package:drag_and_drop_lists/drag_and_drop_lists.dart';` diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..773ccf5 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,28 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/tools/linter-rules. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/tools/analysis \ No newline at end of file diff --git a/example/.gitignore b/example/.gitignore index 1ba9c33..a8e938c 100644 --- a/example/.gitignore +++ b/example/.gitignore @@ -8,6 +8,7 @@ .buildlog/ .history .svn/ +migrate_working_dir/ # IntelliJ related *.iml @@ -22,6 +23,7 @@ # Flutter/Dart/Pub related **/doc/api/ +**/ios/Flutter/.last_build_id .dart_tool/ .flutter-plugins .flutter-plugins-dependencies @@ -39,5 +41,7 @@ app.*.symbols # Obfuscation related app.*.map.json -# Exceptions to above rules. -!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/example/.metadata b/example/.metadata index 6ac2303..50331c0 100644 --- a/example/.metadata +++ b/example/.metadata @@ -4,7 +4,27 @@ # This file should be version controlled and should not be manually edited. version: - revision: f7a6a7906be96d2288f5d63a5a54c515a6e987fe - channel: stable + revision: "nixpkgs000000000000000000000000000000000" + channel: "stable" project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: nixpkgs000000000000000000000000000000000 + base_revision: nixpkgs000000000000000000000000000000000 + - platform: web + create_revision: nixpkgs000000000000000000000000000000000 + base_revision: nixpkgs000000000000000000000000000000000 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/example/README.md b/example/README.md index a135626..2b3fce4 100644 --- a/example/README.md +++ b/example/README.md @@ -8,9 +8,9 @@ This project is a starting point for a Flutter application. A few resources to get you started if this is your first Flutter project: -- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) -For help getting started with Flutter, view our -[online documentation](https://flutter.dev/docs), which offers tutorials, +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, samples, guidance on mobile development, and a full API reference. diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml new file mode 100644 index 0000000..61b6c4d --- /dev/null +++ b/example/analysis_options.yaml @@ -0,0 +1,29 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/example/android/.gitignore b/example/android/.gitignore index bc2100d..6f56801 100644 --- a/example/android/.gitignore +++ b/example/android/.gitignore @@ -5,3 +5,9 @@ gradle-wrapper.jar /gradlew.bat /local.properties GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 320be22..8359694 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -1,3 +1,9 @@ +plugins { + id "com.android.application" + id "kotlin-android" + id "dev.flutter.flutter-gradle-plugin" +} + def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { @@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) { } } -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { flutterVersionCode = '1' @@ -21,26 +22,31 @@ if (flutterVersionName == null) { flutterVersionName = '1.0' } -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - android { - compileSdkVersion 28 + namespace = "com.example.example" + compileSdkVersion flutter.compileSdkVersion + ndkVersion flutter.ndkVersion - sourceSets { - main.java.srcDirs += 'src/main/kotlin' + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 } - lintOptions { - disable 'InvalidPackage' + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' } defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.example.example" - minSdkVersion 16 - targetSdkVersion 28 + // You can update the following values to match your application needs. + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. + minSdkVersion flutter.minSdkVersion + targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName } @@ -56,8 +62,4 @@ android { flutter { source '../..' -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -} +} \ No newline at end of file diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml index c208884..399f698 100644 --- a/example/android/app/src/debug/AndroidManifest.xml +++ b/example/android/app/src/debug/AndroidManifest.xml @@ -1,6 +1,6 @@ - - diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 55ca830..e76d500 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -1,16 +1,11 @@ - - - + - - diff --git a/example/android/app/src/main/res/drawable-v21/launch_background.xml b/example/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/example/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/example/android/app/src/main/res/values-night/styles.xml b/example/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..06952be --- /dev/null +++ b/example/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/example/android/app/src/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml index 1f83a33..cb1ef88 100644 --- a/example/android/app/src/main/res/values/styles.xml +++ b/example/android/app/src/main/res/values/styles.xml @@ -1,18 +1,18 @@ - - - diff --git a/example/android/app/src/profile/AndroidManifest.xml b/example/android/app/src/profile/AndroidManifest.xml index c208884..399f698 100644 --- a/example/android/app/src/profile/AndroidManifest.xml +++ b/example/android/app/src/profile/AndroidManifest.xml @@ -1,6 +1,6 @@ - - diff --git a/example/android/build.gradle b/example/android/build.gradle index 3100ad2..bc157bd 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,20 +1,7 @@ -buildscript { - ext.kotlin_version = '1.3.50' - repositories { - google() - jcenter() - } - - dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - allprojects { repositories { google() - jcenter() + mavenCentral() } } @@ -26,6 +13,6 @@ subprojects { project.evaluationDependsOn(':app') } -task clean(type: Delete) { +tasks.register("clean", Delete) { delete rootProject.buildDir } diff --git a/example/android/gradle.properties b/example/android/gradle.properties index 38c8d45..94adc3a 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -1,4 +1,3 @@ org.gradle.jvmargs=-Xmx1536M -android.enableR8=true android.useAndroidX=true android.enableJetifier=true diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index 296b146..fb5eb59 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip diff --git a/example/android/settings.gradle b/example/android/settings.gradle index 5a2f14f..9fec99b 100644 --- a/example/android/settings.gradle +++ b/example/android/settings.gradle @@ -1,15 +1,25 @@ -include ':app' +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } + repositories { + google() + mavenCentral() + gradlePluginPortal() + } } -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "8.3.1" apply false + id "org.jetbrains.kotlin.android" version "1.8.22" apply false } + +include ":app" \ No newline at end of file diff --git a/example/ios/.gitignore b/example/ios/.gitignore index e96ef60..7a7f987 100644 --- a/example/ios/.gitignore +++ b/example/ios/.gitignore @@ -1,3 +1,4 @@ +**/dgph *.mode1v3 *.mode2v3 *.moved-aside @@ -18,6 +19,7 @@ Flutter/App.framework Flutter/Flutter.framework Flutter/Flutter.podspec Flutter/Generated.xcconfig +Flutter/ephemeral/ Flutter/app.flx Flutter/app.zip Flutter/flutter_assets/ diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist index 6b4c0f7..8d4492f 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -3,7 +3,7 @@ CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) + en CFBundleExecutable App CFBundleIdentifier @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 8.0 + 9.0 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 5f1bf4c..6edd238 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 50; objects = { /* Begin PBXBuildFile section */ @@ -90,7 +90,6 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 97C147021CF9000F007C117D /* Info.plist */, - 97C146F11CF9000F007C117D /* Supporting Files */, 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, @@ -99,13 +98,6 @@ path = Runner; sourceTree = ""; }; - 97C146F11CF9000F007C117D /* Supporting Files */ = { - isa = PBXGroup; - children = ( - ); - name = "Supporting Files"; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -135,7 +127,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1020; + LastUpgradeCheck = 1300; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -241,7 +233,6 @@ /* Begin XCBuildConfiguration section */ 249021D3217E4FDB00AE95B9 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -281,7 +272,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -298,15 +289,10 @@ CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( + LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)/Flutter", + "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = com.example.example; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -318,7 +304,6 @@ }; 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -364,7 +349,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -374,7 +359,6 @@ }; 97C147041CF9000F007C117D /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -414,11 +398,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -432,15 +417,10 @@ CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( + LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)/Flutter", + "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = com.example.example; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -459,15 +439,10 @@ CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( + LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)/Flutter", + "@executable_path/Frameworks", ); PRODUCT_BUNDLE_IDENTIFIER = com.example.example; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 1d526a1..919434a 100644 --- a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index a28140c..c87d15a 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ - - - - + + - - CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Example CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -41,5 +43,7 @@ UIViewControllerBasedStatusBarAppearance + CADisableMinimumFrameDurationOnPhone + diff --git a/example/lib/basic_example.dart b/example/lib/basic_example.dart index c341dbe..b98b50c 100644 --- a/example/lib/basic_example.dart +++ b/example/lib/basic_example.dart @@ -1,19 +1,16 @@ -import 'package:drag_and_drop_lists/drag_and_drop_item.dart'; import 'package:drag_and_drop_lists/drag_and_drop_lists.dart'; -import 'package:example/navigation_drawer.dart'; +import 'package:example/custom_navigation_drawer.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; class BasicExample extends StatefulWidget { - BasicExample({Key key, this.title}) : super(key: key); - final String title; + const BasicExample({Key? key}) : super(key: key); @override - _BasicExample createState() => _BasicExample(); + State createState() => _BasicExample(); } class _BasicExample extends State { - List _contents; + late List _contents; @override void initState() { @@ -23,7 +20,7 @@ class _BasicExample extends State { return DragAndDropList( header: Row( children: [ - Expanded( + const Expanded( flex: 1, child: Divider(), ), @@ -31,7 +28,7 @@ class _BasicExample extends State { padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), child: Text('Header $index'), ), - Expanded( + const Expanded( flex: 1, child: Divider(), ), @@ -56,9 +53,9 @@ class _BasicExample extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text('Basic'), + title: const Text('Basic'), ), - drawer: NavigationDrawer(), + drawer: const CustomNavigationDrawer(), body: DragAndDropLists( children: _contents, onItemReorder: _onItemReorder, diff --git a/example/lib/navigation_drawer.dart b/example/lib/custom_navigation_drawer.dart similarity index 67% rename from example/lib/navigation_drawer.dart rename to example/lib/custom_navigation_drawer.dart index 240d074..61e55d7 100644 --- a/example/lib/navigation_drawer.dart +++ b/example/lib/custom_navigation_drawer.dart @@ -1,31 +1,32 @@ import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; -class NavigationDrawer extends StatelessWidget { +class CustomNavigationDrawer extends StatelessWidget { + const CustomNavigationDrawer({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return Drawer( child: ListView( children: [ ListTile( - title: Text('Basic'), - leading: Icon(Icons.list), + title: const Text('Basic'), + leading: const Icon(Icons.list), onTap: () { Navigator.of(context).pop(); Navigator.of(context).pushReplacementNamed('/'); }, ), ListTile( - title: Text('List Tiles'), - leading: Icon(Icons.view_list), + title: const Text('List Tiles'), + leading: const Icon(Icons.view_list), onTap: () { Navigator.of(context).pop(); Navigator.of(context).pushReplacementNamed('/list_tile_example'); }, ), ListTile( - title: Text('Expansion Tiles'), - leading: Icon(Icons.keyboard_arrow_down), + title: const Text('Expansion Tiles'), + leading: const Icon(Icons.keyboard_arrow_down), onTap: () { Navigator.of(context).pop(); Navigator.of(context) @@ -33,16 +34,16 @@ class NavigationDrawer extends StatelessWidget { }, ), ListTile( - title: Text('Slivers'), - leading: Icon(Icons.assignment), + title: const Text('Slivers'), + leading: const Icon(Icons.assignment), onTap: () { Navigator.of(context).pop(); Navigator.of(context).pushReplacementNamed('/sliver_example'); }, ), ListTile( - title: Text('Drag Into List'), - leading: Icon(Icons.add), + title: const Text('Drag Into List'), + leading: const Icon(Icons.add), onTap: () { Navigator.of(context).pop(); Navigator.of(context) @@ -50,24 +51,24 @@ class NavigationDrawer extends StatelessWidget { }, ), ListTile( - title: Text('Horizontal'), - leading: Icon(Icons.swap_horiz), + title: const Text('Horizontal'), + leading: const Icon(Icons.swap_horiz), onTap: () { Navigator.of(context).pop(); Navigator.of(context).pushReplacementNamed('/horizontal_example'); }, ), ListTile( - title: Text('Fixed Items'), - leading: Icon(Icons.block), + title: const Text('Fixed Items'), + leading: const Icon(Icons.block), onTap: () { Navigator.of(context).pop(); Navigator.of(context).pushReplacementNamed('/fixed_example'); }, ), ListTile( - title: Text('Drag Handle'), - leading: Icon(Icons.drag_handle), + title: const Text('Drag Handle'), + leading: const Icon(Icons.drag_handle), onTap: () { Navigator.of(context).pop(); Navigator.of(context) diff --git a/example/lib/drag_handle_example.dart b/example/lib/drag_handle_example.dart index aee6deb..84442bb 100644 --- a/example/lib/drag_handle_example.dart +++ b/example/lib/drag_handle_example.dart @@ -1,19 +1,16 @@ import 'package:drag_and_drop_lists/drag_and_drop_lists.dart'; -import 'package:example/navigation_drawer.dart'; +import 'package:example/custom_navigation_drawer.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; -import 'package:flutter/widgets.dart'; class DragHandleExample extends StatefulWidget { - DragHandleExample({Key key, this.title}) : super(key: key); - final String title; + const DragHandleExample({Key? key}) : super(key: key); @override - _DragHandleExample createState() => _DragHandleExample(); + State createState() => _DragHandleExample(); } class _DragHandleExample extends State { - List _contents; + late List _contents; @override void initState() { @@ -26,10 +23,10 @@ class _DragHandleExample extends State { Row( children: [ Padding( - padding: EdgeInsets.only(left: 8, bottom: 4), + padding: const EdgeInsets.only(left: 8, bottom: 4), child: Text( 'Header $index', - style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16), + style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16), ), ), ], @@ -41,7 +38,7 @@ class _DragHandleExample extends State { child: Row( children: [ Padding( - padding: EdgeInsets.symmetric(vertical: 8, horizontal: 12), + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), child: Text( 'Sub $index.1', ), @@ -53,7 +50,7 @@ class _DragHandleExample extends State { child: Row( children: [ Padding( - padding: EdgeInsets.symmetric(vertical: 8, horizontal: 12), + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), child: Text( 'Sub $index.2', ), @@ -65,7 +62,7 @@ class _DragHandleExample extends State { child: Row( children: [ Padding( - padding: EdgeInsets.symmetric(vertical: 8, horizontal: 12), + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), child: Text( 'Sub $index.3', ), @@ -80,19 +77,19 @@ class _DragHandleExample extends State { @override Widget build(BuildContext context) { - var backgroundColor = Color.fromARGB(255, 243, 242, 248); + var backgroundColor = const Color.fromARGB(255, 243, 242, 248); return Scaffold( backgroundColor: backgroundColor, appBar: AppBar( - title: Text('Drag Handle'), + title: const Text('Drag Handle'), ), - drawer: NavigationDrawer(), + drawer: const CustomNavigationDrawer(), body: DragAndDropLists( children: _contents, onItemReorder: _onItemReorder, onListReorder: _onListReorder, - listPadding: EdgeInsets.symmetric(horizontal: 15, vertical: 10), + listPadding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10), itemDivider: Divider( thickness: 2, height: 2, @@ -105,22 +102,34 @@ class _DragHandleExample extends State { color: Colors.grey.withOpacity(0.5), spreadRadius: 2, blurRadius: 3, - offset: Offset(0, 0), // changes position of shadow + offset: const Offset(0, 0), // changes position of shadow ), ], ), listInnerDecoration: BoxDecoration( color: Theme.of(context).canvasColor, - borderRadius: BorderRadius.all(Radius.circular(8.0)), + borderRadius: const BorderRadius.all(Radius.circular(8.0)), ), lastItemTargetHeight: 8, addLastItemTargetHeightToTop: true, lastListTargetSize: 40, - dragHandle: Padding( - padding: EdgeInsets.only(right: 10), - child: Icon( - Icons.menu, - color: Colors.black26, + listDragHandle: const DragHandle( + verticalAlignment: DragHandleVerticalAlignment.top, + child: Padding( + padding: EdgeInsets.only(right: 10), + child: Icon( + Icons.menu, + color: Colors.black26, + ), + ), + ), + itemDragHandle: const DragHandle( + child: Padding( + padding: EdgeInsets.only(right: 10), + child: Icon( + Icons.menu, + color: Colors.blueGrey, + ), ), ), ), diff --git a/example/lib/drag_into_list_example.dart b/example/lib/drag_into_list_example.dart index 7d19e3a..026c217 100644 --- a/example/lib/drag_into_list_example.dart +++ b/example/lib/drag_into_list_example.dart @@ -1,29 +1,25 @@ -import 'package:drag_and_drop_lists/drag_and_drop_item.dart'; import 'package:drag_and_drop_lists/drag_and_drop_list_interface.dart'; import 'package:drag_and_drop_lists/drag_and_drop_lists.dart'; -import 'package:example/navigation_drawer.dart'; -import 'package:flutter/cupertino.dart'; +import 'package:example/custom_navigation_drawer.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; class DragIntoListExample extends StatefulWidget { - DragIntoListExample({Key key, this.title}) : super(key: key); - final String title; + const DragIntoListExample({Key? key}) : super(key: key); @override - _DragIntoListExample createState() => _DragIntoListExample(); + State createState() => _DragIntoListExample(); } class _DragIntoListExample extends State { - List _contents = List(); + final List _contents = []; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text('Drag Into List'), + title: const Text('Drag Into List'), ), - drawer: NavigationDrawer(), + drawer: const CustomNavigationDrawer(), body: Column( children: [ Flexible( @@ -34,7 +30,7 @@ class _DragIntoListExample extends State { onListReorder: _onListReorder, onItemAdd: _onItemAdd, onListAdd: _onListAdd, - listGhost: Container( + listGhost: const SizedBox( height: 50, width: 100, child: Center( @@ -53,9 +49,14 @@ class _DragIntoListExample extends State { color: Colors.pink, child: Center( child: Draggable( - feedback: Icon(Icons.assignment), - child: Icon(Icons.assignment), - data: DragAndDropList(header: Text('New default list')), + feedback: const Icon(Icons.assignment), + data: DragAndDropList( + header: const Text( + 'New default list', + ), + children: [], + ), + child: const Icon(Icons.assignment), ), ), ), @@ -65,9 +66,9 @@ class _DragIntoListExample extends State { color: Colors.orange, child: Center( child: Draggable( - feedback: Icon(Icons.photo), - child: Icon(Icons.photo), - data: DragAndDropItem(child: Text('New default item')), + feedback: const Icon(Icons.photo), + data: DragAndDropItem(child: const Text('New default item')), + child: const Icon(Icons.photo), ), ), ), @@ -96,22 +97,22 @@ class _DragIntoListExample extends State { } _onItemAdd(DragAndDropItem newItem, int listIndex, int itemIndex) { - print('adding new item'); setState(() { - if (itemIndex == -1) + if (itemIndex == -1) { _contents[listIndex].children.add(newItem); - else + } else { _contents[listIndex].children.insert(itemIndex, newItem); + } }); } _onListAdd(DragAndDropListInterface newList, int listIndex) { - print('adding new list'); setState(() { - if (listIndex == -1) - _contents.add(newList); - else - _contents.insert(listIndex, newList); + if (listIndex == -1) { + _contents.add(newList as DragAndDropList); + } else { + _contents.insert(listIndex, newList as DragAndDropList); + } }); } } diff --git a/example/lib/expansion_tile_example.dart b/example/lib/expansion_tile_example.dart index cc6915b..d170643 100644 --- a/example/lib/expansion_tile_example.dart +++ b/example/lib/expansion_tile_example.dart @@ -1,26 +1,22 @@ -import 'package:drag_and_drop_lists/drag_and_drop_list_expansion.dart'; import 'package:drag_and_drop_lists/drag_and_drop_lists.dart'; -import 'package:example/navigation_drawer.dart'; +import 'package:example/custom_navigation_drawer.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; -import 'package:flutter/widgets.dart'; class ExpansionTileExample extends StatefulWidget { - ExpansionTileExample({Key key, this.title}) : super(key: key); - final String title; + const ExpansionTileExample({Key? key}) : super(key: key); @override - _ListTileExample createState() => _ListTileExample(); + State createState() => _ListTileExample(); } class InnerList { final String name; List children; - InnerList({this.name, this.children}); + InnerList({required this.name, required this.children}); } class _ListTileExample extends State { - List _lists; + late List _lists; @override void initState() { @@ -38,9 +34,9 @@ class _ListTileExample extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text('Expansion Tiles'), + title: const Text('Expansion Tiles'), ), - drawer: NavigationDrawer(), + drawer: const CustomNavigationDrawer(), body: DragAndDropLists( children: List.generate(_lists.length, (index) => _buildList(index)), onItemReorder: _onItemReorder, @@ -50,12 +46,12 @@ class _ListTileExample extends State { padding: const EdgeInsets.symmetric(vertical: 30.0), child: Center( child: Container( - padding: EdgeInsets.symmetric(vertical: 30.0, horizontal: 100.0), + padding: const EdgeInsets.symmetric(vertical: 30.0, horizontal: 100.0), decoration: BoxDecoration( border: Border.all(), borderRadius: BorderRadius.circular(7.0), ), - child: Icon(Icons.add_box), + child: const Icon(Icons.add_box), ), ), ), @@ -68,7 +64,7 @@ class _ListTileExample extends State { return DragAndDropListExpansion( title: Text('List ${innerList.name}'), subtitle: Text('Subtitle ${innerList.name}'), - leading: Icon(Icons.ac_unit), + leading: const Icon(Icons.ac_unit), children: List.generate(innerList.children.length, (index) => _buildItem(innerList.children[index])), listKey: ObjectKey(innerList), diff --git a/example/lib/fixed_example.dart b/example/lib/fixed_example.dart index 4f51180..7b23085 100644 --- a/example/lib/fixed_example.dart +++ b/example/lib/fixed_example.dart @@ -1,19 +1,16 @@ -import 'package:drag_and_drop_lists/drag_and_drop_item.dart'; import 'package:drag_and_drop_lists/drag_and_drop_lists.dart'; -import 'package:example/navigation_drawer.dart'; +import 'package:example/custom_navigation_drawer.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; class FixedExample extends StatefulWidget { - FixedExample({Key key, this.title}) : super(key: key); - final String title; + const FixedExample({Key? key}) : super(key: key); @override - _FixedExample createState() => _FixedExample(); + State createState() => _FixedExample(); } class _FixedExample extends State { - List _contents; + late List _contents; @override void initState() { @@ -23,7 +20,7 @@ class _FixedExample extends State { return DragAndDropList( header: Row( children: [ - Expanded( + const Expanded( flex: 1, child: Divider(), ), @@ -33,7 +30,7 @@ class _FixedExample extends State { ? 'Header $index : Non-Draggable' : 'Header $index'), ), - Expanded( + const Expanded( flex: 1, child: Divider(), ), @@ -60,9 +57,9 @@ class _FixedExample extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text('Fixed Items'), + title: const Text('Fixed Items'), ), - drawer: NavigationDrawer(), + drawer: const CustomNavigationDrawer(), body: DragAndDropLists( children: _contents, onItemReorder: _onItemReorder, diff --git a/example/lib/horizontal_example.dart b/example/lib/horizontal_example.dart index 5fea6a6..594b9c4 100644 --- a/example/lib/horizontal_example.dart +++ b/example/lib/horizontal_example.dart @@ -1,25 +1,22 @@ -import 'package:drag_and_drop_lists/drag_and_drop_item.dart'; import 'package:drag_and_drop_lists/drag_and_drop_lists.dart'; -import 'package:example/navigation_drawer.dart'; +import 'package:example/custom_navigation_drawer.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; class HorizontalExample extends StatefulWidget { - HorizontalExample({Key key, this.title}) : super(key: key); - final String title; + const HorizontalExample({Key? key}) : super(key: key); @override - _HorizontalExample createState() => _HorizontalExample(); + State createState() => _HorizontalExample(); } class InnerList { final String name; List children; - InnerList({this.name, this.children}); + InnerList({required this.name, required this.children}); } class _HorizontalExample extends State { - List _lists; + late List _lists; @override void initState() { @@ -37,9 +34,9 @@ class _HorizontalExample extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text('Horizontal'), + title: const Text('Horizontal'), ), - drawer: NavigationDrawer(), + drawer: const CustomNavigationDrawer(), body: DragAndDropLists( children: List.generate(_lists.length, (index) => _buildList(index)), onItemReorder: _onItemReorder, @@ -49,8 +46,8 @@ class _HorizontalExample extends State { listDraggingWidth: 150, listDecoration: BoxDecoration( color: Colors.grey[200], - borderRadius: BorderRadius.all(Radius.circular(7.0)), - boxShadow: [ + borderRadius: const BorderRadius.all(Radius.circular(7.0)), + boxShadow: const [ BoxShadow( color: Colors.black45, spreadRadius: 3.0, @@ -59,7 +56,7 @@ class _HorizontalExample extends State { ), ], ), - listPadding: EdgeInsets.all(8.0), + listPadding: const EdgeInsets.all(8.0), ), ); } @@ -71,14 +68,14 @@ class _HorizontalExample extends State { children: [ Expanded( child: Container( - decoration: BoxDecoration( + decoration: const BoxDecoration( borderRadius: BorderRadius.vertical(top: Radius.circular(7.0)), color: Colors.pink, ), - padding: EdgeInsets.all(10), + padding: const EdgeInsets.all(10), child: Text( 'Header ${innerList.name}', - style: Theme.of(context).primaryTextTheme.headline6, + style: Theme.of(context).primaryTextTheme.titleLarge, ), ), ), @@ -88,26 +85,26 @@ class _HorizontalExample extends State { children: [ Expanded( child: Container( - decoration: BoxDecoration( + decoration: const BoxDecoration( borderRadius: BorderRadius.vertical(bottom: Radius.circular(7.0)), color: Colors.pink, ), - padding: EdgeInsets.all(10), + padding: const EdgeInsets.all(10), child: Text( 'Footer ${innerList.name}', - style: Theme.of(context).primaryTextTheme.headline6, + style: Theme.of(context).primaryTextTheme.titleLarge, ), ), ), ], ), - leftSide: VerticalDivider( + leftSide: const VerticalDivider( color: Colors.pink, width: 1.5, thickness: 1.5, ), - rightSide: VerticalDivider( + rightSide: const VerticalDivider( color: Colors.pink, width: 1.5, thickness: 1.5, diff --git a/example/lib/list_tile_example.dart b/example/lib/list_tile_example.dart index 84e42a4..0b10478 100644 --- a/example/lib/list_tile_example.dart +++ b/example/lib/list_tile_example.dart @@ -1,20 +1,16 @@ import 'package:drag_and_drop_lists/drag_and_drop_lists.dart'; -import 'package:example/navigation_drawer.dart'; -import 'package:flutter/cupertino.dart'; +import 'package:example/custom_navigation_drawer.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; -import 'package:flutter/widgets.dart'; class ListTileExample extends StatefulWidget { - ListTileExample({Key key, this.title}) : super(key: key); - final String title; + const ListTileExample({Key? key}) : super(key: key); @override - _ListTileExample createState() => _ListTileExample(); + State createState() => _ListTileExample(); } class _ListTileExample extends State { - List _contents; + late List _contents; @override void initState() { @@ -30,12 +26,12 @@ class _ListTileExample extends State { ), subtitle: Text('Header $index subtitle'), ), - Divider(), + const Divider(), ], ), footer: Column( children: [ - Divider(), + const Divider(), ListTile( title: Text( 'Footer $index', @@ -50,7 +46,7 @@ class _ListTileExample extends State { title: Text( 'Sub $index.1', ), - trailing: Icon(Icons.access_alarm), + trailing: const Icon(Icons.access_alarm), ), ), DragAndDropItem( @@ -58,7 +54,7 @@ class _ListTileExample extends State { title: Text( 'Sub $index.2', ), - trailing: Icon(Icons.alarm_off), + trailing: const Icon(Icons.alarm_off), ), ), DragAndDropItem( @@ -66,7 +62,7 @@ class _ListTileExample extends State { title: Text( 'Sub $index.3', ), - trailing: Icon(Icons.alarm_on), + trailing: const Icon(Icons.alarm_on), ), ), ], @@ -78,9 +74,9 @@ class _ListTileExample extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text('List Tiles'), + title: const Text('List Tiles'), ), - drawer: NavigationDrawer(), + drawer: const CustomNavigationDrawer(), body: DragAndDropLists( children: _contents, onItemReorder: _onItemReorder, @@ -89,19 +85,19 @@ class _ListTileExample extends State { padding: const EdgeInsets.symmetric(vertical: 30.0), child: Center( child: Container( - padding: EdgeInsets.symmetric(vertical: 40.0, horizontal: 100.0), + padding: const EdgeInsets.symmetric(vertical: 40.0, horizontal: 100.0), decoration: BoxDecoration( border: Border.all(), borderRadius: BorderRadius.circular(7.0), ), - child: Icon(Icons.add_box), + child: const Icon(Icons.add_box), ), ), ), - listPadding: EdgeInsets.symmetric(horizontal: 5, vertical: 10), + listPadding: const EdgeInsets.symmetric(horizontal: 5, vertical: 10), contentsWhenEmpty: Row( children: [ - Expanded( + const Expanded( child: Padding( padding: EdgeInsets.only(left: 40, right: 10), child: Divider(), @@ -110,10 +106,10 @@ class _ListTileExample extends State { Text( 'Empty List', style: TextStyle( - color: Theme.of(context).textTheme.caption.color, + color: Theme.of(context).textTheme.bodySmall!.color, fontStyle: FontStyle.italic), ), - Expanded( + const Expanded( child: Padding( padding: EdgeInsets.only(left: 20, right: 40), child: Divider(), @@ -123,13 +119,13 @@ class _ListTileExample extends State { ), listDecoration: BoxDecoration( color: Theme.of(context).canvasColor, - borderRadius: BorderRadius.all(Radius.circular(6.0)), + borderRadius: const BorderRadius.all(Radius.circular(6.0)), boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.5), spreadRadius: 2, blurRadius: 3, - offset: Offset(0, 3), // changes position of shadow + offset: const Offset(0, 3), // changes position of shadow ), ], ), diff --git a/example/lib/main.dart b/example/lib/main.dart index 0d04664..b942893 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -9,10 +9,12 @@ import 'package:example/sliver_example.dart'; import 'package:flutter/material.dart'; void main() { - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + // This widget is the root of your application. @override Widget build(BuildContext context) { @@ -24,14 +26,14 @@ class MyApp extends StatelessWidget { ), initialRoute: '/', routes: { - '/': (context) => BasicExample(), - '/list_tile_example': (context) => ListTileExample(), - '/expansion_tile_example': (context) => ExpansionTileExample(), - '/sliver_example': (context) => SliverExample(), - '/horizontal_example': (context) => HorizontalExample(), - '/drag_into_list_example': (context) => DragIntoListExample(), - '/fixed_example': (context) => FixedExample(), - '/drag_handle_example': (context) => DragHandleExample(), + '/': (context) => const BasicExample(), + '/list_tile_example': (context) => const ListTileExample(), + '/expansion_tile_example': (context) => const ExpansionTileExample(), + '/sliver_example': (context) => const SliverExample(), + '/horizontal_example': (context) => const HorizontalExample(), + '/drag_into_list_example': (context) => const DragIntoListExample(), + '/fixed_example': (context) => const FixedExample(), + '/drag_handle_example': (context) => const DragHandleExample(), }, ); } diff --git a/example/lib/sliver_example.dart b/example/lib/sliver_example.dart index 996d98d..ab2fb89 100644 --- a/example/lib/sliver_example.dart +++ b/example/lib/sliver_example.dart @@ -1,21 +1,17 @@ -import 'package:drag_and_drop_lists/drag_and_drop_item.dart'; import 'package:drag_and_drop_lists/drag_and_drop_lists.dart'; -import 'package:example/navigation_drawer.dart'; -import 'package:flutter/cupertino.dart'; +import 'package:example/custom_navigation_drawer.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; class SliverExample extends StatefulWidget { - SliverExample({Key key, this.title}) : super(key: key); - final String title; + const SliverExample({Key? key}) : super(key: key); @override - _SliverExample createState() => _SliverExample(); + State createState() => _SliverExample(); } class _SliverExample extends State { - List _contents; - ScrollController _scrollController = ScrollController(); + late List _contents; + final ScrollController _scrollController = ScrollController(); @override void initState() { @@ -25,7 +21,7 @@ class _SliverExample extends State { return DragAndDropList( header: Row( children: [ - Expanded( + const Expanded( flex: 1, child: Divider(), ), @@ -33,7 +29,7 @@ class _SliverExample extends State { padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), child: Text('Header $index'), ), - Expanded( + const Expanded( flex: 1, child: Divider(), ), @@ -41,8 +37,16 @@ class _SliverExample extends State { ), children: [ DragAndDropItem( - child: Text('$index.1'), - ), + child: Container( + color: Colors.blue, + width: 50, + height: 30, + ), + feedbackWidget: Container( + color: Colors.red, + width: 50, + height: 30, + )), DragAndDropItem( child: Text('$index.2'), ), @@ -57,7 +61,7 @@ class _SliverExample extends State { @override Widget build(BuildContext context) { return Scaffold( - drawer: NavigationDrawer(), + drawer: const CustomNavigationDrawer(), body: CustomScrollView( controller: _scrollController, slivers: [ @@ -69,13 +73,13 @@ class _SliverExample extends State { alignment: Alignment.bottomCenter, child: Text( 'Slivers', - style: Theme.of(context).primaryTextTheme.headline1, + style: Theme.of(context).primaryTextTheme.displayLarge, ), ), ), ), SliverPadding( - padding: EdgeInsets.only(top: 20), + padding: const EdgeInsets.only(top: 20), sliver: DragAndDropLists( children: _contents, onItemReorder: _onItemReorder, diff --git a/example/linux/.gitignore b/example/linux/.gitignore new file mode 100644 index 0000000..d3896c9 --- /dev/null +++ b/example/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/example/linux/CMakeLists.txt b/example/linux/CMakeLists.txt new file mode 100644 index 0000000..74c66dd --- /dev/null +++ b/example/linux/CMakeLists.txt @@ -0,0 +1,138 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "example") +# The unique GTK application identifier for this application. See: +# https://wiki.gnome.org/HowDoI/ChooseApplicationID +set(APPLICATION_ID "com.example.example") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Load bundled libraries from the lib/ directory relative to the binary. +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Root filesystem for cross-building. +if(FLUTTER_TARGET_PLATFORM_SYSROOT) + set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +endif() + +# Define build configuration options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Define the application target. To change its name, change BINARY_NAME above, +# not the value here, or `flutter run` will no longer work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add dependency libraries. Add any application-specific dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) + install(FILES "${bundled_library}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endforeach(bundled_library) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/example/linux/flutter/CMakeLists.txt b/example/linux/flutter/CMakeLists.txt new file mode 100644 index 0000000..d5bd016 --- /dev/null +++ b/example/linux/flutter/CMakeLists.txt @@ -0,0 +1,88 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/example/linux/flutter/generated_plugin_registrant.cc b/example/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 0000000..e71a16d --- /dev/null +++ b/example/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,11 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + + +void fl_register_plugins(FlPluginRegistry* registry) { +} diff --git a/example/linux/flutter/generated_plugin_registrant.h b/example/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 0000000..e0f0a47 --- /dev/null +++ b/example/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/example/linux/flutter/generated_plugins.cmake b/example/linux/flutter/generated_plugins.cmake new file mode 100644 index 0000000..2e1de87 --- /dev/null +++ b/example/linux/flutter/generated_plugins.cmake @@ -0,0 +1,23 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/example/linux/main.cc b/example/linux/main.cc new file mode 100644 index 0000000..e7c5c54 --- /dev/null +++ b/example/linux/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/example/linux/my_application.cc b/example/linux/my_application.cc new file mode 100644 index 0000000..0ba8f43 --- /dev/null +++ b/example/linux/my_application.cc @@ -0,0 +1,104 @@ +#include "my_application.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen* screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "example"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } else { + gtk_window_set_title(window, "example"); + } + + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject* object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, + "flags", G_APPLICATION_NON_UNIQUE, + nullptr)); +} diff --git a/example/linux/my_application.h b/example/linux/my_application.h new file mode 100644 index 0000000..72271d5 --- /dev/null +++ b/example/linux/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/example/macos/.gitignore b/example/macos/.gitignore new file mode 100644 index 0000000..746adbb --- /dev/null +++ b/example/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/example/macos/Flutter/Flutter-Debug.xcconfig b/example/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 0000000..c2efd0b --- /dev/null +++ b/example/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/example/macos/Flutter/Flutter-Release.xcconfig b/example/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 0000000..c2efd0b --- /dev/null +++ b/example/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/example/macos/Flutter/GeneratedPluginRegistrant.swift b/example/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 0000000..cccf817 --- /dev/null +++ b/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,10 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { +} diff --git a/example/macos/Runner.xcodeproj/project.pbxproj b/example/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..c84862c --- /dev/null +++ b/example/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,572 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* example.app */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* example.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..fb7259e --- /dev/null +++ b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/example/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..1d526a1 --- /dev/null +++ b/example/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example/macos/Runner/AppDelegate.swift b/example/macos/Runner/AppDelegate.swift new file mode 100644 index 0000000..d53ef64 --- /dev/null +++ b/example/macos/Runner/AppDelegate.swift @@ -0,0 +1,9 @@ +import Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..a2ec33f --- /dev/null +++ b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000..3c4935a Binary files /dev/null and b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000..ed4cc16 Binary files /dev/null and b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 0000000..483be61 Binary files /dev/null and b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100644 index 0000000..bcbf36d Binary files /dev/null and b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png new file mode 100644 index 0000000..9c0a652 Binary files /dev/null and b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100644 index 0000000..e71a726 Binary files /dev/null and b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 0000000..8a31fe2 Binary files /dev/null and b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ diff --git a/example/macos/Runner/Base.lproj/MainMenu.xib b/example/macos/Runner/Base.lproj/MainMenu.xib new file mode 100644 index 0000000..80e867a --- /dev/null +++ b/example/macos/Runner/Base.lproj/MainMenu.xib @@ -0,0 +1,343 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/macos/Runner/Configs/AppInfo.xcconfig b/example/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 0000000..8b42559 --- /dev/null +++ b/example/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = example + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.example.example + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2022 com.example. All rights reserved. diff --git a/example/macos/Runner/Configs/Debug.xcconfig b/example/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 0000000..36b0fd9 --- /dev/null +++ b/example/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/example/macos/Runner/Configs/Release.xcconfig b/example/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 0000000..dff4f49 --- /dev/null +++ b/example/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/example/macos/Runner/Configs/Warnings.xcconfig b/example/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 0000000..42bcbf4 --- /dev/null +++ b/example/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/example/macos/Runner/DebugProfile.entitlements b/example/macos/Runner/DebugProfile.entitlements new file mode 100644 index 0000000..dddb8a3 --- /dev/null +++ b/example/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + + diff --git a/example/macos/Runner/Info.plist b/example/macos/Runner/Info.plist new file mode 100644 index 0000000..4789daa --- /dev/null +++ b/example/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/example/macos/Runner/MainFlutterWindow.swift b/example/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 0000000..2722837 --- /dev/null +++ b/example/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController.init() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/example/macos/Runner/Release.entitlements b/example/macos/Runner/Release.entitlements new file mode 100644 index 0000000..852fa1a --- /dev/null +++ b/example/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/example/pubspec.lock b/example/pubspec.lock deleted file mode 100644 index 8ef7a6a..0000000 --- a/example/pubspec.lock +++ /dev/null @@ -1,160 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - async: - dependency: transitive - description: - name: async - url: "https://pub.dartlang.org" - source: hosted - version: "2.5.0-nullsafety.3" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0-nullsafety.3" - characters: - dependency: transitive - description: - name: characters - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0-nullsafety.5" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0-nullsafety.3" - clock: - dependency: transitive - description: - name: clock - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0-nullsafety.3" - collection: - dependency: transitive - description: - name: collection - url: "https://pub.dartlang.org" - source: hosted - version: "1.15.0-nullsafety.5" - cupertino_icons: - dependency: "direct main" - description: - name: cupertino_icons - url: "https://pub.dartlang.org" - source: hosted - version: "0.1.3" - drag_and_drop_lists: - dependency: "direct main" - description: - path: ".." - relative: true - source: path - version: "0.2.10" - fake_async: - dependency: transitive - description: - name: fake_async - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0-nullsafety.3" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - matcher: - dependency: transitive - description: - name: matcher - url: "https://pub.dartlang.org" - source: hosted - version: "0.12.10-nullsafety.3" - meta: - dependency: transitive - description: - name: meta - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.0-nullsafety.6" - path: - dependency: transitive - description: - name: path - url: "https://pub.dartlang.org" - source: hosted - version: "1.8.0-nullsafety.3" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_span: - dependency: transitive - description: - name: source_span - url: "https://pub.dartlang.org" - source: hosted - version: "1.8.0-nullsafety.4" - stack_trace: - dependency: transitive - description: - name: stack_trace - url: "https://pub.dartlang.org" - source: hosted - version: "1.10.0-nullsafety.6" - stream_channel: - dependency: transitive - description: - name: stream_channel - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0-nullsafety.3" - string_scanner: - dependency: transitive - description: - name: string_scanner - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0-nullsafety.3" - term_glyph: - dependency: transitive - description: - name: term_glyph - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0-nullsafety.3" - test_api: - dependency: transitive - description: - name: test_api - url: "https://pub.dartlang.org" - source: hosted - version: "0.2.19-nullsafety.6" - typed_data: - dependency: transitive - description: - name: typed_data - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.0-nullsafety.5" - vector_math: - dependency: transitive - description: - name: vector_math - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0-nullsafety.5" -sdks: - dart: ">=2.12.0-0.0 <3.0.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index e3a315e..4f8acb3 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,8 +1,8 @@ name: example -description: Example flutter project for DragAndDropLists +description: A new Flutter project. # The following line prevents the package from being accidentally published to -# pub.dev using `pub publish`. This is preferred for private packages. +# pub.dev using `flutter pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev # The following defines the version and build number for your application. @@ -18,8 +18,14 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: ">=2.7.0 <3.0.0" + sdk: ">=2.17.6 <4.0.0" +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. dependencies: flutter: sdk: flutter @@ -27,19 +33,25 @@ dependencies: drag_and_drop_lists: path: ../ - # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^0.1.3 + cupertino_icons: ^1.0.2 dev_dependencies: flutter_test: sdk: flutter + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^2.0.0 + # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec -# The following section is specific to Flutter. +# The following section is specific to Flutter packages. flutter: # The following line ensures that the Material Icons font is @@ -53,7 +65,7 @@ flutter: # - images/a_dot_ham.jpeg # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware. + # https://flutter.dev/assets-and-images/#resolution-aware # For details regarding adding assets from package dependencies, see # https://flutter.dev/assets-and-images/#from-packages diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart index 747db1d..092d222 100644 --- a/example/test/widget_test.dart +++ b/example/test/widget_test.dart @@ -1,7 +1,7 @@ // This is a basic Flutter widget test. // // To perform an interaction with a widget in your test, use the WidgetTester -// utility that Flutter provides. For example, you can send tap and scroll +// utility in the flutter_test package. For example, you can send tap and scroll // gestures. You can also use WidgetTester to find child widgets in the widget // tree, read text, and verify that the values of widget properties are correct. @@ -13,7 +13,7 @@ import 'package:example/main.dart'; void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { // Build our app and trigger a frame. - await tester.pumpWidget(MyApp()); + await tester.pumpWidget(const MyApp()); // Verify that our counter starts at 0. expect(find.text('0'), findsOneWidget); diff --git a/example/web/icons/Icon-maskable-192.png b/example/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000..eb9b4d7 Binary files /dev/null and b/example/web/icons/Icon-maskable-192.png differ diff --git a/example/web/icons/Icon-maskable-512.png b/example/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000..d69c566 Binary files /dev/null and b/example/web/icons/Icon-maskable-512.png differ diff --git a/example/web/index.html b/example/web/index.html index 9b7a438..1aa025d 100644 --- a/example/web/index.html +++ b/example/web/index.html @@ -1,6 +1,21 @@ + + + @@ -12,22 +27,12 @@ - + example - - - + diff --git a/example/web/manifest.json b/example/web/manifest.json index 8c01291..096edf8 100644 --- a/example/web/manifest.json +++ b/example/web/manifest.json @@ -18,6 +18,18 @@ "src": "icons/Icon-512.png", "sizes": "512x512", "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" } ] } diff --git a/example/windows/.gitignore b/example/windows/.gitignore new file mode 100644 index 0000000..d492d0d --- /dev/null +++ b/example/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/example/windows/CMakeLists.txt b/example/windows/CMakeLists.txt new file mode 100644 index 0000000..c027074 --- /dev/null +++ b/example/windows/CMakeLists.txt @@ -0,0 +1,101 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(example LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "example") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Define build configuration option. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() +# Define settings for the Profile build mode. +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/example/windows/flutter/CMakeLists.txt b/example/windows/flutter/CMakeLists.txt new file mode 100644 index 0000000..930d207 --- /dev/null +++ b/example/windows/flutter/CMakeLists.txt @@ -0,0 +1,104 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + windows-x64 $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/example/windows/flutter/generated_plugin_registrant.cc b/example/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 0000000..8b6d468 --- /dev/null +++ b/example/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,11 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + + +void RegisterPlugins(flutter::PluginRegistry* registry) { +} diff --git a/example/windows/flutter/generated_plugin_registrant.h b/example/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 0000000..dc139d8 --- /dev/null +++ b/example/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/example/windows/flutter/generated_plugins.cmake b/example/windows/flutter/generated_plugins.cmake new file mode 100644 index 0000000..b93c4c3 --- /dev/null +++ b/example/windows/flutter/generated_plugins.cmake @@ -0,0 +1,23 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/example/windows/runner/CMakeLists.txt b/example/windows/runner/CMakeLists.txt new file mode 100644 index 0000000..b9e550f --- /dev/null +++ b/example/windows/runner/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/example/windows/runner/Runner.rc b/example/windows/runner/Runner.rc new file mode 100644 index 0000000..5fdea29 --- /dev/null +++ b/example/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#ifdef FLUTTER_BUILD_NUMBER +#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#else +#define VERSION_AS_NUMBER 1,0,0 +#endif + +#ifdef FLUTTER_BUILD_NAME +#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.example" "\0" + VALUE "FileDescription", "example" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2022 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "example.exe" "\0" + VALUE "ProductName", "example" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/example/windows/runner/flutter_window.cpp b/example/windows/runner/flutter_window.cpp new file mode 100644 index 0000000..b43b909 --- /dev/null +++ b/example/windows/runner/flutter_window.cpp @@ -0,0 +1,61 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/example/windows/runner/flutter_window.h b/example/windows/runner/flutter_window.h new file mode 100644 index 0000000..6da0652 --- /dev/null +++ b/example/windows/runner/flutter_window.h @@ -0,0 +1,33 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/example/windows/runner/main.cpp b/example/windows/runner/main.cpp new file mode 100644 index 0000000..bcb57b0 --- /dev/null +++ b/example/windows/runner/main.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.CreateAndShow(L"example", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/example/windows/runner/resource.h b/example/windows/runner/resource.h new file mode 100644 index 0000000..66a65d1 --- /dev/null +++ b/example/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/example/windows/runner/resources/app_icon.ico b/example/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000..c04e20c Binary files /dev/null and b/example/windows/runner/resources/app_icon.ico differ diff --git a/example/windows/runner/runner.exe.manifest b/example/windows/runner/runner.exe.manifest new file mode 100644 index 0000000..c977c4a --- /dev/null +++ b/example/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/example/windows/runner/utils.cpp b/example/windows/runner/utils.cpp new file mode 100644 index 0000000..f5bf9fa --- /dev/null +++ b/example/windows/runner/utils.cpp @@ -0,0 +1,64 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr); + std::string utf8_string; + if (target_length == 0 || target_length > utf8_string.max_size()) { + return utf8_string; + } + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, utf8_string.data(), + target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/example/windows/runner/utils.h b/example/windows/runner/utils.h new file mode 100644 index 0000000..3879d54 --- /dev/null +++ b/example/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/example/windows/runner/win32_window.cpp b/example/windows/runner/win32_window.cpp new file mode 100644 index 0000000..c10f08d --- /dev/null +++ b/example/windows/runner/win32_window.cpp @@ -0,0 +1,245 @@ +#include "win32_window.h" + +#include + +#include "resource.h" + +namespace { + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + FreeLibrary(user32_module); + } +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + return OnCreate(); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} diff --git a/example/windows/runner/win32_window.h b/example/windows/runner/win32_window.h new file mode 100644 index 0000000..17ba431 --- /dev/null +++ b/example/windows/runner/win32_window.h @@ -0,0 +1,98 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates and shows a win32 window with |title| and position and size using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size to will treat the width height passed in to this function + // as logical pixels and scale to appropriate for the default monitor. Returns + // true if the window was created successfully. + bool CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responsponds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/lib/drag_and_drop_builder_parameters.dart b/lib/drag_and_drop_builder_parameters.dart index 3a2ffbd..6a27c5d 100644 --- a/lib/drag_and_drop_builder_parameters.dart +++ b/lib/drag_and_drop_builder_parameters.dart @@ -1,69 +1,59 @@ -import 'package:drag_and_drop_lists/drag_and_drop_item.dart'; -import 'package:drag_and_drop_lists/drag_and_drop_item_target.dart'; import 'package:drag_and_drop_lists/drag_and_drop_list_interface.dart'; import 'package:drag_and_drop_lists/drag_and_drop_lists.dart'; import 'package:flutter/widgets.dart'; -enum DragHandleVerticalAlignment { - top, - center, - bottom, -} - -typedef void OnPointerMove(PointerMoveEvent event); -typedef void OnPointerUp(PointerUpEvent event); -typedef void OnPointerDown(PointerDownEvent event); -typedef void OnItemReordered( +typedef OnPointerMove = void Function(PointerMoveEvent event); +typedef OnPointerUp = void Function(PointerUpEvent event); +typedef OnPointerDown = void Function(PointerDownEvent event); +typedef OnItemReordered = void Function( DragAndDropItem reorderedItem, DragAndDropItem receiverItem, ); -typedef void OnItemDropOnLastTarget( +typedef OnItemDropOnLastTarget = void Function( DragAndDropItem newOrReorderedItem, DragAndDropListInterface parentList, DragAndDropItemTarget receiver, ); -typedef void OnListReordered( +typedef OnListReordered = void Function( DragAndDropListInterface reorderedList, DragAndDropListInterface receiverList, ); class DragAndDropBuilderParameters { - final OnPointerMove onPointerMove; - final OnPointerUp onPointerUp; - final OnPointerDown onPointerDown; - final OnItemReordered onItemReordered; - final OnItemDropOnLastTarget onItemDropOnLastTarget; - final OnListReordered onListReordered; - final ListOnWillAccept listOnWillAccept; - final ListTargetOnWillAccept listTargetOnWillAccept; - final OnListDraggingChanged onListDraggingChanged; - final ItemOnWillAccept itemOnWillAccept; - final ItemTargetOnWillAccept itemTargetOnWillAccept; - final OnItemDraggingChanged onItemDraggingChanged; + final OnPointerMove? onPointerMove; + final OnPointerUp? onPointerUp; + final OnPointerDown? onPointerDown; + final OnItemReordered? onItemReordered; + final OnItemDropOnLastTarget? onItemDropOnLastTarget; + final OnListReordered? onListReordered; + final ListOnWillAccept? listOnWillAccept; + final ListTargetOnWillAccept? listTargetOnWillAccept; + final OnListDraggingChanged? onListDraggingChanged; + final ItemOnWillAccept? itemOnWillAccept; + final ItemTargetOnWillAccept? itemTargetOnWillAccept; + final OnItemDraggingChanged? onItemDraggingChanged; final Axis axis; final CrossAxisAlignment verticalAlignment; - final double listDraggingWidth; + final double? listDraggingWidth; final bool dragOnLongPress; final int itemSizeAnimationDuration; - final Widget itemGhost; + final Widget? itemGhost; final double itemGhostOpacity; - final Widget itemDivider; - final double itemDraggingWidth; - final Decoration itemDecorationWhileDragging; + final Widget? itemDivider; + final double? itemDraggingWidth; + final Decoration? itemDecorationWhileDragging; final int listSizeAnimationDuration; - final Widget listGhost; + final Widget? listGhost; final double listGhostOpacity; - final EdgeInsets listPadding; - final Decoration listDecoration; - final Decoration listDecorationWhileDragging; - final Decoration listInnerDecoration; + final EdgeInsets? listPadding; + final Decoration? listDecoration; + final Decoration? listDecorationWhileDragging; + final Decoration? listInnerDecoration; final double listWidth; final double lastItemTargetHeight; final bool addLastItemTargetHeightToTop; - final Widget dragHandle; - final bool dragHandleOnLeft; - final DragHandleVerticalAlignment listDragHandleVerticalAlignment; - final DragHandleVerticalAlignment itemDragHandleVerticalAlignment; + final DragHandle? listDragHandle; + final DragHandle? itemDragHandle; final bool constrainDraggingAxis; final bool disableScrolling; @@ -100,10 +90,8 @@ class DragAndDropBuilderParameters { this.listWidth = double.infinity, this.lastItemTargetHeight = 20, this.addLastItemTargetHeightToTop = false, - this.dragHandle, - this.dragHandleOnLeft = false, - this.itemDragHandleVerticalAlignment = DragHandleVerticalAlignment.center, - this.listDragHandleVerticalAlignment = DragHandleVerticalAlignment.top, + this.listDragHandle, + this.itemDragHandle, this.constrainDraggingAxis = true, this.disableScrolling = false, }); diff --git a/lib/drag_and_drop_item.dart b/lib/drag_and_drop_item.dart index 7721ad4..e96f45e 100644 --- a/lib/drag_and_drop_item.dart +++ b/lib/drag_and_drop_item.dart @@ -5,10 +5,18 @@ class DragAndDropItem implements DragAndDropInterface { /// The child widget of this item. final Widget child; + /// Widget when draggable + final Widget? feedbackWidget; + /// Whether or not this item can be dragged. /// Set to true if it can be reordered. /// Set to false if it must remain fixed. final bool canDrag; - - DragAndDropItem({@required this.child, this.canDrag = true}); + final Key? key; + DragAndDropItem({ + required this.child, + this.feedbackWidget, + this.canDrag = true, + this.key, + }); } diff --git a/lib/drag_and_drop_item_target.dart b/lib/drag_and_drop_item_target.dart index ea59eae..f4eec2a 100644 --- a/lib/drag_and_drop_item_target.dart +++ b/lib/drag_and_drop_item_target.dart @@ -1,4 +1,3 @@ -import 'package:drag_and_drop_lists/drag_and_drop_builder_parameters.dart'; import 'package:drag_and_drop_lists/drag_and_drop_list_interface.dart'; import 'package:drag_and_drop_lists/drag_and_drop_lists.dart'; import 'package:flutter/material.dart'; @@ -6,17 +5,16 @@ import 'package:flutter/widgets.dart'; class DragAndDropItemTarget extends StatefulWidget { final Widget child; - final DragAndDropListInterface parent; + final DragAndDropListInterface? parent; final DragAndDropBuilderParameters parameters; final OnItemDropOnLastTarget onReorderOrAdd; - DragAndDropItemTarget( - {@required this.child, - @required this.onReorderOrAdd, - @required this.parameters, + const DragAndDropItemTarget( + {required this.child, + required this.onReorderOrAdd, + required this.parameters, this.parent, - Key key}) - : super(key: key); + super.key}); @override State createState() => _DragAndDropItemTarget(); @@ -24,7 +22,7 @@ class DragAndDropItemTarget extends StatefulWidget { class _DragAndDropItemTarget extends State with TickerProviderStateMixin { - DragAndDropItem _hoveredDraggable; + DragAndDropItem? _hoveredDraggable; @override Widget build(BuildContext context) { @@ -36,52 +34,48 @@ class _DragAndDropItemTarget extends State AnimatedSize( duration: Duration( milliseconds: widget.parameters.itemSizeAnimationDuration), - vsync: this, alignment: Alignment.bottomCenter, child: _hoveredDraggable != null ? Opacity( opacity: widget.parameters.itemGhostOpacity, child: widget.parameters.itemGhost ?? - _hoveredDraggable.child, + _hoveredDraggable!.child, ) : Container(), ), - widget.child ?? - Container( - height: 20, - ), + widget.child, ], ), Positioned.fill( child: DragTarget( builder: (context, candidateData, rejectedData) { - if (candidateData != null && candidateData.isNotEmpty) {} + if (candidateData.isNotEmpty) {} return Container(); }, - onWillAccept: (incoming) { + onWillAcceptWithDetails: (details) { bool accept = true; - if (widget.parameters.itemTargetOnWillAccept != null) + if (widget.parameters.itemTargetOnWillAccept != null) { accept = - widget.parameters.itemTargetOnWillAccept(incoming, widget); + widget.parameters.itemTargetOnWillAccept!(details.data, widget); + } if (accept && mounted) { setState(() { - _hoveredDraggable = incoming; + _hoveredDraggable = details.data; }); } return accept; }, - onLeave: (incoming) { + onLeave: (data) { if (mounted) { setState(() { _hoveredDraggable = null; }); } }, - onAccept: (incoming) { + onAcceptWithDetails: (details) { if (mounted) { setState(() { - if (widget.onReorderOrAdd != null) - widget.onReorderOrAdd(incoming, widget.parent, widget); + widget.onReorderOrAdd(details.data, widget.parent!, widget); _hoveredDraggable = null; }); } diff --git a/lib/drag_and_drop_item_wrapper.dart b/lib/drag_and_drop_item_wrapper.dart index 5086796..a85241e 100644 --- a/lib/drag_and_drop_item_wrapper.dart +++ b/lib/drag_and_drop_item_wrapper.dart @@ -1,18 +1,13 @@ -import 'package:drag_and_drop_lists/drag_and_drop_builder_parameters.dart'; -import 'package:drag_and_drop_lists/drag_and_drop_item.dart'; import 'package:drag_and_drop_lists/drag_and_drop_lists.dart'; import 'package:drag_and_drop_lists/measure_size.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; -import 'package:flutter/widgets.dart'; class DragAndDropItemWrapper extends StatefulWidget { final DragAndDropItem child; - final DragAndDropBuilderParameters parameters; + final DragAndDropBuilderParameters? parameters; - DragAndDropItemWrapper( - {@required this.child, @required this.parameters, Key key}) - : super(key: key); + const DragAndDropItemWrapper( + {required this.child, required this.parameters, super.key}); @override State createState() => _DragAndDropItemWrapper(); @@ -20,7 +15,7 @@ class DragAndDropItemWrapper extends StatefulWidget { class _DragAndDropItemWrapper extends State with TickerProviderStateMixin { - DragAndDropItem _hoveredDraggable; + DragAndDropItem? _hoveredDraggable; bool _dragging = false; Size _containerSize = Size.zero; @@ -30,37 +25,37 @@ class _DragAndDropItemWrapper extends State Widget build(BuildContext context) { Widget draggable; if (widget.child.canDrag) { - if (widget.parameters.dragHandle != null) { - Widget feedback = Container( - width: widget.parameters.itemDraggingWidth ?? _containerSize.width, + if (widget.parameters!.itemDragHandle != null) { + Widget feedback = SizedBox( + width: widget.parameters!.itemDraggingWidth ?? _containerSize.width, child: Stack( children: [ widget.child.child, Positioned( - right: widget.parameters.dragHandleOnLeft ? null : 0, - left: widget.parameters.dragHandleOnLeft ? 0 : null, - top: widget.parameters.itemDragHandleVerticalAlignment == + right: widget.parameters!.itemDragHandle!.onLeft ? null : 0, + left: widget.parameters!.itemDragHandle!.onLeft ? 0 : null, + top: widget.parameters!.itemDragHandle!.verticalAlignment == DragHandleVerticalAlignment.bottom ? null : 0, - bottom: widget.parameters.itemDragHandleVerticalAlignment == + bottom: widget.parameters!.itemDragHandle!.verticalAlignment == DragHandleVerticalAlignment.top ? null : 0, - child: widget.parameters.dragHandle, + child: widget.parameters!.itemDragHandle!, ), ], ), ); var positionedDragHandle = Positioned( - right: widget.parameters.dragHandleOnLeft ? null : 0, - left: widget.parameters.dragHandleOnLeft ? 0 : null, - top: widget.parameters.itemDragHandleVerticalAlignment == + right: widget.parameters!.itemDragHandle!.onLeft ? null : 0, + left: widget.parameters!.itemDragHandle!.onLeft ? 0 : null, + top: widget.parameters!.itemDragHandle!.verticalAlignment == DragHandleVerticalAlignment.bottom ? null : 0, - bottom: widget.parameters.itemDragHandleVerticalAlignment == + bottom: widget.parameters!.itemDragHandle!.verticalAlignment == DragHandleVerticalAlignment.top ? null : 0, @@ -68,25 +63,20 @@ class _DragAndDropItemWrapper extends State cursor: SystemMouseCursors.grab, child: Draggable( data: widget.child, - axis: widget.parameters.axis == Axis.vertical && - widget.parameters.constrainDraggingAxis + axis: widget.parameters!.axis == Axis.vertical && + widget.parameters!.constrainDraggingAxis ? Axis.vertical : null, - child: MeasureSize( - onSizeChange: (size) { - setState(() { - _dragHandleSize = size; - }); - }, - child: widget.parameters.dragHandle, - ), feedback: Transform.translate( offset: _feedbackContainerOffset(), child: Material( color: Colors.transparent, child: Container( - decoration: widget.parameters.itemDecorationWhileDragging, - child: feedback, + decoration: widget.parameters!.itemDecorationWhileDragging, + child: Directionality( + textDirection: Directionality.of(context), + child: feedback, + ), ), ), ), @@ -95,6 +85,14 @@ class _DragAndDropItemWrapper extends State onDragCompleted: () => _setDragging(false), onDraggableCanceled: (_, __) => _setDragging(false), onDragEnd: (_) => _setDragging(false), + child: MeasureSize( + onSizeChange: (size) { + setState(() { + _dragHandleSize = size!; + }); + }, + child: widget.parameters!.itemDragHandle, + ), ), ), ); @@ -112,25 +110,26 @@ class _DragAndDropItemWrapper extends State ], ), ); - } else if (widget.parameters.dragOnLongPress) { + } else if (widget.parameters!.dragOnLongPress) { draggable = MeasureSize( onSizeChange: _setContainerSize, child: LongPressDraggable( data: widget.child, - axis: widget.parameters.axis == Axis.vertical && - widget.parameters.constrainDraggingAxis + axis: widget.parameters!.axis == Axis.vertical && + widget.parameters!.constrainDraggingAxis ? Axis.vertical : null, - child: widget.child.child, - feedback: Container( + feedback: SizedBox( width: - widget.parameters.itemDraggingWidth ?? _containerSize.width, + widget.parameters!.itemDraggingWidth ?? _containerSize.width, child: Material( + color: Colors.transparent, child: Container( - child: widget.child.child, - decoration: widget.parameters.itemDecorationWhileDragging, + decoration: widget.parameters!.itemDecorationWhileDragging, + child: Directionality( + textDirection: Directionality.of(context), + child: widget.child.feedbackWidget ?? widget.child.child), ), - color: Colors.transparent, ), ), childWhenDragging: Container(), @@ -138,6 +137,7 @@ class _DragAndDropItemWrapper extends State onDragCompleted: () => _setDragging(false), onDraggableCanceled: (_, __) => _setDragging(false), onDragEnd: (_) => _setDragging(false), + child: widget.child.child, ), ); } else { @@ -145,20 +145,22 @@ class _DragAndDropItemWrapper extends State onSizeChange: _setContainerSize, child: Draggable( data: widget.child, - axis: widget.parameters.axis == Axis.vertical && - widget.parameters.constrainDraggingAxis + axis: widget.parameters!.axis == Axis.vertical && + widget.parameters!.constrainDraggingAxis ? Axis.vertical : null, - child: widget.child.child, - feedback: Container( + feedback: SizedBox( width: - widget.parameters.itemDraggingWidth ?? _containerSize.width, + widget.parameters!.itemDraggingWidth ?? _containerSize.width, child: Material( + color: Colors.transparent, child: Container( - child: widget.child.child, - decoration: widget.parameters.itemDecorationWhileDragging, + decoration: widget.parameters!.itemDecorationWhileDragging, + child: Directionality( + textDirection: Directionality.of(context), + child: widget.child.feedbackWidget ?? widget.child.child, + ), ), - color: Colors.transparent, ), ), childWhenDragging: Container(), @@ -166,14 +168,14 @@ class _DragAndDropItemWrapper extends State onDragCompleted: () => _setDragging(false), onDraggableCanceled: (_, __) => _setDragging(false), onDragEnd: (_) => _setDragging(false), + child: widget.child.child, ), ); } } else { draggable = AnimatedSize( - duration: - Duration(milliseconds: widget.parameters.itemSizeAnimationDuration), - vsync: this, + duration: Duration( + milliseconds: widget.parameters!.itemSizeAnimationDuration), alignment: Alignment.bottomCenter, child: _hoveredDraggable != null ? Container() : widget.child.child, ); @@ -182,59 +184,60 @@ class _DragAndDropItemWrapper extends State children: [ Column( mainAxisSize: MainAxisSize.min, - crossAxisAlignment: widget.parameters.verticalAlignment, + crossAxisAlignment: widget.parameters!.verticalAlignment, children: [ AnimatedSize( duration: Duration( - milliseconds: widget.parameters.itemSizeAnimationDuration), - vsync: this, + milliseconds: widget.parameters!.itemSizeAnimationDuration), alignment: Alignment.topLeft, child: _hoveredDraggable != null ? Opacity( - opacity: widget.parameters.itemGhostOpacity, - child: widget.parameters.itemGhost ?? - _hoveredDraggable.child, + opacity: widget.parameters!.itemGhostOpacity, + child: widget.parameters!.itemGhost ?? + _hoveredDraggable!.child, ) : Container(), ), Listener( - child: draggable, onPointerMove: _onPointerMove, - onPointerDown: widget.parameters.onPointerDown, - onPointerUp: widget.parameters.onPointerUp, + onPointerDown: widget.parameters!.onPointerDown, + onPointerUp: widget.parameters!.onPointerUp, + child: draggable, ), ], ), Positioned.fill( child: DragTarget( builder: (context, candidateData, rejectedData) { - if (candidateData != null && candidateData.isNotEmpty) {} + if (candidateData.isNotEmpty) {} return Container(); }, - onWillAccept: (incoming) { + onWillAcceptWithDetails: (details) { bool accept = true; - if (widget.parameters.itemOnWillAccept != null) - accept = - widget.parameters.itemOnWillAccept(incoming, widget.child); + if (widget.parameters!.itemOnWillAccept != null) { + accept = widget.parameters!.itemOnWillAccept!( + details.data, widget.child); + } if (accept && mounted) { setState(() { - _hoveredDraggable = incoming; + _hoveredDraggable = details.data; }); } return accept; }, - onLeave: (incoming) { + onLeave: (data) { if (mounted) { setState(() { _hoveredDraggable = null; }); } }, - onAccept: (incoming) { + onAcceptWithDetails: (details) { if (mounted) { setState(() { - if (widget.parameters.onItemReordered != null) - widget.parameters.onItemReordered(incoming, widget.child); + if (widget.parameters!.onItemReordered != null) { + widget.parameters!.onItemReordered!(details.data, widget.child); + } _hoveredDraggable = null; }); } @@ -248,12 +251,12 @@ class _DragAndDropItemWrapper extends State Offset _feedbackContainerOffset() { double xOffset; double yOffset; - if (widget.parameters.dragHandleOnLeft) { + if (widget.parameters!.itemDragHandle!.onLeft) { xOffset = 0; } else { xOffset = -_containerSize.width + _dragHandleSize.width; } - if (widget.parameters.itemDragHandleVerticalAlignment == + if (widget.parameters!.itemDragHandle!.verticalAlignment == DragHandleVerticalAlignment.bottom) { yOffset = -_containerSize.height + _dragHandleSize.width; } else { @@ -263,10 +266,10 @@ class _DragAndDropItemWrapper extends State return Offset(xOffset, yOffset); } - void _setContainerSize(Size size) { + void _setContainerSize(Size? size) { if (mounted) { setState(() { - _containerSize = size; + _containerSize = size!; }); } } @@ -276,13 +279,13 @@ class _DragAndDropItemWrapper extends State setState(() { _dragging = dragging; }); - if (widget.parameters.onItemDraggingChanged != null) { - widget.parameters.onItemDraggingChanged(widget.child, dragging); + if (widget.parameters!.onItemDraggingChanged != null) { + widget.parameters!.onItemDraggingChanged!(widget.child, dragging); } } } void _onPointerMove(PointerMoveEvent event) { - if (_dragging) widget.parameters.onPointerMove(event); + if (_dragging) widget.parameters!.onPointerMove!(event); } } diff --git a/lib/drag_and_drop_list.dart b/lib/drag_and_drop_list.dart index a446535..ec005c9 100644 --- a/lib/drag_and_drop_list.dart +++ b/lib/drag_and_drop_list.dart @@ -8,28 +8,28 @@ import 'package:flutter/widgets.dart'; class DragAndDropList implements DragAndDropListInterface { /// The widget that is displayed at the top of the list. - final Widget header; + final Widget? header; /// The widget that is displayed at the bottom of the list. - final Widget footer; + final Widget? footer; /// The widget that is displayed to the left of the list. - final Widget leftSide; + final Widget? leftSide; /// The widget that is displayed to the right of the list. - final Widget rightSide; + final Widget? rightSide; /// The widget to be displayed when a list is empty. /// If this is not null, it will override that set in [DragAndDropLists.contentsWhenEmpty]. - final Widget contentsWhenEmpty; + final Widget? contentsWhenEmpty; /// The widget to be displayed as the last element in the list that will accept /// a dragged item. - final Widget lastTarget; + final Widget? lastTarget; /// The decoration displayed around a list. /// If this is not null, it will override that set in [DragAndDropLists.listDecoration]. - final Decoration decoration; + final Decoration? decoration; /// The vertical alignment of the contents in this list. /// If this is not null, it will override that set in [DragAndDropLists.verticalAlignment]. @@ -41,35 +41,36 @@ class DragAndDropList implements DragAndDropListInterface { /// The child elements that will be contained in this list. /// It is possible to not provide any children when an empty list is desired. - final List children = List(); + @override + final List children; /// Whether or not this item can be dragged. /// Set to true if it can be reordered. /// Set to false if it must remain fixed. + @override final bool canDrag; - - DragAndDropList( - {List children, - this.header, - this.footer, - this.leftSide, - this.rightSide, - this.contentsWhenEmpty, - this.lastTarget, - this.decoration, - this.horizontalAlignment = MainAxisAlignment.start, - this.verticalAlignment = CrossAxisAlignment.start, - this.canDrag = true}) { - if (children != null) { - children.forEach((element) => this.children.add(element)); - } - } + @override + final Key? key; + DragAndDropList({ + required this.children, + this.key, + this.header, + this.footer, + this.leftSide, + this.rightSide, + this.contentsWhenEmpty, + this.lastTarget, + this.decoration, + this.horizontalAlignment = MainAxisAlignment.start, + this.verticalAlignment = CrossAxisAlignment.start, + this.canDrag = true, + }); @override Widget generateWidget(DragAndDropBuilderParameters params) { - var contents = List(); + var contents = []; if (header != null) { - contents.add(Flexible(child: header)); + contents.add(Flexible(child: header!)); } Widget intrinsicHeight = IntrinsicHeight( child: Row( @@ -80,7 +81,7 @@ class DragAndDropList implements DragAndDropListInterface { ), ); if (params.axis == Axis.horizontal) { - intrinsicHeight = Container( + intrinsicHeight = SizedBox( width: params.listWidth, child: intrinsicHeight, ); @@ -94,13 +95,14 @@ class DragAndDropList implements DragAndDropListInterface { contents.add(intrinsicHeight); if (footer != null) { - contents.add(Flexible(child: footer)); + contents.add(Flexible(child: footer!)); } return Container( + key: key, width: params.axis == Axis.vertical ? double.infinity - : params.listWidth - params.listPadding.horizontal, + : params.listWidth - params.listPadding!.horizontal, decoration: decoration ?? params.listDecoration, child: Column( mainAxisSize: MainAxisSize.min, @@ -111,40 +113,41 @@ class DragAndDropList implements DragAndDropListInterface { } List _generateDragAndDropListInnerContents( - DragAndDropBuilderParameters params) { - var contents = List(); + DragAndDropBuilderParameters parameters) { + var contents = []; if (leftSide != null) { - contents.add(leftSide); + contents.add(leftSide!); } - if (children != null && children.isNotEmpty) { - List allChildren = List(); - if (params.addLastItemTargetHeightToTop) { + if (children.isNotEmpty) { + List allChildren = []; + if (parameters.addLastItemTargetHeightToTop) { allChildren.add(Padding( - padding: EdgeInsets.only(top: params.lastItemTargetHeight), + padding: EdgeInsets.only(top: parameters.lastItemTargetHeight), )); } for (int i = 0; i < children.length; i++) { allChildren.add(DragAndDropItemWrapper( + key: children[i].key, child: children[i], - parameters: params, + parameters: parameters, )); - if (params.itemDivider != null && i < children.length - 1) { - allChildren.add(params.itemDivider); + if (parameters.itemDivider != null && i < children.length - 1) { + allChildren.add(parameters.itemDivider!); } } allChildren.add(DragAndDropItemTarget( parent: this, - parameters: params, - onReorderOrAdd: params.onItemDropOnLastTarget, + parameters: parameters, + onReorderOrAdd: parameters.onItemDropOnLastTarget!, child: lastTarget ?? Container( - height: params.lastItemTargetHeight, + height: parameters.lastItemTargetHeight, ), )); contents.add( Expanded( child: SingleChildScrollView( - physics: NeverScrollableScrollPhysics(), + physics: const NeverScrollableScrollPhysics(), child: Column( crossAxisAlignment: verticalAlignment, mainAxisSize: MainAxisSize.max, @@ -157,12 +160,12 @@ class DragAndDropList implements DragAndDropListInterface { contents.add( Expanded( child: SingleChildScrollView( - physics: NeverScrollableScrollPhysics(), + physics: const NeverScrollableScrollPhysics(), child: Column( mainAxisSize: MainAxisSize.max, children: [ contentsWhenEmpty ?? - Text( + const Text( 'Empty list', style: TextStyle( fontStyle: FontStyle.italic, @@ -170,11 +173,11 @@ class DragAndDropList implements DragAndDropListInterface { ), DragAndDropItemTarget( parent: this, - parameters: params, - onReorderOrAdd: params.onItemDropOnLastTarget, + parameters: parameters, + onReorderOrAdd: parameters.onItemDropOnLastTarget!, child: lastTarget ?? Container( - height: params.lastItemTargetHeight, + height: parameters.lastItemTargetHeight, ), ), ], @@ -184,7 +187,7 @@ class DragAndDropList implements DragAndDropListInterface { ); } if (rightSide != null) { - contents.add(rightSide); + contents.add(rightSide!); } return contents; } diff --git a/lib/drag_and_drop_list_expansion.dart b/lib/drag_and_drop_list_expansion.dart index 453a48b..e38e1a1 100644 --- a/lib/drag_and_drop_list_expansion.dart +++ b/lib/drag_and_drop_list_expansion.dart @@ -7,16 +7,15 @@ import 'package:drag_and_drop_lists/drag_and_drop_item_wrapper.dart'; import 'package:drag_and_drop_lists/drag_and_drop_list_interface.dart'; import 'package:drag_and_drop_lists/programmatic_expansion_tile.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; -typedef void OnExpansionChanged(bool expanded); +typedef OnExpansionChanged = void Function(bool expanded); /// This class mirrors flutter's [ExpansionTile], with similar options. class DragAndDropListExpansion implements DragAndDropListExpansionInterface { - final Widget title; - final Widget subtitle; - final Widget trailing; - final Widget leading; + final Widget? title; + final Widget? subtitle; + final Widget? trailing; + final Widget? leading; final bool initiallyExpanded; /// Set this to a unique key that will remain unchanged over the lifetime of the list. @@ -24,22 +23,27 @@ class DragAndDropListExpansion implements DragAndDropListExpansionInterface { final Key listKey; /// This function will be called when the expansion of a tile is changed. - final OnExpansionChanged onExpansionChanged; - final Color backgroundColor; - final List children; - final Widget contentsWhenEmpty; - final Widget lastTarget; + final OnExpansionChanged? onExpansionChanged; + final Color? backgroundColor; + @override + final List? children; + final Widget? contentsWhenEmpty; + final Widget? lastTarget; /// Whether or not this item can be dragged. /// Set to true if it can be reordered. /// Set to false if it must remain fixed. + @override final bool canDrag; + @override + final Key? key; + /// Disable to borders displayed at the top and bottom when expanded final bool disableTopAndBottomBorders; - ValueNotifier _expanded = ValueNotifier(true); - GlobalKey _expansionKey = + final ValueNotifier _expanded = ValueNotifier(true); + final GlobalKey _expansionKey = GlobalKey(); DragAndDropListExpansion({ @@ -53,10 +57,11 @@ class DragAndDropListExpansion implements DragAndDropListExpansionInterface { this.onExpansionChanged, this.contentsWhenEmpty, this.lastTarget, - this.listKey, + required this.listKey, this.canDrag = true, + this.key, this.disableTopAndBottomBorders = false, - }) : assert(listKey != null) { + }) { _expanded.value = initiallyExpanded; } @@ -87,7 +92,7 @@ class DragAndDropListExpansion implements DragAndDropListExpansionInterface { if (params.listPadding != null) { expandable = Padding( - padding: params.listPadding, + padding: params.listPadding!, child: expandable, ); } @@ -95,31 +100,29 @@ class DragAndDropListExpansion implements DragAndDropListExpansionInterface { Widget toReturn = ValueListenableBuilder( valueListenable: _expanded, child: expandable, - builder: (context, error, child) { + builder: (context, dynamic error, child) { if (!_expanded.value) { - return Stack( - children: [ - child, - Positioned.fill( - child: DragTarget( - builder: (context, candidateData, rejectedData) { - if (candidateData != null && candidateData.isNotEmpty) {} - return Container(); - }, - onWillAccept: (incoming) { - _startExpansionTimer(); - return false; - }, - onLeave: (incoming) { - _stopExpansionTimer(); - }, - onAccept: (incoming) {}, - ), - ) - ], - ); + return Stack(children: [ + child!, + Positioned.fill( + child: DragTarget( + builder: (context, candidateData, rejectedData) { + if (candidateData.isNotEmpty) {} + return Container(); + }, + onWillAcceptWithDetails: (details) { + _startExpansionTimer(); + return false; + }, + onLeave: (data) { + _stopExpansionTimer(); + }, + onAcceptWithDetails: (details) {}, + ), + ) + ]); } else { - return child; + return child!; } }, ); @@ -128,31 +131,31 @@ class DragAndDropListExpansion implements DragAndDropListExpansionInterface { } List _generateDragAndDropListInnerContents( - DragAndDropBuilderParameters params) { - var contents = List(); - if (children != null && children.isNotEmpty) { - for (int i = 0; i < children.length; i++) { + DragAndDropBuilderParameters parameters) { + var contents = []; + if (children != null && children!.isNotEmpty) { + for (int i = 0; i < children!.length; i++) { contents.add(DragAndDropItemWrapper( - child: children[i], - parameters: params, + child: children![i], + parameters: parameters, )); - if (params.itemDivider != null && i < children.length - 1) { - contents.add(params.itemDivider); + if (parameters.itemDivider != null && i < children!.length - 1) { + contents.add(parameters.itemDivider!); } } contents.add(DragAndDropItemTarget( parent: this, - parameters: params, - onReorderOrAdd: params.onItemDropOnLastTarget, + parameters: parameters, + onReorderOrAdd: parameters.onItemDropOnLastTarget!, child: lastTarget ?? Container( - height: params.lastItemTargetHeight, + height: parameters.lastItemTargetHeight, ), )); } else { contents.add( contentsWhenEmpty ?? - Text( + const Text( 'Empty list', style: TextStyle( fontStyle: FontStyle.italic, @@ -162,11 +165,11 @@ class DragAndDropListExpansion implements DragAndDropListExpansionInterface { contents.add( DragAndDropItemTarget( parent: this, - parameters: params, - onReorderOrAdd: params.onItemDropOnLastTarget, + parameters: parameters, + onReorderOrAdd: parameters.onItemDropOnLastTarget!, child: lastTarget ?? Container( - height: params.lastItemTargetHeight, + height: parameters.lastItemTargetHeight, ), ), ); @@ -176,17 +179,18 @@ class DragAndDropListExpansion implements DragAndDropListExpansionInterface { @override toggleExpanded() { - if (isExpanded) + if (isExpanded) { collapse(); - else + } else { expand(); + } } @override collapse() { if (!isExpanded) { _expanded.value = false; - _expansionKey.currentState.collapse(); + _expansionKey.currentState!.collapse(); } } @@ -194,23 +198,23 @@ class DragAndDropListExpansion implements DragAndDropListExpansionInterface { expand() { if (!isExpanded) { _expanded.value = true; - _expansionKey.currentState.expand(); + _expansionKey.currentState!.expand(); } } _onSetExpansion(bool expanded) { _expanded.value = expanded; - if (onExpansionChanged != null) onExpansionChanged(expanded); + if (onExpansionChanged != null) onExpansionChanged!(expanded); } @override get isExpanded => _expanded.value; - Timer _expansionTimer; + late Timer _expansionTimer; _startExpansionTimer() async { - _expansionTimer = Timer(Duration(milliseconds: 400), _expansionCallback); + _expansionTimer = Timer(const Duration(milliseconds: 400), _expansionCallback); } _stopExpansionTimer() async { diff --git a/lib/drag_and_drop_list_interface.dart b/lib/drag_and_drop_list_interface.dart index d793016..e26b456 100644 --- a/lib/drag_and_drop_list_interface.dart +++ b/lib/drag_and_drop_list_interface.dart @@ -4,19 +4,20 @@ import 'package:drag_and_drop_lists/drag_and_drop_item.dart'; import 'package:flutter/material.dart'; abstract class DragAndDropListInterface implements DragAndDropInterface { - List get children; + List? get children; /// Whether or not this item can be dragged. /// Set to true if it can be reordered. /// Set to false if it must remain fixed. bool get canDrag; - + Key? get key; Widget generateWidget(DragAndDropBuilderParameters params); } abstract class DragAndDropListExpansionInterface implements DragAndDropListInterface { - final List children; + @override + final List? children; DragAndDropListExpansionInterface({this.children}); diff --git a/lib/drag_and_drop_list_target.dart b/lib/drag_and_drop_list_target.dart index 2b12b52..e8184e1 100644 --- a/lib/drag_and_drop_list_target.dart +++ b/lib/drag_and_drop_list_target.dart @@ -3,24 +3,23 @@ import 'package:drag_and_drop_lists/drag_and_drop_list_interface.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; -typedef void OnDropOnLastTarget( +typedef OnDropOnLastTarget = void Function( DragAndDropListInterface newOrReordered, DragAndDropListTarget receiver, ); class DragAndDropListTarget extends StatefulWidget { - final Widget child; + final Widget? child; final DragAndDropBuilderParameters parameters; final OnDropOnLastTarget onDropOnLastTarget; final double lastListTargetSize; - DragAndDropListTarget( + const DragAndDropListTarget( {this.child, - @required this.parameters, - @required this.onDropOnLastTarget, + required this.parameters, + required this.onDropOnLastTarget, this.lastListTargetSize = 110, - Key key}) - : super(key: key); + super.key}); @override State createState() => _DragAndDropListTarget(); @@ -28,7 +27,7 @@ class DragAndDropListTarget extends StatefulWidget { class _DragAndDropListTarget extends State with TickerProviderStateMixin { - DragAndDropListInterface _hoveredDraggable; + DragAndDropListInterface? _hoveredDraggable; @override Widget build(BuildContext context) { @@ -37,7 +36,6 @@ class _DragAndDropListTarget extends State AnimatedSize( duration: Duration( milliseconds: widget.parameters.listSizeAnimationDuration), - vsync: this, alignment: widget.parameters.axis == Axis.vertical ? Alignment.bottomCenter : Alignment.centerLeft, @@ -45,25 +43,25 @@ class _DragAndDropListTarget extends State ? Opacity( opacity: widget.parameters.listGhostOpacity, child: widget.parameters.listGhost ?? - _hoveredDraggable.generateWidget(widget.parameters), + _hoveredDraggable!.generateWidget(widget.parameters), ) : Container(), ), widget.child ?? - Container( + SizedBox( height: widget.parameters.axis == Axis.vertical ? widget.lastListTargetSize : null, width: widget.parameters.axis == Axis.horizontal - ? 110 - : widget.lastListTargetSize, + ? widget.lastListTargetSize + : null, ), ], ); if (widget.parameters.listPadding != null) { visibleContents = Padding( - padding: widget.parameters.listPadding, + padding: widget.parameters.listPadding!, child: visibleContents, ); } @@ -78,34 +76,33 @@ class _DragAndDropListTarget extends State Positioned.fill( child: DragTarget( builder: (context, candidateData, rejectedData) { - if (candidateData != null && candidateData.isNotEmpty) {} + if (candidateData.isNotEmpty) {} return Container(); }, - onWillAccept: (incoming) { + onWillAcceptWithDetails: (details) { bool accept = true; if (widget.parameters.listTargetOnWillAccept != null) { accept = - widget.parameters.listTargetOnWillAccept(incoming, widget); + widget.parameters.listTargetOnWillAccept!(details.data, widget); } if (accept && mounted) { setState(() { - _hoveredDraggable = incoming; + _hoveredDraggable = details.data; }); } return accept; }, - onLeave: (incoming) { + onLeave: (data) { if (mounted) { setState(() { _hoveredDraggable = null; }); } }, - onAccept: (incoming) { + onAcceptWithDetails: (details) { if (mounted) { setState(() { - if (widget.onDropOnLastTarget != null) - widget.onDropOnLastTarget(incoming, widget); + widget.onDropOnLastTarget(details.data, widget); _hoveredDraggable = null; }); } diff --git a/lib/drag_and_drop_list_wrapper.dart b/lib/drag_and_drop_list_wrapper.dart index 2b50920..75c9d4a 100644 --- a/lib/drag_and_drop_list_wrapper.dart +++ b/lib/drag_and_drop_list_wrapper.dart @@ -1,16 +1,15 @@ import 'package:drag_and_drop_lists/drag_and_drop_builder_parameters.dart'; import 'package:drag_and_drop_lists/drag_and_drop_list_interface.dart'; +import 'package:drag_and_drop_lists/drag_handle.dart'; import 'package:drag_and_drop_lists/measure_size.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; -import 'package:flutter/widgets.dart'; class DragAndDropListWrapper extends StatefulWidget { final DragAndDropListInterface dragAndDropList; final DragAndDropBuilderParameters parameters; - DragAndDropListWrapper({this.dragAndDropList, this.parameters, Key key}) - : super(key: key); + const DragAndDropListWrapper( + {required this.dragAndDropList, required this.parameters, super.key}); @override State createState() => _DragAndDropListWrapper(); @@ -18,7 +17,7 @@ class DragAndDropListWrapper extends StatefulWidget { class _DragAndDropListWrapper extends State with TickerProviderStateMixin { - DragAndDropListInterface _hoveredDraggable; + DragAndDropListInterface? _hoveredDraggable; bool _dragging = false; Size _containerSize = Size.zero; @@ -36,10 +35,10 @@ class _DragAndDropListWrapper extends State Widget draggable; if (widget.dragAndDropList.canDrag) { - if (widget.parameters.dragHandle != null) { + if (widget.parameters.listDragHandle != null) { Widget dragHandle = MouseRegion( cursor: SystemMouseCursors.grab, - child: widget.parameters.dragHandle, + child: widget.parameters.listDragHandle, ); Widget feedback = @@ -48,7 +47,7 @@ class _DragAndDropListWrapper extends State draggable = MeasureSize( onSizeChange: (size) { setState(() { - _containerSize = size; + _containerSize = size!; }); }, child: Stack( @@ -59,20 +58,12 @@ class _DragAndDropListWrapper extends State ), // dragAndDropListContents, Positioned( - right: widget.parameters.dragHandleOnLeft ? null : 0, - left: widget.parameters.dragHandleOnLeft ? 0 : null, + right: widget.parameters.listDragHandle!.onLeft ? null : 0, + left: widget.parameters.listDragHandle!.onLeft ? 0 : null, top: _dragHandleDistanceFromTop(), child: Draggable( data: widget.dragAndDropList, axis: draggableAxis(), - child: MeasureSize( - onSizeChange: (size) { - setState(() { - _dragHandleSize = size; - }); - }, - child: dragHandle, - ), feedback: Transform.translate( offset: _feedbackContainerOffset(), child: feedback, @@ -82,6 +73,14 @@ class _DragAndDropListWrapper extends State onDragCompleted: () => _setDragging(false), onDraggableCanceled: (_, __) => _setDragging(false), onDragEnd: (_) => _setDragging(false), + child: MeasureSize( + onSizeChange: (size) { + setState(() { + _dragHandleSize = size!; + }); + }, + child: dragHandle, + ), ), ), ], @@ -91,7 +90,6 @@ class _DragAndDropListWrapper extends State draggable = LongPressDraggable( data: widget.dragAndDropList, axis: draggableAxis(), - child: dragAndDropListContents, feedback: buildFeedbackWithoutHandle(context, dragAndDropListContents), childWhenDragging: Container(), @@ -99,12 +97,12 @@ class _DragAndDropListWrapper extends State onDragCompleted: () => _setDragging(false), onDraggableCanceled: (_, __) => _setDragging(false), onDragEnd: (_) => _setDragging(false), + child: dragAndDropListContents, ); } else { draggable = Draggable( data: widget.dragAndDropList, axis: draggableAxis(), - child: dragAndDropListContents, feedback: buildFeedbackWithoutHandle(context, dragAndDropListContents), childWhenDragging: Container(), @@ -112,6 +110,7 @@ class _DragAndDropListWrapper extends State onDragCompleted: () => _setDragging(false), onDraggableCanceled: (_, __) => _setDragging(false), onDragEnd: (_) => _setDragging(false), + child: dragAndDropListContents, ); } } else { @@ -122,7 +121,6 @@ class _DragAndDropListWrapper extends State AnimatedSize( duration: Duration(milliseconds: widget.parameters.listSizeAnimationDuration), - vsync: this, alignment: widget.parameters.axis == Axis.vertical ? Alignment.bottomCenter : Alignment.centerLeft, @@ -132,21 +130,21 @@ class _DragAndDropListWrapper extends State child: widget.parameters.listGhost ?? Container( padding: widget.parameters.axis == Axis.vertical - ? EdgeInsets.all(0) + ? const EdgeInsets.all(0) : EdgeInsets.symmetric( horizontal: - widget.parameters.listPadding.horizontal), + widget.parameters.listPadding!.horizontal), child: - _hoveredDraggable.generateWidget(widget.parameters), + _hoveredDraggable!.generateWidget(widget.parameters), ), ) : Container(), ), Listener( - child: draggable, onPointerMove: _onPointerMove, onPointerDown: widget.parameters.onPointerDown, onPointerUp: widget.parameters.onPointerUp, + child: draggable, ), ]; @@ -163,23 +161,23 @@ class _DragAndDropListWrapper extends State Positioned.fill( child: DragTarget( builder: (context, candidateData, rejectedData) { - if (candidateData != null && candidateData.isNotEmpty) {} + if (candidateData.isNotEmpty) {} return Container(); }, - onWillAccept: (incoming) { + onWillAcceptWithDetails: (details) { bool accept = true; if (widget.parameters.listOnWillAccept != null) { - accept = widget.parameters - .listOnWillAccept(incoming, widget.dragAndDropList); + accept = widget.parameters.listOnWillAccept!( + details.data, widget.dragAndDropList); } if (accept && mounted) { setState(() { - _hoveredDraggable = incoming; + _hoveredDraggable = details.data; }); } return accept; }, - onLeave: (incoming) { + onLeave: (data) { if (_hoveredDraggable != null) { if (mounted) { setState(() { @@ -188,11 +186,11 @@ class _DragAndDropListWrapper extends State } } }, - onAccept: (incoming) { + onAcceptWithDetails: (details) { if (mounted) { setState(() { - widget.parameters - .onListReordered(incoming, widget.dragAndDropList); + widget.parameters.onListReordered!( + details.data, widget.dragAndDropList); _hoveredDraggable = null; }); } @@ -205,7 +203,7 @@ class _DragAndDropListWrapper extends State Widget toReturn = stack; if (widget.parameters.listPadding != null) { toReturn = Padding( - padding: widget.parameters.listPadding, + padding: widget.parameters.listPadding!, child: stack, ); } @@ -227,19 +225,22 @@ class _DragAndDropListWrapper extends State color: Colors.transparent, child: Container( decoration: widget.parameters.listDecorationWhileDragging, - child: Container( + child: SizedBox( width: widget.parameters.listDraggingWidth ?? _containerSize.width, child: Stack( children: [ - dragAndDropListContents, + Directionality( + textDirection: Directionality.of(context), + child: dragAndDropListContents, + ), Positioned( - right: widget.parameters.dragHandleOnLeft ? null : 0, - left: widget.parameters.dragHandleOnLeft ? 0 : null, - top: widget.parameters.listDragHandleVerticalAlignment == + right: widget.parameters.listDragHandle!.onLeft ? null : 0, + left: widget.parameters.listDragHandle!.onLeft ? 0 : null, + top: widget.parameters.listDragHandle!.verticalAlignment == DragHandleVerticalAlignment.bottom ? null : 0, - bottom: widget.parameters.listDragHandleVerticalAlignment == + bottom: widget.parameters.listDragHandle!.verticalAlignment == DragHandleVerticalAlignment.top ? null : 0, @@ -252,9 +253,9 @@ class _DragAndDropListWrapper extends State ); } - Container buildFeedbackWithoutHandle( + SizedBox buildFeedbackWithoutHandle( BuildContext context, Widget dragAndDropListContents) { - return Container( + return SizedBox( width: widget.parameters.axis == Axis.vertical ? (widget.parameters.listDraggingWidth ?? MediaQuery.of(context).size.width) @@ -264,13 +265,16 @@ class _DragAndDropListWrapper extends State color: Colors.transparent, child: Container( decoration: widget.parameters.listDecorationWhileDragging, - child: dragAndDropListContents, + child: Directionality( + textDirection: Directionality.of(context), + child: dragAndDropListContents, + ), ), ), ); } - Axis draggableAxis() { + Axis? draggableAxis() { return widget.parameters.axis == Axis.vertical && widget.parameters.constrainDraggingAxis ? Axis.vertical @@ -278,7 +282,7 @@ class _DragAndDropListWrapper extends State } double _dragHandleDistanceFromTop() { - switch (widget.parameters.listDragHandleVerticalAlignment) { + switch (widget.parameters.listDragHandle!.verticalAlignment) { case DragHandleVerticalAlignment.top: return 0; case DragHandleVerticalAlignment.center: @@ -293,12 +297,12 @@ class _DragAndDropListWrapper extends State Offset _feedbackContainerOffset() { double xOffset; double yOffset; - if (widget.parameters.dragHandleOnLeft) { + if (widget.parameters.listDragHandle!.onLeft) { xOffset = 0; } else { xOffset = -_containerSize.width + _dragHandleSize.width; } - if (widget.parameters.listDragHandleVerticalAlignment == + if (widget.parameters.listDragHandle!.verticalAlignment == DragHandleVerticalAlignment.bottom) { yOffset = -_containerSize.height + _dragHandleSize.width; } else { @@ -314,13 +318,13 @@ class _DragAndDropListWrapper extends State _dragging = dragging; }); if (widget.parameters.onListDraggingChanged != null) { - widget.parameters - .onListDraggingChanged(widget.dragAndDropList, dragging); + widget.parameters.onListDraggingChanged!( + widget.dragAndDropList, dragging); } } } void _onPointerMove(PointerMoveEvent event) { - if (_dragging) widget.parameters.onPointerMove(event); + if (_dragging) widget.parameters.onPointerMove!(event); } } diff --git a/lib/drag_and_drop_lists.dart b/lib/drag_and_drop_lists.dart index b2790f7..6cc62c6 100644 --- a/lib/drag_and_drop_lists.dart +++ b/lib/drag_and_drop_lists.dart @@ -20,6 +20,7 @@ import 'package:drag_and_drop_lists/drag_and_drop_item_target.dart'; import 'package:drag_and_drop_lists/drag_and_drop_list_interface.dart'; import 'package:drag_and_drop_lists/drag_and_drop_list_target.dart'; import 'package:drag_and_drop_lists/drag_and_drop_list_wrapper.dart'; +import 'package:drag_and_drop_lists/drag_handle.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; @@ -32,51 +33,52 @@ export 'package:drag_and_drop_lists/drag_and_drop_list.dart'; export 'package:drag_and_drop_lists/drag_and_drop_list_expansion.dart'; export 'package:drag_and_drop_lists/drag_and_drop_list_target.dart'; export 'package:drag_and_drop_lists/drag_and_drop_list_wrapper.dart'; +export 'package:drag_and_drop_lists/drag_handle.dart'; -typedef void OnItemReorder( +typedef OnItemReorder = void Function( int oldItemIndex, int oldListIndex, int newItemIndex, int newListIndex, ); -typedef void OnItemAdd( +typedef OnItemAdd = void Function( DragAndDropItem newItem, int listIndex, int newItemIndex, ); -typedef void OnListAdd(DragAndDropListInterface newList, int newListIndex); -typedef void OnListReorder(int oldListIndex, int newListIndex); -typedef void OnListDraggingChanged( - DragAndDropListInterface list, +typedef OnListAdd = void Function(DragAndDropListInterface newList, int newListIndex); +typedef OnListReorder = void Function(int oldListIndex, int newListIndex); +typedef OnListDraggingChanged = void Function( + DragAndDropListInterface? list, bool dragging, ); -typedef bool ListOnWillAccept( - DragAndDropListInterface incoming, - DragAndDropListInterface target, +typedef ListOnWillAccept = bool Function( + DragAndDropListInterface? incoming, + DragAndDropListInterface? target, ); -typedef void ListOnAccept( +typedef ListOnAccept = void Function( DragAndDropListInterface incoming, DragAndDropListInterface target, ); -typedef bool ListTargetOnWillAccept( - DragAndDropListInterface incoming, DragAndDropListTarget target); -typedef void ListTargetOnAccept( +typedef ListTargetOnWillAccept = bool Function( + DragAndDropListInterface? incoming, DragAndDropListTarget target); +typedef ListTargetOnAccept = void Function( DragAndDropListInterface incoming, DragAndDropListTarget target); -typedef void OnItemDraggingChanged( +typedef OnItemDraggingChanged = void Function( DragAndDropItem item, bool dragging, ); -typedef bool ItemOnWillAccept( - DragAndDropItem incoming, +typedef ItemOnWillAccept = bool Function( + DragAndDropItem? incoming, DragAndDropItem target, ); -typedef void ItemOnAccept( +typedef ItemOnAccept = void Function( DragAndDropItem incoming, DragAndDropItem target, ); -typedef bool ItemTargetOnWillAccept( - DragAndDropItem incoming, DragAndDropItemTarget target); -typedef void ItemTargetOnAccept( +typedef ItemTargetOnWillAccept = bool Function( + DragAndDropItem? incoming, DragAndDropItemTarget target); +typedef ItemTargetOnAccept = void Function( DragAndDropItem incoming, DragAndDropListInterface parentList, DragAndDropItemTarget target, @@ -99,67 +101,67 @@ class DragAndDropLists extends StatefulWidget { final OnListReorder onListReorder; /// Calls this function when a new item has been added. - final OnItemAdd onItemAdd; + final OnItemAdd? onItemAdd; /// Calls this function when a new list has been added. - final OnListAdd onListAdd; + final OnListAdd? onListAdd; /// Set in order to provide custom acceptance criteria for when a list can be /// dropped onto a specific other list - final ListOnWillAccept listOnWillAccept; + final ListOnWillAccept? listOnWillAccept; /// Set in order to get the lists involved in a drag and drop operation after /// a list has been accepted. For general use cases where only reordering is /// necessary, only [onListReorder] or [onListAdd] is needed, and this should /// be left null. [onListReorder] or [onListAdd] will be called after this. - final ListOnAccept listOnAccept; + final ListOnAccept? listOnAccept; /// Set in order to provide custom acceptance criteria for when a list can be /// dropped onto a specific target. This target always exists as the last /// target the DragAndDropLists, and also can be used independently. - final ListTargetOnWillAccept listTargetOnWillAccept; + final ListTargetOnWillAccept? listTargetOnWillAccept; /// Set in order to get the list and target involved in a drag and drop /// operation after a list has been accepted. For general use cases where only /// reordering is necessary, only [onListReorder] or [onListAdd] is needed, /// and this should be left null. [onListReorder] or [onListAdd] will be /// called after this. - final ListTargetOnAccept listTargetOnAccept; + final ListTargetOnAccept? listTargetOnAccept; /// Called when a list dragging is starting or ending - final OnListDraggingChanged onListDraggingChanged; + final OnListDraggingChanged? onListDraggingChanged; /// Set in order to provide custom acceptance criteria for when a item can be /// dropped onto a specific other item - final ItemOnWillAccept itemOnWillAccept; + final ItemOnWillAccept? itemOnWillAccept; /// Set in order to get the items involved in a drag and drop operation after /// an item has been accepted. For general use cases where only reordering is /// necessary, only [onItemReorder] or [onItemAdd] is needed, and this should /// be left null. [onItemReorder] or [onItemAdd] will be called after this. - final ItemOnAccept itemOnAccept; + final ItemOnAccept? itemOnAccept; /// Set in order to provide custom acceptance criteria for when a item can be /// dropped onto a specific target. This target always exists as the last /// target for list of items, and also can be used independently. - final ItemTargetOnWillAccept itemTargetOnWillAccept; + final ItemTargetOnWillAccept? itemTargetOnWillAccept; /// Set in order to get the item and target involved in a drag and drop /// operation after a item has been accepted. For general use cases where only /// reordering is necessary, only [onItemReorder] or [onItemAdd] is needed, /// and this should be left null. [onItemReorder] or [onItemAdd] will be /// called after this. - final ItemTargetOnAccept itemTargetOnAccept; + final ItemTargetOnAccept? itemTargetOnAccept; /// Called when an item dragging is starting or ending - final OnItemDraggingChanged onItemDraggingChanged; + final OnItemDraggingChanged? onItemDraggingChanged; /// Width of a list item when it is being dragged. - final double itemDraggingWidth; + final double? itemDraggingWidth; /// The widget that will be displayed at a potential drop position in a list /// when an item is being dragged. - final Widget itemGhost; + final Widget? itemGhost; /// The opacity of the [itemGhost]. This must be between 0 and 1. final double itemGhostOpacity; @@ -171,22 +173,22 @@ class DragAndDropLists extends StatefulWidget { final bool itemDragOnLongPress; /// The decoration surrounding an item while it is in the process of being dragged. - final Decoration itemDecorationWhileDragging; + final Decoration? itemDecorationWhileDragging; /// A widget that will be displayed between each individual item. - final Widget itemDivider; + final Widget? itemDivider; /// The width of a list when dragging. - final double listDraggingWidth; + final double? listDraggingWidth; /// The widget to be displayed as the last element in the DragAndDropLists, /// where a list will be accepted as the last list. - final Widget listTarget; + final Widget? listTarget; /// The widget to be displayed at a potential list position while a list is being dragged. /// This must not be null when [children] includes one or more /// [DragAndDropListExpansion] or other class that inherit from [DragAndDropListExpansionInterface]. - final Widget listGhost; + final Widget? listGhost; /// The opacity of [listGhost]. It must be between 0 and 1. final double listGhostOpacity; @@ -201,25 +203,25 @@ class DragAndDropLists extends StatefulWidget { final bool listDragOnLongPress; /// The decoration surrounding a list. - final Decoration listDecoration; + final Decoration? listDecoration; /// The decoration surrounding a list while it is in the process of being dragged. - final Decoration listDecorationWhileDragging; + final Decoration? listDecorationWhileDragging; /// The decoration surrounding the inner list of items. - final Decoration listInnerDecoration; + final Decoration? listInnerDecoration; /// A widget that will be displayed between each individual list. - final Widget listDivider; + final Widget? listDivider; /// Whether it should put a divider on the last list or not. final bool listDividerOnLastChild; /// The padding between each individual list. - final EdgeInsets listPadding; + final EdgeInsets? listPadding; /// A widget that will be displayed whenever a list contains no items. - final Widget contentsWhenEmpty; + final Widget? contentsWhenEmpty; /// The width of each individual list. This must be set to a finite value when /// [axis] is set to Axis.horizontal. @@ -258,7 +260,7 @@ class DragAndDropLists extends StatefulWidget { /// A scroll controller that can be used for the scrolling of the first level lists. /// This must be set if [sliverList] is set to true. - final ScrollController scrollController; + final ScrollController? scrollController; /// Set to true in order to disable all scrolling of the lists. /// Note: to disable scrolling for sliver lists, it is also necessary in your @@ -267,26 +269,27 @@ class DragAndDropLists extends StatefulWidget { /// Set a custom drag handle to use iOS-like handles to drag rather than long /// or short presses - final Widget dragHandle; - - /// Set the drag handle to be on the left side instead of the default right side - final bool dragHandleOnLeft; - - /// Align the list drag handle to the top, center, or bottom - final DragHandleVerticalAlignment listDragHandleVerticalAlignment; + final DragHandle? listDragHandle; - /// Align the item drag handle to the top, center, or bottom - final DragHandleVerticalAlignment itemDragHandleVerticalAlignment; + /// Set a custom drag handle to use iOS-like handles to drag rather than long + /// or short presses + final DragHandle? itemDragHandle; /// Constrain the dragging axis in a vertical list to only allow dragging on /// the vertical axis. By default this is set to true. This may be useful to /// disable when setting customDragTargets final bool constrainDraggingAxis; + + /// If you put a widget before DragAndDropLists there's an unexpected padding + /// before the list renders. This is the default behaviour for ListView which + /// is used internally. To remove the padding, set this field to true + /// https://github.com/flutter/flutter/issues/14842#issuecomment-371344881 + final bool removeTopPadding; DragAndDropLists({ - this.children, - this.onItemReorder, - this.onListReorder, + required this.children, + required this.onItemReorder, + required this.onListReorder, this.onItemAdd, this.onListAdd, this.onListDraggingChanged, @@ -329,19 +332,19 @@ class DragAndDropLists extends StatefulWidget { this.sliverList = false, this.scrollController, this.disableScrolling = false, - this.dragHandle, - this.dragHandleOnLeft = false, - this.listDragHandleVerticalAlignment = DragHandleVerticalAlignment.top, - this.itemDragHandleVerticalAlignment = DragHandleVerticalAlignment.center, + this.listDragHandle, + this.itemDragHandle, this.constrainDraggingAxis = true, - Key key, - }) : super(key: key) { + this.removeTopPadding = false, + super.key, + }) { if (listGhost == null && children - .where((element) => element is DragAndDropListExpansionInterface) - .isNotEmpty) + .whereType() + .isNotEmpty) { throw Exception( 'If using DragAndDropListExpansion, you must provide a non-null listGhost'); + } if (sliverList && scrollController == null) { throw Exception( 'A scroll controller must be provided when using sliver lists'); @@ -361,19 +364,20 @@ class DragAndDropLists extends StatefulWidget { } class DragAndDropListsState extends State { - ScrollController _scrollController; + ScrollController? _scrollController; bool _pointerDown = false; - double _pointerYPosition; - double _pointerXPosition; + double? _pointerYPosition; + double? _pointerXPosition; bool _scrolling = false; - PageStorageBucket _pageStorageBucket = PageStorageBucket(); + final PageStorageBucket _pageStorageBucket = PageStorageBucket(); @override void initState() { - if (widget.scrollController != null) + if (widget.scrollController != null) { _scrollController = widget.scrollController; - else + } else { _scrollController = ScrollController(); + } super.initState(); } @@ -413,22 +417,20 @@ class DragAndDropListsState extends State { listWidth: widget.listWidth, lastItemTargetHeight: widget.lastItemTargetHeight, addLastItemTargetHeightToTop: widget.addLastItemTargetHeightToTop, - dragHandle: widget.dragHandle, - dragHandleOnLeft: widget.dragHandleOnLeft, - itemDragHandleVerticalAlignment: widget.itemDragHandleVerticalAlignment, - listDragHandleVerticalAlignment: widget.listDragHandleVerticalAlignment, + listDragHandle: widget.listDragHandle, + itemDragHandle: widget.itemDragHandle, constrainDraggingAxis: widget.constrainDraggingAxis, disableScrolling: widget.disableScrolling, ); DragAndDropListTarget dragAndDropListTarget = DragAndDropListTarget( - child: widget.listTarget, parameters: parameters, onDropOnLastTarget: _internalOnListDropOnLastTarget, lastListTargetSize: widget.lastListTargetSize, + child: widget.listTarget, ); - if (widget.children != null && widget.children.isNotEmpty) { + if (widget.children.isNotEmpty) { Widget outerListHolder; if (widget.sliverList) { @@ -441,11 +443,11 @@ class DragAndDropListsState extends State { } if (widget.children - .where((e) => e is DragAndDropListExpansionInterface) + .whereType() .isNotEmpty) { outerListHolder = PageStorage( - child: outerListHolder, bucket: _pageStorageBucket, + child: outerListHolder, ); } return outerListHolder; @@ -454,7 +456,7 @@ class DragAndDropListsState extends State { child: Column( mainAxisSize: MainAxisSize.min, children: [ - widget.contentsWhenEmpty ?? Text('Empty'), + widget.contentsWhenEmpty ?? const Text('Empty'), dragAndDropListTarget, ], ), @@ -491,13 +493,21 @@ class DragAndDropListsState extends State { } } - ListView _buildListView(DragAndDropBuilderParameters parameters, + Widget _buildListView(DragAndDropBuilderParameters parameters, DragAndDropListTarget dragAndDropListTarget) { - return ListView( + Widget listView = ListView( scrollDirection: widget.axis, controller: _scrollController, children: _buildOuterList(dragAndDropListTarget, parameters), ); + + return widget.removeTopPadding + ? MediaQuery.removePadding( + removeTop: true, + context: context, + child: listView, + ) + : listView; } List _buildOuterList(DragAndDropListTarget dragAndDropListTarget, @@ -512,12 +522,13 @@ class DragAndDropListsState extends State { } int _calculateChildrenCount(bool includeSeparators) { - if (includeSeparators) - return ((widget.children?.length ?? 0) * 2) - + if (includeSeparators) { + return (widget.children.length * 2) - (widget.listDividerOnLastChild ? 0 : 1) + 1; - else - return (widget.children?.length ?? 0) + 1; + } else { + return widget.children.length + 1; + } } Widget _buildInnerList( @@ -529,7 +540,7 @@ class DragAndDropListsState extends State { if (index == childrenCount - 1) { return dragAndDropListTarget; } else if (includeSeparators && index.isOdd) { - return widget.listDivider; + return widget.listDivider!; } else { return DragAndDropListWrapper( dragAndDropList: @@ -541,7 +552,7 @@ class DragAndDropListsState extends State { _internalOnItemReorder(DragAndDropItem reordered, DragAndDropItem receiver) { if (widget.itemOnAccept != null) { - widget.itemOnAccept(reordered, receiver); + widget.itemOnAccept!(reordered, receiver); } int reorderedListIndex = -1; @@ -552,12 +563,12 @@ class DragAndDropListsState extends State { for (int i = 0; i < widget.children.length; i++) { if (reorderedItemIndex == -1) { reorderedItemIndex = - widget.children[i].children.indexWhere((e) => reordered == e); + widget.children[i].children!.indexWhere((e) => reordered == e); if (reorderedItemIndex != -1) reorderedListIndex = i; } if (receiverItemIndex == -1) { receiverItemIndex = - widget.children[i].children.indexWhere((e) => receiver == e); + widget.children[i].children!.indexWhere((e) => receiver == e); if (receiverItemIndex != -1) receiverListIndex = i; } if (reorderedItemIndex != -1 && receiverItemIndex != -1) { @@ -567,8 +578,9 @@ class DragAndDropListsState extends State { if (reorderedItemIndex == -1) { // this is a new item - if (widget.onItemAdd != null) - widget.onItemAdd(reordered, receiverListIndex, receiverItemIndex); + if (widget.onItemAdd != null) { + widget.onItemAdd!(reordered, receiverListIndex, receiverItemIndex); + } } else { if (reorderedListIndex == receiverListIndex && receiverItemIndex > reorderedItemIndex) { @@ -576,9 +588,8 @@ class DragAndDropListsState extends State { receiverItemIndex--; } - if (widget.onItemReorder != null) - widget.onItemReorder(reorderedItemIndex, reorderedListIndex, - receiverItemIndex, receiverListIndex); + widget.onItemReorder(reorderedItemIndex, reorderedListIndex, + receiverItemIndex, receiverListIndex); } } @@ -589,25 +600,24 @@ class DragAndDropListsState extends State { int newListIndex = receiverListIndex; - if (widget.listOnAccept != null) widget.listOnAccept(reordered, receiver); + if (widget.listOnAccept != null) widget.listOnAccept!(reordered, receiver); if (reorderedListIndex == -1) { // this is a new list - if (widget.onListAdd != null) widget.onListAdd(reordered, newListIndex); + if (widget.onListAdd != null) widget.onListAdd!(reordered, newListIndex); } else { if (newListIndex > reorderedListIndex) { // same list, so if the new position is after the old position, the removal of the old item must be taken into account newListIndex--; } - if (widget.onListReorder != null) - widget.onListReorder(reorderedListIndex, newListIndex); + widget.onListReorder(reorderedListIndex, newListIndex); } } _internalOnItemDropOnLastTarget(DragAndDropItem newOrReordered, DragAndDropListInterface parentList, DragAndDropItemTarget receiver) { if (widget.itemTargetOnAccept != null) { - widget.itemTargetOnAccept(newOrReordered, parentList, receiver); + widget.itemTargetOnAccept!(newOrReordered, parentList, receiver); } int reorderedListIndex = -1; @@ -615,7 +625,7 @@ class DragAndDropListsState extends State { int receiverListIndex = -1; int receiverItemIndex = -1; - if (widget.children != null && widget.children.isNotEmpty) { + if (widget.children.isNotEmpty) { for (int i = 0; i < widget.children.length; i++) { if (reorderedItemIndex == -1) { reorderedItemIndex = widget.children[i].children @@ -636,17 +646,18 @@ class DragAndDropListsState extends State { } if (reorderedItemIndex == -1) { - if (widget.onItemAdd != null) - widget.onItemAdd(newOrReordered, receiverListIndex, reorderedItemIndex); + if (widget.onItemAdd != null) { + widget.onItemAdd!( + newOrReordered, receiverListIndex, reorderedItemIndex); + } } else { if (reorderedListIndex == receiverListIndex && receiverItemIndex > reorderedItemIndex) { // same list, so if the new position is after the old position, the removal of the old item must be taken into account receiverItemIndex--; } - if (widget.onItemReorder != null) - widget.onItemReorder(reorderedItemIndex, reorderedListIndex, - receiverItemIndex, receiverListIndex); + widget.onItemReorder(reorderedItemIndex, reorderedListIndex, + receiverItemIndex, receiverListIndex); } } @@ -656,15 +667,16 @@ class DragAndDropListsState extends State { int reorderedListIndex = widget.children.indexWhere((e) => newOrReordered == e); - if (widget.listOnAccept != null) - widget.listTargetOnAccept(newOrReordered, receiver); + if (widget.listOnAccept != null) { + widget.listTargetOnAccept!(newOrReordered, receiver); + } if (reorderedListIndex >= 0) { - if (widget.onListReorder != null) - widget.onListReorder(reorderedListIndex, widget.children.length - 1); + widget.onListReorder(reorderedListIndex, widget.children.length - 1); } else { - if (widget.onListAdd != null) - widget.onListAdd(newOrReordered, reorderedListIndex); + if (widget.onListAdd != null) { + widget.onListAdd!(newOrReordered, reorderedListIndex); + } } } @@ -687,87 +699,150 @@ class DragAndDropListsState extends State { _pointerDown = false; } + final int _duration = 30; // in ms + final int _scrollAreaSize = 20; + final double _overDragMin = 5.0; + final double _overDragMax = 20.0; + final double _overDragCoefficient = 3.3; + _scrollList() async { if (!widget.disableScrolling && !_scrolling && _pointerDown && _pointerYPosition != null && _pointerXPosition != null) { - int duration = 30; // in ms - int scrollAreaSize = 20; - double step = 1.5; - double overDragMax = 20.0; - double overDragCoefficient = 5.0; - double newOffset; - - var rb = context.findRenderObject(); - Size size; - if (rb is RenderBox) + double? newOffset; + + var rb = context.findRenderObject()!; + late Size size; + if (rb is RenderBox) { size = rb.size; - else if (rb is RenderSliver) size = rb.paintBounds.size; + } else if (rb is RenderSliver) { + size = rb.paintBounds.size; + } + var topLeftOffset = localToGlobal(rb, Offset.zero); var bottomRightOffset = localToGlobal(rb, size.bottomRight(Offset.zero)); if (widget.axis == Axis.vertical) { - double top = topLeftOffset.dy; - double bottom = bottomRightOffset.dy; - - if (_pointerYPosition < (top + scrollAreaSize) && - _scrollController.position.pixels > - _scrollController.position.minScrollExtent) { - final overDrag = - max((top + scrollAreaSize) - _pointerYPosition, overDragMax); - newOffset = max( - _scrollController.position.minScrollExtent, - _scrollController.position.pixels - - step * overDrag / overDragCoefficient); - } else if (_pointerYPosition > (bottom - scrollAreaSize) && - _scrollController.position.pixels < - _scrollController.position.maxScrollExtent) { - final overDrag = max( - _pointerYPosition - (bottom - scrollAreaSize), overDragMax); - newOffset = min( - _scrollController.position.maxScrollExtent, - _scrollController.position.pixels + - step * overDrag / overDragCoefficient); - } + newOffset = _scrollListVertical(topLeftOffset, bottomRightOffset); } else { - double left = topLeftOffset.dx; - double right = bottomRightOffset.dx; - - if (_pointerXPosition < (left + scrollAreaSize) && - _scrollController.position.pixels > - _scrollController.position.minScrollExtent) { - final overDrag = - max((left + scrollAreaSize) - _pointerXPosition, overDragMax); - newOffset = max( - _scrollController.position.minScrollExtent, - _scrollController.position.pixels - - step * overDrag / overDragCoefficient); - } else if (_pointerXPosition > (right - scrollAreaSize) && - _scrollController.position.pixels < - _scrollController.position.maxScrollExtent) { - final overDrag = max( - _pointerYPosition - (right - scrollAreaSize), overDragMax); - newOffset = min( - _scrollController.position.maxScrollExtent, - _scrollController.position.pixels + - step * overDrag / overDragCoefficient); + var directionality = Directionality.of(context); + if (directionality == TextDirection.ltr) { + newOffset = + _scrollListHorizontalLtr(topLeftOffset, bottomRightOffset); + } else { + newOffset = + _scrollListHorizontalRtl(topLeftOffset, bottomRightOffset); } } if (newOffset != null) { _scrolling = true; - await _scrollController.animateTo(newOffset, - duration: Duration(milliseconds: duration), curve: Curves.linear); + await _scrollController!.animateTo(newOffset, + duration: Duration(milliseconds: _duration), curve: Curves.linear); _scrolling = false; if (_pointerDown) _scrollList(); } } } + double? _scrollListVertical(Offset topLeftOffset, Offset bottomRightOffset) { + double top = topLeftOffset.dy; + double bottom = bottomRightOffset.dy; + double? newOffset; + + var pointerYPosition = _pointerYPosition; + var scrollController = _scrollController; + if (scrollController != null && pointerYPosition != null) { + if (pointerYPosition < (top + _scrollAreaSize) && + scrollController.position.pixels > + scrollController.position.minScrollExtent) { + final overDrag = + max((top + _scrollAreaSize) - pointerYPosition, _overDragMax); + newOffset = max(scrollController.position.minScrollExtent, + scrollController.position.pixels - overDrag / _overDragCoefficient); + } else if (pointerYPosition > (bottom - _scrollAreaSize) && + scrollController.position.pixels < + scrollController.position.maxScrollExtent) { + final overDrag = max( + pointerYPosition - (bottom - _scrollAreaSize), _overDragMax); + newOffset = min(scrollController.position.maxScrollExtent, + scrollController.position.pixels + overDrag / _overDragCoefficient); + } + } + + return newOffset; + } + + double? _scrollListHorizontalLtr( + Offset topLeftOffset, Offset bottomRightOffset) { + double left = topLeftOffset.dx; + double right = bottomRightOffset.dx; + double? newOffset; + + var pointerXPosition = _pointerXPosition; + var scrollController = _scrollController; + if (scrollController != null && pointerXPosition != null) { + if (pointerXPosition < (left + _scrollAreaSize) && + scrollController.position.pixels > + scrollController.position.minScrollExtent) { + // scrolling toward minScrollExtent + final overDrag = min( + (left + _scrollAreaSize) - pointerXPosition + _overDragMin, + _overDragMax); + newOffset = max(scrollController.position.minScrollExtent, + scrollController.position.pixels - overDrag / _overDragCoefficient); + } else if (pointerXPosition > (right - _scrollAreaSize) && + scrollController.position.pixels < + scrollController.position.maxScrollExtent) { + // scrolling toward maxScrollExtent + final overDrag = min( + pointerXPosition - (right - _scrollAreaSize) + _overDragMin, + _overDragMax); + newOffset = min(scrollController.position.maxScrollExtent, + scrollController.position.pixels + overDrag / _overDragCoefficient); + } + } + + return newOffset; + } + + double? _scrollListHorizontalRtl( + Offset topLeftOffset, Offset bottomRightOffset) { + double left = topLeftOffset.dx; + double right = bottomRightOffset.dx; + double? newOffset; + + var pointerXPosition = _pointerXPosition; + var scrollController = _scrollController; + if (scrollController != null && pointerXPosition != null) { + if (pointerXPosition < (left + _scrollAreaSize) && + scrollController.position.pixels < + scrollController.position.maxScrollExtent) { + // scrolling toward maxScrollExtent + final overDrag = min( + (left + _scrollAreaSize) - pointerXPosition + _overDragMin, + _overDragMax); + newOffset = min(scrollController.position.maxScrollExtent, + scrollController.position.pixels + overDrag / _overDragCoefficient); + } else if (pointerXPosition > (right - _scrollAreaSize) && + scrollController.position.pixels > + scrollController.position.minScrollExtent) { + // scrolling toward minScrollExtent + final overDrag = min( + pointerXPosition - (right - _scrollAreaSize) + _overDragMin, + _overDragMax); + newOffset = max(scrollController.position.minScrollExtent, + scrollController.position.pixels - overDrag / _overDragCoefficient); + } + } + + return newOffset; + } + static Offset localToGlobal(RenderObject object, Offset point, - {RenderObject ancestor}) { + {RenderObject? ancestor}) { return MatrixUtils.transformPoint(object.getTransformTo(ancestor), point); } } diff --git a/lib/drag_handle.dart b/lib/drag_handle.dart new file mode 100644 index 0000000..1495057 --- /dev/null +++ b/lib/drag_handle.dart @@ -0,0 +1,30 @@ +import 'package:flutter/widgets.dart'; + +enum DragHandleVerticalAlignment { + top, + center, + bottom, +} + +class DragHandle extends StatelessWidget { + /// Set the drag handle to be on the left side instead of the default right side + final bool onLeft; + + /// Align the list drag handle to the top, center, or bottom + final DragHandleVerticalAlignment verticalAlignment; + + /// Child widget to displaying the drag handle + final Widget child; + + const DragHandle({ + super.key, + required this.child, + this.onLeft = false, + this.verticalAlignment = DragHandleVerticalAlignment.center, + }); + + @override + Widget build(BuildContext context) { + return child; + } +} diff --git a/lib/measure_size.dart b/lib/measure_size.dart index 49fbcaf..776931b 100644 --- a/lib/measure_size.dart +++ b/lib/measure_size.dart @@ -1,23 +1,23 @@ import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; -typedef void OnWidgetSizeChange(Size size); +typedef OnWidgetSizeChange = void Function(Size? size); class MeasureSize extends StatefulWidget { - final Widget child; + final Widget? child; final OnWidgetSizeChange onSizeChange; const MeasureSize({ - Key key, - @required this.onSizeChange, - @required this.child, - }) : super(key: key); + super.key, + required this.onSizeChange, + required this.child, + }); @override - _MeasureSizeState createState() => _MeasureSizeState(); + MeasureSizeState createState() => MeasureSizeState(); } -class _MeasureSizeState extends State { +class MeasureSizeState extends State { @override Widget build(BuildContext context) { SchedulerBinding.instance.addPostFrameCallback(postFrameCallback); @@ -28,7 +28,7 @@ class _MeasureSizeState extends State { } var widgetKey = GlobalKey(); - var oldSize; + Size? oldSize; var topLeftPosition = Offset.zero; void postFrameCallback(_) { diff --git a/lib/programmatic_expansion_tile.dart b/lib/programmatic_expansion_tile.dart index 4fc692c..cb30361 100644 --- a/lib/programmatic_expansion_tile.dart +++ b/lib/programmatic_expansion_tile.dart @@ -4,7 +4,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; -import 'package:flutter/widgets.dart'; const Duration _kExpand = Duration(milliseconds: 200); @@ -28,10 +27,10 @@ class ProgrammaticExpansionTile extends StatefulWidget { /// the tile to reveal or hide the [children]. The [initiallyExpanded] property must /// be non-null. const ProgrammaticExpansionTile({ - @required Key key, - @required this.listKey, + required Key key, + required this.listKey, this.leading, - @required this.title, + required this.title, this.subtitle, this.isThreeLine = false, this.backgroundColor, @@ -40,27 +39,24 @@ class ProgrammaticExpansionTile extends StatefulWidget { this.trailing, this.initiallyExpanded = false, this.disableTopAndBottomBorders = false, - }) : assert(initiallyExpanded != null), - assert(listKey != null), - assert(key != null), - super(key: key); + }) : super(key: key); final Key listKey; /// A widget to display before the title. /// /// Typically a [CircleAvatar] widget. - final Widget leading; + final Widget? leading; /// The primary content of the list item. /// /// Typically a [Text] widget. - final Widget title; + final Widget? title; /// Additional content displayed below the title. /// /// Typically a [Text] widget. - final Widget subtitle; + final Widget? subtitle; /// Additional content displayed below the title. /// @@ -72,18 +68,18 @@ class ProgrammaticExpansionTile extends StatefulWidget { /// When the tile starts expanding, this function is called with the value /// true. When the tile starts collapsing, this function is called with /// the value false. - final ValueChanged onExpansionChanged; + final ValueChanged? onExpansionChanged; /// The widgets that are displayed when the tile expands. /// /// Typically [ListTile] widgets. - final List children; + final List children; /// The color to display behind the sublist when expanded. - final Color backgroundColor; + final Color? backgroundColor; /// A widget to display instead of a rotating arrow icon. - final Widget trailing; + final Widget? trailing; /// Specifies if the list tile is initially expanded (true) or collapsed (false, the default). final bool initiallyExpanded; @@ -110,13 +106,13 @@ class ProgrammaticExpansionTileState extends State final ColorTween _iconColorTween = ColorTween(); final ColorTween _backgroundColorTween = ColorTween(); - AnimationController _controller; - Animation _iconTurns; - Animation _heightFactor; - Animation _borderColor; - Animation _headerColor; - Animation _iconColor; - Animation _backgroundColor; + late AnimationController _controller; + late Animation _iconTurns; + late Animation _heightFactor; + late Animation _borderColor; + late Animation _headerColor; + late Animation _iconColor; + late Animation _backgroundColor; bool _isExpanded = false; @@ -133,7 +129,7 @@ class ProgrammaticExpansionTileState extends State _controller.drive(_backgroundColorTween.chain(_easeOutTween)); _isExpanded = PageStorage.of(context) - ?.readState(context, identifier: widget.listKey) as bool ?? + .readState(context, identifier: widget.listKey) as bool? ?? widget.initiallyExpanded; if (_isExpanded) _controller.value = 1.0; @@ -142,7 +138,7 @@ class ProgrammaticExpansionTileState extends State SchedulerBinding.instance.addPostFrameCallback((Duration duration) { if (widget.onExpansionChanged != null && _isExpanded != widget.initiallyExpanded) { - widget.onExpansionChanged(_isExpanded); + widget.onExpansionChanged!(_isExpanded); } }); } @@ -180,15 +176,15 @@ class ProgrammaticExpansionTileState extends State }); } PageStorage.of(context) - ?.writeState(context, _isExpanded, identifier: widget.listKey); + .writeState(context, _isExpanded, identifier: widget.listKey); }); if (widget.onExpansionChanged != null) { - widget.onExpansionChanged(_isExpanded); + widget.onExpansionChanged!(_isExpanded); } } } - Widget _buildChildren(BuildContext context, Widget child) { + Widget _buildChildren(BuildContext context, Widget? child) { final Color borderSideColor = _borderColor.value ?? Colors.transparent; bool setBorder = !widget.disableTopAndBottomBorders; @@ -237,11 +233,11 @@ class ProgrammaticExpansionTileState extends State final ThemeData theme = Theme.of(context); _borderColorTween.end = theme.dividerColor; _headerColorTween - ..begin = theme.textTheme.subtitle1.color - ..end = theme.accentColor; + ..begin = theme.textTheme.titleMedium!.color + ..end = theme.colorScheme.secondary; _iconColorTween ..begin = theme.unselectedWidgetColor - ..end = theme.accentColor; + ..end = theme.colorScheme.secondary; _backgroundColorTween.end = widget.backgroundColor; super.didChangeDependencies(); } @@ -252,7 +248,7 @@ class ProgrammaticExpansionTileState extends State return AnimatedBuilder( animation: _controller.view, builder: _buildChildren, - child: closed ? null : Column(children: widget.children), + child: closed ? null : Column(children: widget.children as List), ); } } diff --git a/pubspec.yaml b/pubspec.yaml index b485d37..75af0f0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,16 +1,18 @@ name: drag_and_drop_lists description: A flutter package to allow drag-and-drop reordering of two-level lists. -version: 0.2.10 +version: 0.4.2 homepage: https://github.com/philip-brink/DragAndDropLists environment: - sdk: ">=2.7.0 <3.0.0" + sdk: '>=2.17.0 <4.0.0' + flutter: '>=3.0.0' dependencies: flutter: sdk: flutter dev_dependencies: + flutter_lints: ^4.0.0 flutter_test: sdk: flutter diff --git a/test/drag_and_drop_lists_test.dart b/test/drag_and_drop_lists_test.dart index 187be44..3892990 100644 --- a/test/drag_and_drop_lists_test.dart +++ b/test/drag_and_drop_lists_test.dart @@ -1,6 +1,6 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:drag_and_drop_lists/drag_and_drop_lists.dart'; +//import 'package:drag_and_drop_lists/drag_and_drop_lists.dart'; void main() { test('adds one to input values', () {