├── .gitignore ├── .metadata ├── README.md ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── adaptive_app_demos │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-v21 │ │ │ └── launch_background.xml │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ └── values │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-1024x1024@1x.png │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ └── Icon-App-83.5x83.5@2x.png │ └── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── LaunchImage.png │ │ ├── LaunchImage@2x.png │ │ ├── LaunchImage@3x.png │ │ └── README.md │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ └── Runner-Bridging-Header.h ├── lib ├── app_model.dart ├── global │ ├── device_size.dart │ ├── device_type.dart │ ├── styling.dart │ └── targeted_actions.dart ├── main.dart ├── main_app_scaffold.dart ├── pages │ ├── adaptive_data_table_page.dart │ ├── adaptive_grid_page.dart │ ├── adaptive_reflow_page.dart │ ├── focus_examples_page.dart │ └── login_page.dart └── widgets │ ├── app_title_bar.dart │ ├── buttons.dart │ ├── ok_cancel_dialog.dart │ └── scroll_view_with_scrollbars.dart ├── linux ├── .gitignore ├── CMakeLists.txt ├── flutter │ ├── CMakeLists.txt │ ├── generated_plugin_registrant.cc │ ├── generated_plugin_registrant.h │ └── generated_plugins.cmake ├── main.cc ├── my_application.cc └── my_application.h ├── macos ├── .gitignore ├── Flutter │ ├── Flutter-Debug.xcconfig │ ├── Flutter-Release.xcconfig │ └── GeneratedPluginRegistrant.swift ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ └── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── app_icon_1024.png │ │ ├── app_icon_128.png │ │ ├── app_icon_16.png │ │ ├── app_icon_256.png │ │ ├── app_icon_32.png │ │ ├── app_icon_512.png │ │ └── app_icon_64.png │ ├── Base.lproj │ └── MainMenu.xib │ ├── Configs │ ├── AppInfo.xcconfig │ ├── Debug.xcconfig │ ├── Release.xcconfig │ └── Warnings.xcconfig │ ├── DebugProfile.entitlements │ ├── Info.plist │ ├── MainFlutterWindow.swift │ └── Release.entitlements ├── pubspec.lock ├── pubspec.yaml ├── web ├── favicon.png ├── icons │ ├── Icon-192.png │ └── Icon-512.png ├── index.html └── manifest.json └── windows ├── .gitignore ├── CMakeLists.txt ├── flutter ├── CMakeLists.txt ├── generated_plugin_registrant.cc ├── generated_plugin_registrant.h └── generated_plugins.cmake └── runner ├── CMakeLists.txt ├── Runner.rc ├── flutter_window.cpp ├── flutter_window.h ├── main.cpp ├── resource.h ├── resources └── app_icon.ico ├── run_loop.cpp ├── run_loop.h ├── runner.exe.manifest ├── utils.cpp ├── utils.h ├── win32_window.cpp └── win32_window.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 8264cb3e8a797eef39cbcd32bb56fd07790efb7f 8 | channel: dev 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # adaptive_app_demos 2 | 3 | Demo code for adaptive app development techniques. 4 | 5 | 6 | ### Demonstrated in this repo: 7 | * Login view 8 | * re-flows from vertical to horizontal layout depending on screen size 9 | * shows how to use horizontal constraints to stop an item from growing excessively large 10 | * Main App Scaffold 11 | * a responsive scaffold that changes from tab bar + drawer, to a combined side-menu for larger screens 12 | * children are shared between widgets showing how you can reuse the same widget in multiple places 13 | * A custom title bar is implemented for all platforms using a single widget 14 | * VisualDensity is controlled with a button inside the title bar, and changes density across the entire app 15 | * VisualDensity button is right-aligned on macOS to not conflict with native window controls 16 | * AdaptiveGrid uses LayoutBuilder to create a responsive grid, 17 | * //TODO: shows cross platform multi-select behavior 18 | * //TODO: shows popups and right-click menu to delete 19 | * //TODO: shows select all and delete behavior 20 | * AdaptiveDataTable uses LayoutBuilder to add/remove columns in a table, depending on space available 21 | * AdaptiveReflow uses LayoutBuilder to reflow between 3 different layout types: Vertical, Horizontal and Mixed. 22 | * FocusExamples show various examples of how you can control focusable widgets and listen for key events 23 | * Logout dialog contains adaptive logic to place the "Ok" button on the left-hand side on Windows, as per OS convention. 24 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 30 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | 35 | defaultConfig { 36 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 37 | applicationId "com.example.adaptive_app_demos" 38 | minSdkVersion 16 39 | targetSdkVersion 30 40 | versionCode flutterVersionCode.toInteger() 41 | versionName flutterVersionName 42 | } 43 | 44 | buildTypes { 45 | release { 46 | // TODO: Add your own signing config for the release build. 47 | // Signing with the debug keys for now, so `flutter run --release` works. 48 | signingConfig signingConfigs.debug 49 | } 50 | } 51 | } 52 | 53 | flutter { 54 | source '../..' 55 | } 56 | 57 | dependencies { 58 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 59 | } 60 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 13 | 17 | 21 | 26 | 30 | 31 | 32 | 33 | 34 | 35 | 37 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/adaptive_app_demos/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.adaptive_app_demos 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:4.1.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | jcenter() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip 7 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/ephemeral/ 22 | Flutter/app.flx 23 | Flutter/app.zip 24 | Flutter/flutter_assets/ 25 | Flutter/flutter_export_environment.sh 26 | ServiceDefinitions.json 27 | Runner/GeneratedPluginRegistrant.* 28 | 29 | # Exceptions to above rules. 30 | !default.mode1v3 31 | !default.mode2v3 32 | !default.pbxuser 33 | !default.perspectivev3 34 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 13 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 14 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 15 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXCopyFilesBuildPhase section */ 19 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 20 | isa = PBXCopyFilesBuildPhase; 21 | buildActionMask = 2147483647; 22 | dstPath = ""; 23 | dstSubfolderSpec = 10; 24 | files = ( 25 | ); 26 | name = "Embed Frameworks"; 27 | runOnlyForDeploymentPostprocessing = 0; 28 | }; 29 | /* End PBXCopyFilesBuildPhase section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 33 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 34 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 35 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 36 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 37 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 38 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 39 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 40 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 42 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 43 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 44 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 45 | /* End PBXFileReference section */ 46 | 47 | /* Begin PBXFrameworksBuildPhase section */ 48 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 49 | isa = PBXFrameworksBuildPhase; 50 | buildActionMask = 2147483647; 51 | files = ( 52 | ); 53 | runOnlyForDeploymentPostprocessing = 0; 54 | }; 55 | /* End PBXFrameworksBuildPhase section */ 56 | 57 | /* Begin PBXGroup section */ 58 | 9740EEB11CF90186004384FC /* Flutter */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 62 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 63 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 64 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 65 | ); 66 | name = Flutter; 67 | sourceTree = ""; 68 | }; 69 | 97C146E51CF9000F007C117D = { 70 | isa = PBXGroup; 71 | children = ( 72 | 9740EEB11CF90186004384FC /* Flutter */, 73 | 97C146F01CF9000F007C117D /* Runner */, 74 | 97C146EF1CF9000F007C117D /* Products */, 75 | ); 76 | sourceTree = ""; 77 | }; 78 | 97C146EF1CF9000F007C117D /* Products */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | 97C146EE1CF9000F007C117D /* Runner.app */, 82 | ); 83 | name = Products; 84 | sourceTree = ""; 85 | }; 86 | 97C146F01CF9000F007C117D /* Runner */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 90 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 91 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 92 | 97C147021CF9000F007C117D /* Info.plist */, 93 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 94 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 95 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 96 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 97 | ); 98 | path = Runner; 99 | sourceTree = ""; 100 | }; 101 | /* End PBXGroup section */ 102 | 103 | /* Begin PBXNativeTarget section */ 104 | 97C146ED1CF9000F007C117D /* Runner */ = { 105 | isa = PBXNativeTarget; 106 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 107 | buildPhases = ( 108 | 9740EEB61CF901F6004384FC /* Run Script */, 109 | 97C146EA1CF9000F007C117D /* Sources */, 110 | 97C146EB1CF9000F007C117D /* Frameworks */, 111 | 97C146EC1CF9000F007C117D /* Resources */, 112 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 113 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 114 | ); 115 | buildRules = ( 116 | ); 117 | dependencies = ( 118 | ); 119 | name = Runner; 120 | productName = Runner; 121 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 122 | productType = "com.apple.product-type.application"; 123 | }; 124 | /* End PBXNativeTarget section */ 125 | 126 | /* Begin PBXProject section */ 127 | 97C146E61CF9000F007C117D /* Project object */ = { 128 | isa = PBXProject; 129 | attributes = { 130 | LastUpgradeCheck = 1020; 131 | ORGANIZATIONNAME = ""; 132 | TargetAttributes = { 133 | 97C146ED1CF9000F007C117D = { 134 | CreatedOnToolsVersion = 7.3.1; 135 | LastSwiftMigration = 1100; 136 | }; 137 | }; 138 | }; 139 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 140 | compatibilityVersion = "Xcode 9.3"; 141 | developmentRegion = en; 142 | hasScannedForEncodings = 0; 143 | knownRegions = ( 144 | en, 145 | Base, 146 | ); 147 | mainGroup = 97C146E51CF9000F007C117D; 148 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 149 | projectDirPath = ""; 150 | projectRoot = ""; 151 | targets = ( 152 | 97C146ED1CF9000F007C117D /* Runner */, 153 | ); 154 | }; 155 | /* End PBXProject section */ 156 | 157 | /* Begin PBXResourcesBuildPhase section */ 158 | 97C146EC1CF9000F007C117D /* Resources */ = { 159 | isa = PBXResourcesBuildPhase; 160 | buildActionMask = 2147483647; 161 | files = ( 162 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 163 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 164 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 165 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 166 | ); 167 | runOnlyForDeploymentPostprocessing = 0; 168 | }; 169 | /* End PBXResourcesBuildPhase section */ 170 | 171 | /* Begin PBXShellScriptBuildPhase section */ 172 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 173 | isa = PBXShellScriptBuildPhase; 174 | buildActionMask = 2147483647; 175 | files = ( 176 | ); 177 | inputPaths = ( 178 | ); 179 | name = "Thin Binary"; 180 | outputPaths = ( 181 | ); 182 | runOnlyForDeploymentPostprocessing = 0; 183 | shellPath = /bin/sh; 184 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 185 | }; 186 | 9740EEB61CF901F6004384FC /* Run Script */ = { 187 | isa = PBXShellScriptBuildPhase; 188 | buildActionMask = 2147483647; 189 | files = ( 190 | ); 191 | inputPaths = ( 192 | ); 193 | name = "Run Script"; 194 | outputPaths = ( 195 | ); 196 | runOnlyForDeploymentPostprocessing = 0; 197 | shellPath = /bin/sh; 198 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 199 | }; 200 | /* End PBXShellScriptBuildPhase section */ 201 | 202 | /* Begin PBXSourcesBuildPhase section */ 203 | 97C146EA1CF9000F007C117D /* Sources */ = { 204 | isa = PBXSourcesBuildPhase; 205 | buildActionMask = 2147483647; 206 | files = ( 207 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 208 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 209 | ); 210 | runOnlyForDeploymentPostprocessing = 0; 211 | }; 212 | /* End PBXSourcesBuildPhase section */ 213 | 214 | /* Begin PBXVariantGroup section */ 215 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 216 | isa = PBXVariantGroup; 217 | children = ( 218 | 97C146FB1CF9000F007C117D /* Base */, 219 | ); 220 | name = Main.storyboard; 221 | sourceTree = ""; 222 | }; 223 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 224 | isa = PBXVariantGroup; 225 | children = ( 226 | 97C147001CF9000F007C117D /* Base */, 227 | ); 228 | name = LaunchScreen.storyboard; 229 | sourceTree = ""; 230 | }; 231 | /* End PBXVariantGroup section */ 232 | 233 | /* Begin XCBuildConfiguration section */ 234 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 235 | isa = XCBuildConfiguration; 236 | buildSettings = { 237 | ALWAYS_SEARCH_USER_PATHS = NO; 238 | CLANG_ANALYZER_NONNULL = YES; 239 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 240 | CLANG_CXX_LIBRARY = "libc++"; 241 | CLANG_ENABLE_MODULES = YES; 242 | CLANG_ENABLE_OBJC_ARC = YES; 243 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 244 | CLANG_WARN_BOOL_CONVERSION = YES; 245 | CLANG_WARN_COMMA = YES; 246 | CLANG_WARN_CONSTANT_CONVERSION = YES; 247 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 248 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 249 | CLANG_WARN_EMPTY_BODY = YES; 250 | CLANG_WARN_ENUM_CONVERSION = YES; 251 | CLANG_WARN_INFINITE_RECURSION = YES; 252 | CLANG_WARN_INT_CONVERSION = YES; 253 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 254 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 255 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 256 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 257 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 258 | CLANG_WARN_STRICT_PROTOTYPES = YES; 259 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 260 | CLANG_WARN_UNREACHABLE_CODE = YES; 261 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 262 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 263 | COPY_PHASE_STRIP = NO; 264 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 265 | ENABLE_NS_ASSERTIONS = NO; 266 | ENABLE_STRICT_OBJC_MSGSEND = YES; 267 | GCC_C_LANGUAGE_STANDARD = gnu99; 268 | GCC_NO_COMMON_BLOCKS = YES; 269 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 270 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 271 | GCC_WARN_UNDECLARED_SELECTOR = YES; 272 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 273 | GCC_WARN_UNUSED_FUNCTION = YES; 274 | GCC_WARN_UNUSED_VARIABLE = YES; 275 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 276 | MTL_ENABLE_DEBUG_INFO = NO; 277 | SDKROOT = iphoneos; 278 | SUPPORTED_PLATFORMS = iphoneos; 279 | TARGETED_DEVICE_FAMILY = "1,2"; 280 | VALIDATE_PRODUCT = YES; 281 | }; 282 | name = Profile; 283 | }; 284 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 285 | isa = XCBuildConfiguration; 286 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 287 | buildSettings = { 288 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 289 | CLANG_ENABLE_MODULES = YES; 290 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 291 | ENABLE_BITCODE = NO; 292 | INFOPLIST_FILE = Runner/Info.plist; 293 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 294 | PRODUCT_BUNDLE_IDENTIFIER = com.example.adaptiveAppDemos; 295 | PRODUCT_NAME = "$(TARGET_NAME)"; 296 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 297 | SWIFT_VERSION = 5.0; 298 | VERSIONING_SYSTEM = "apple-generic"; 299 | }; 300 | name = Profile; 301 | }; 302 | 97C147031CF9000F007C117D /* Debug */ = { 303 | isa = XCBuildConfiguration; 304 | buildSettings = { 305 | ALWAYS_SEARCH_USER_PATHS = NO; 306 | CLANG_ANALYZER_NONNULL = YES; 307 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 308 | CLANG_CXX_LIBRARY = "libc++"; 309 | CLANG_ENABLE_MODULES = YES; 310 | CLANG_ENABLE_OBJC_ARC = YES; 311 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 312 | CLANG_WARN_BOOL_CONVERSION = YES; 313 | CLANG_WARN_COMMA = YES; 314 | CLANG_WARN_CONSTANT_CONVERSION = YES; 315 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 316 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 317 | CLANG_WARN_EMPTY_BODY = YES; 318 | CLANG_WARN_ENUM_CONVERSION = YES; 319 | CLANG_WARN_INFINITE_RECURSION = YES; 320 | CLANG_WARN_INT_CONVERSION = YES; 321 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 322 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 323 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 324 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 325 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 326 | CLANG_WARN_STRICT_PROTOTYPES = YES; 327 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 328 | CLANG_WARN_UNREACHABLE_CODE = YES; 329 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 330 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 331 | COPY_PHASE_STRIP = NO; 332 | DEBUG_INFORMATION_FORMAT = dwarf; 333 | ENABLE_STRICT_OBJC_MSGSEND = YES; 334 | ENABLE_TESTABILITY = YES; 335 | GCC_C_LANGUAGE_STANDARD = gnu99; 336 | GCC_DYNAMIC_NO_PIC = NO; 337 | GCC_NO_COMMON_BLOCKS = YES; 338 | GCC_OPTIMIZATION_LEVEL = 0; 339 | GCC_PREPROCESSOR_DEFINITIONS = ( 340 | "DEBUG=1", 341 | "$(inherited)", 342 | ); 343 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 344 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 345 | GCC_WARN_UNDECLARED_SELECTOR = YES; 346 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 347 | GCC_WARN_UNUSED_FUNCTION = YES; 348 | GCC_WARN_UNUSED_VARIABLE = YES; 349 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 350 | MTL_ENABLE_DEBUG_INFO = YES; 351 | ONLY_ACTIVE_ARCH = YES; 352 | SDKROOT = iphoneos; 353 | TARGETED_DEVICE_FAMILY = "1,2"; 354 | }; 355 | name = Debug; 356 | }; 357 | 97C147041CF9000F007C117D /* Release */ = { 358 | isa = XCBuildConfiguration; 359 | buildSettings = { 360 | ALWAYS_SEARCH_USER_PATHS = NO; 361 | CLANG_ANALYZER_NONNULL = YES; 362 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 363 | CLANG_CXX_LIBRARY = "libc++"; 364 | CLANG_ENABLE_MODULES = YES; 365 | CLANG_ENABLE_OBJC_ARC = YES; 366 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 367 | CLANG_WARN_BOOL_CONVERSION = YES; 368 | CLANG_WARN_COMMA = YES; 369 | CLANG_WARN_CONSTANT_CONVERSION = YES; 370 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 371 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 372 | CLANG_WARN_EMPTY_BODY = YES; 373 | CLANG_WARN_ENUM_CONVERSION = YES; 374 | CLANG_WARN_INFINITE_RECURSION = YES; 375 | CLANG_WARN_INT_CONVERSION = YES; 376 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 377 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 378 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 379 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 380 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 381 | CLANG_WARN_STRICT_PROTOTYPES = YES; 382 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 383 | CLANG_WARN_UNREACHABLE_CODE = YES; 384 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 385 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 386 | COPY_PHASE_STRIP = NO; 387 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 388 | ENABLE_NS_ASSERTIONS = NO; 389 | ENABLE_STRICT_OBJC_MSGSEND = YES; 390 | GCC_C_LANGUAGE_STANDARD = gnu99; 391 | GCC_NO_COMMON_BLOCKS = YES; 392 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 393 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 394 | GCC_WARN_UNDECLARED_SELECTOR = YES; 395 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 396 | GCC_WARN_UNUSED_FUNCTION = YES; 397 | GCC_WARN_UNUSED_VARIABLE = YES; 398 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 399 | MTL_ENABLE_DEBUG_INFO = NO; 400 | SDKROOT = iphoneos; 401 | SUPPORTED_PLATFORMS = iphoneos; 402 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 403 | TARGETED_DEVICE_FAMILY = "1,2"; 404 | VALIDATE_PRODUCT = YES; 405 | }; 406 | name = Release; 407 | }; 408 | 97C147061CF9000F007C117D /* Debug */ = { 409 | isa = XCBuildConfiguration; 410 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 411 | buildSettings = { 412 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 413 | CLANG_ENABLE_MODULES = YES; 414 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 415 | ENABLE_BITCODE = NO; 416 | INFOPLIST_FILE = Runner/Info.plist; 417 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 418 | PRODUCT_BUNDLE_IDENTIFIER = com.example.adaptiveAppDemos; 419 | PRODUCT_NAME = "$(TARGET_NAME)"; 420 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 421 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 422 | SWIFT_VERSION = 5.0; 423 | VERSIONING_SYSTEM = "apple-generic"; 424 | }; 425 | name = Debug; 426 | }; 427 | 97C147071CF9000F007C117D /* Release */ = { 428 | isa = XCBuildConfiguration; 429 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 430 | buildSettings = { 431 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 432 | CLANG_ENABLE_MODULES = YES; 433 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 434 | ENABLE_BITCODE = NO; 435 | INFOPLIST_FILE = Runner/Info.plist; 436 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 437 | PRODUCT_BUNDLE_IDENTIFIER = com.example.adaptiveAppDemos; 438 | PRODUCT_NAME = "$(TARGET_NAME)"; 439 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 440 | SWIFT_VERSION = 5.0; 441 | VERSIONING_SYSTEM = "apple-generic"; 442 | }; 443 | name = Release; 444 | }; 445 | /* End XCBuildConfiguration section */ 446 | 447 | /* Begin XCConfigurationList section */ 448 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 449 | isa = XCConfigurationList; 450 | buildConfigurations = ( 451 | 97C147031CF9000F007C117D /* Debug */, 452 | 97C147041CF9000F007C117D /* Release */, 453 | 249021D3217E4FDB00AE95B9 /* Profile */, 454 | ); 455 | defaultConfigurationIsVisible = 0; 456 | defaultConfigurationName = Release; 457 | }; 458 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 459 | isa = XCConfigurationList; 460 | buildConfigurations = ( 461 | 97C147061CF9000F007C117D /* Debug */, 462 | 97C147071CF9000F007C117D /* Release */, 463 | 249021D4217E4FDB00AE95B9 /* Profile */, 464 | ); 465 | defaultConfigurationIsVisible = 0; 466 | defaultConfigurationName = Release; 467 | }; 468 | /* End XCConfigurationList section */ 469 | }; 470 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 471 | } 472 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | adaptive_app_demos 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /lib/app_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:adaptive_app_demos/global/device_type.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class AppModel with ChangeNotifier { 5 | bool getDefaultTouchMode() => DeviceType.isMobile == true; 6 | 7 | // Main menu, selected page 8 | int _selectedIndex = 0; 9 | int get selectedIndex => _selectedIndex; 10 | set selectedIndex(int value) => notify(() => _selectedIndex = value); 11 | 12 | // Touch mode, determines density of views 13 | late bool _touchMode = getDefaultTouchMode(); 14 | bool get touchMode => _touchMode; 15 | set touchMode(bool value) => notify(() => _touchMode = value); 16 | 17 | void toggleTouchMode() => touchMode = !touchMode; 18 | 19 | // Indicates whether a user is logged in or not 20 | bool _isLoggedIn = false; 21 | bool get isLoggedIn => _isLoggedIn; 22 | set isLoggedIn(bool value) { 23 | if (value == false) { 24 | reset(); 25 | } 26 | notify(() => _isLoggedIn = value); 27 | } 28 | 29 | void login() => notify(() => _isLoggedIn = true); 30 | void logout() { 31 | _selectedIndex = 0; 32 | notify(() => _isLoggedIn = false); 33 | } 34 | 35 | void reset() { 36 | _selectedIndex = 0; 37 | _touchMode = getDefaultTouchMode(); 38 | } 39 | 40 | // Helper method for single-line state changes 41 | void notify(VoidCallback stateChange) { 42 | stateChange.call(); 43 | notifyListeners(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/global/device_size.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | 3 | enum ScreenType { Handset, Tablet, Desktop, Watch } 4 | 5 | class FormFactor { 6 | static double desktop = 900; 7 | static double tablet = 600; 8 | static double handset = 300; 9 | } 10 | 11 | ScreenType getFormFactor(BuildContext context) { 12 | double deviceWidth = MediaQuery.of(context).size.shortestSide; 13 | if (deviceWidth > FormFactor.desktop) return ScreenType.Desktop; 14 | if (deviceWidth > FormFactor.tablet) return ScreenType.Tablet; 15 | if (deviceWidth > FormFactor.handset) return ScreenType.Handset; 16 | return ScreenType.Watch; 17 | } 18 | 19 | enum ScreenSize { Small, Normal, Large, ExtraLarge } 20 | ScreenSize getSize(BuildContext context) { 21 | double deviceWidth = MediaQuery.of(context).size.shortestSide; 22 | if (deviceWidth > 900) return ScreenSize.ExtraLarge; 23 | if (deviceWidth > 600) return ScreenSize.Large; 24 | if (deviceWidth > 300) return ScreenSize.Normal; 25 | return ScreenSize.Small; 26 | } 27 | -------------------------------------------------------------------------------- /lib/global/device_type.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:universal_platform/universal_platform.dart'; 3 | 4 | class DeviceType { 5 | // Syntax sugar, proxy the UniversalPlatform methods so our views can reference a single class 6 | static bool isIOS = UniversalPlatform.isIOS; 7 | static bool isAndroid = UniversalPlatform.isAndroid; 8 | static bool isMacOS = UniversalPlatform.isMacOS; 9 | static bool isLinux = UniversalPlatform.isLinux; 10 | static bool isWindows = UniversalPlatform.isWindows; 11 | 12 | // Higher level device class abstractions (more syntax sugar for the views) 13 | static bool isWeb = UniversalPlatform.isWeb; 14 | static bool get isDesktop => isWindows || isMacOS || isLinux; 15 | static bool get isMobile => isAndroid || isIOS; 16 | static bool get isDesktopOrWeb => isDesktop || isWeb; 17 | static bool get isMobileOrWeb => isMobile || isWeb; 18 | } 19 | -------------------------------------------------------------------------------- /lib/global/styling.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class Insets { 4 | static double medium = 5; 5 | static double large = 10; 6 | static double extraLarge = 20; 7 | static double small = 3; 8 | } 9 | 10 | class TextStyles { 11 | static TextStyle buttonText1 = TextStyle(fontWeight: FontWeight.bold, fontSize: 14); 12 | static TextStyle buttonText2 = TextStyle(fontWeight: FontWeight.normal, fontSize: 11); 13 | static TextStyle h1 = TextStyle(fontWeight: FontWeight.bold, fontSize: 22); 14 | static TextStyle h2 = TextStyle(fontWeight: FontWeight.bold, fontSize: 16); 15 | } 16 | -------------------------------------------------------------------------------- /lib/global/targeted_actions.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | import 'package:provider/provider.dart'; 3 | 4 | /// Provides a top level scope for targeted actions. 5 | /// 6 | /// Targeted actions are useful for making actions that are bound to children or 7 | /// siblings of the focused widget available for invocation that wouldn't 8 | /// normally be visible to a Shortcuts widget ancestor of the focused widget. 9 | /// TargetedActionScope is used in place or in addition to the Shortcuts widget 10 | /// that defines the key bindings for a subtree. 11 | /// 12 | /// To use a targeted action, define this scope with a set of shortcuts that 13 | /// should be active in this scope. Then, in a child widget of this one, define 14 | /// a [TargetedActionBinding] with the actions that you wish to execute when the 15 | /// binding is activated with the intent. If no action is defined for a scope 16 | /// for that intent, then nothing happens. 17 | class TargetedActionScope extends StatefulWidget { 18 | TargetedActionScope({Key? key, required this.child, required this.shortcuts}) : super(key: key); 19 | 20 | final Widget child; 21 | final Map shortcuts; 22 | 23 | @override 24 | State createState() => _TargetedActionScopeState(); 25 | } 26 | 27 | class _TargetedActionScopeState extends State { 28 | late _TargetedActionRegistry registry; 29 | Map mappedShortcuts = {}; 30 | 31 | @override 32 | void initState() { 33 | super.initState(); 34 | registry = _TargetedActionRegistry(); 35 | mappedShortcuts = _buildShortcuts(); 36 | } 37 | 38 | @override 39 | void didUpdateWidget(TargetedActionScope oldWidget) { 40 | super.didUpdateWidget(oldWidget); 41 | if (oldWidget.shortcuts != widget.shortcuts) { 42 | mappedShortcuts = _buildShortcuts(); 43 | } 44 | } 45 | 46 | Map _buildShortcuts() { 47 | Map mapped = {}; 48 | for (final LogicalKeySet activator in widget.shortcuts.keys) { 49 | mapped[activator] = _TargetedIntent(widget.shortcuts[activator]!); 50 | } 51 | return mapped; 52 | } 53 | 54 | @override 55 | Widget build(BuildContext context) { 56 | return Provider<_TargetedActionRegistry>.value( 57 | value: registry, 58 | child: Shortcuts( 59 | shortcuts: mappedShortcuts, 60 | child: Actions( 61 | actions: >{ 62 | _TargetedIntent: _TargetedAction(registry), 63 | }, 64 | child: widget.child, 65 | ), 66 | ), 67 | ); 68 | } 69 | } 70 | 71 | /// A binding for use within a [TargetedActionScope]. 72 | /// 73 | /// Place an instance of this widget as a descendant of a [TargetedActionScope], 74 | /// and optionally define any actions that should handle the intents with 75 | /// bindings in the scope. Any actions defined in parents of this widget will 76 | /// also be in the scope. 77 | /// 78 | /// If more than one of these exists in the same [TargetedActionScope], then 79 | /// each of the corresponding contexts will be searched for an action to fulfill 80 | /// the intent. The first one to be found that fulfills the intent will have its 81 | /// action invoked. The order duplicate bindings are searched in is stable with 82 | /// respect to build order, but arbitrary. 83 | // This is a stateful widget because we need to be able to implement deactivate. 84 | class TargetedActionBinding extends StatefulWidget { 85 | TargetedActionBinding({Key? key, required this.child, this.actions}) 86 | : super(key: key); 87 | 88 | final Widget child; 89 | final Map>? actions; 90 | 91 | @override 92 | State createState() => _TargetedActionBindingState(); 93 | } 94 | 95 | class _TargetedActionBindingState extends State { 96 | final GlobalKey _subtreeKey = GlobalKey(debugLabel: 'Targeted Action Binding'); 97 | 98 | @override 99 | Widget build(BuildContext context) { 100 | Provider.of<_TargetedActionRegistry>(context).addTarget(_subtreeKey); 101 | Widget result = KeyedSubtree( 102 | key: _subtreeKey, 103 | child: this.widget.child, 104 | ); 105 | if (widget.actions != null) { 106 | result = Actions(actions: widget.actions!, child: result); 107 | } 108 | return result; 109 | } 110 | 111 | @override 112 | void deactivate() { 113 | Provider.of<_TargetedActionRegistry>(context, listen: false).targetKeys.remove(_subtreeKey); 114 | super.deactivate(); 115 | } 116 | } 117 | 118 | // This is a registry that keeps track of the set of targets in the scope, and 119 | // handles invoking them. 120 | // 121 | // It is found through a provider. 122 | class _TargetedActionRegistry { 123 | _TargetedActionRegistry() 124 | : targetKeys = {}; 125 | 126 | Set targetKeys; 127 | 128 | // Adds the given target key to the set of keys to check. 129 | void addTarget(GlobalKey target) { 130 | targetKeys.add(target); 131 | } 132 | 133 | bool isEnabled(Intent intent) { 134 | // Check each of the target keys to see if there's an action registered in 135 | // that context for the intent. If so, find out if it is enabled. It is 136 | // build-order dependent which action gets invoked if there are two contexts 137 | // tha support the action. 138 | for (GlobalKey key in targetKeys) { 139 | if (key.currentContext != null) { 140 | Action? foundAction = Actions.maybeFind(key.currentContext!, intent: intent); 141 | if (foundAction != null && foundAction.isEnabled(intent)) { 142 | return true; 143 | } 144 | } 145 | } 146 | return false; 147 | } 148 | 149 | Object? invoke(Intent intent) { 150 | // Check each of the target keys to see if there's an action registered in 151 | // that context for the intent. If so, execute it and return the result. It 152 | // is build-order dependent which action gets invoked if there are two 153 | // contexts tha support the action. 154 | for (GlobalKey key in targetKeys) { 155 | if (key.currentContext != null) { 156 | if (Actions.maybeFind(key.currentContext!, intent: intent) != null) { 157 | return Actions.invoke(key.currentContext!, intent); 158 | } 159 | } 160 | } 161 | return null; 162 | } 163 | } 164 | 165 | // A wrapper intent class so that it can hold the "real" intent, and serve as a 166 | // mapping type for the _TargetedAction. 167 | class _TargetedIntent extends Intent { 168 | const _TargetedIntent(this.intent); 169 | 170 | final Intent intent; 171 | } 172 | 173 | // A special action class that invokes the intent tunneled into it via the 174 | // _TargetedIntent. 175 | class _TargetedAction extends Action<_TargetedIntent> { 176 | _TargetedAction(this.registry); 177 | 178 | final _TargetedActionRegistry registry; 179 | 180 | @override 181 | bool isEnabled(_TargetedIntent intent) { 182 | return registry.isEnabled(intent.intent); 183 | } 184 | 185 | @override 186 | Object? invoke(covariant _TargetedIntent intent) { 187 | registry.invoke(intent.intent); 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:adaptive_app_demos/app_model.dart'; 2 | import 'package:adaptive_app_demos/main_app_scaffold.dart'; 3 | import 'package:bitsdojo_window/bitsdojo_window.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:provider/provider.dart'; 6 | 7 | void main() { 8 | runApp(AppScaffold()); 9 | 10 | // Required when using bits_dojo for custom TitleBars 11 | doWhenWindowReady(() { 12 | appWindow.title = "Adaptive App Demo"; 13 | appWindow.show(); 14 | }); 15 | } 16 | 17 | class AppScaffold extends StatelessWidget { 18 | @override 19 | Widget build(BuildContext context) { 20 | return ChangeNotifierProvider( 21 | create: (_) => AppModel(), 22 | child: Builder( 23 | builder: (context) { 24 | bool touchMode = context.select((AppModel m) => m.touchMode); 25 | double densityAmt = touchMode ? 0.0 : -1.0; 26 | VisualDensity density = VisualDensity(horizontal: densityAmt, vertical: densityAmt); 27 | return MaterialApp( 28 | theme: ThemeData(visualDensity: density), 29 | home: MainAppScaffold(), 30 | debugShowCheckedModeBanner: false, 31 | ); 32 | }, 33 | ), 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/main_app_scaffold.dart: -------------------------------------------------------------------------------- 1 | import 'package:adaptive_app_demos/app_model.dart'; 2 | import 'package:adaptive_app_demos/global/device_size.dart'; 3 | import 'package:adaptive_app_demos/global/styling.dart'; 4 | import 'package:adaptive_app_demos/pages/adaptive_data_table_page.dart'; 5 | import 'package:adaptive_app_demos/pages/adaptive_grid_page.dart'; 6 | import 'package:adaptive_app_demos/pages/adaptive_reflow_page.dart'; 7 | import 'package:adaptive_app_demos/pages/focus_examples_page.dart'; 8 | import 'package:adaptive_app_demos/pages/login_page.dart'; 9 | import 'package:adaptive_app_demos/widgets/buttons.dart'; 10 | import 'package:adaptive_app_demos/widgets/ok_cancel_dialog.dart'; 11 | import 'package:bitsdojo_window/bitsdojo_window.dart'; 12 | import 'package:flutter/material.dart'; 13 | import 'package:flutter/services.dart'; 14 | import 'package:provider/provider.dart'; 15 | 16 | import 'widgets/app_title_bar.dart'; 17 | import 'global/targeted_actions.dart'; 18 | 19 | List getMainMenuChildren(BuildContext context) { 20 | // Define a method to change pages in the AppModel 21 | void changePage(int value) => context.read().selectedIndex = value; 22 | int index = context.select((AppModel m) => m.selectedIndex); 23 | return [ 24 | SelectedPageButton(onPressed: () => changePage(0), label: "Adaptive Grid", isSelected: index == 0), 25 | SelectedPageButton(onPressed: () => changePage(1), label: "Adaptive Data Table", isSelected: index == 1), 26 | SelectedPageButton(onPressed: () => changePage(2), label: "Adaptive Reflow", isSelected: index == 2), 27 | SelectedPageButton(onPressed: () => changePage(3), label: "Focus Examples", isSelected: index == 3), 28 | ]; 29 | } 30 | 31 | // Uses a tab navigation + drawer, or a side-menu that combines both 32 | class MainAppScaffold extends StatefulWidget { 33 | @override 34 | _MainAppScaffoldState createState() => _MainAppScaffoldState(); 35 | } 36 | 37 | class _MainAppScaffoldState extends State { 38 | GlobalKey _scaffoldKey = GlobalKey(); 39 | 40 | @override 41 | Widget build(BuildContext context) { 42 | bool useTabs = MediaQuery.of(context).size.width < FormFactor.tablet; 43 | bool isLoggedOut = context.select((AppModel m) => m.isLoggedIn) == false; 44 | return TargetedActionScope( 45 | shortcuts: { 46 | LogicalKeySet(LogicalKeyboardKey.keyA, LogicalKeyboardKey.control): SelectAllIntent(), 47 | LogicalKeySet(LogicalKeyboardKey.keyS, LogicalKeyboardKey.control): SelectNoneIntent(), 48 | LogicalKeySet(LogicalKeyboardKey.delete): DeleteIntent(), 49 | }, 50 | child: WindowBorder( 51 | color: Colors.white, 52 | child: Material( 53 | child: Column( 54 | children: [ 55 | AppTitleBar(), 56 | Expanded( 57 | child: isLoggedOut 58 | // If logged out, show just the login page with no menus 59 | ? LoginPage() 60 | // Otherwise, show the full application with dynamic scaffold 61 | : Focus( 62 | autofocus: true, 63 | child: Scaffold( 64 | key: _scaffoldKey, 65 | drawer: useTabs ? _SideMenu(showPageButtons: false) : null, 66 | appBar: useTabs ? AppBar(backgroundColor: Colors.blue.shade300) : null, 67 | body: Stack(children: [ 68 | // Vertical layout with Tab controller and drawer 69 | if (useTabs) ...[ 70 | Column( 71 | children: [ 72 | Expanded(child: _PageStack()), 73 | _TabMenu(), 74 | ], 75 | ) 76 | ] 77 | // Horizontal layout with desktop style side menu 78 | else ...[ 79 | Row( 80 | children: [ 81 | _SideMenu(), 82 | Expanded(child: _PageStack()), 83 | ], 84 | ), 85 | ], 86 | ]), 87 | ), 88 | ), 89 | ), 90 | ], 91 | ), 92 | ), 93 | ), 94 | ); 95 | } 96 | } 97 | 98 | class _PageStack extends StatelessWidget { 99 | @override 100 | Widget build(BuildContext context) { 101 | int index = context.select((AppModel model) => model.selectedIndex); 102 | Widget? page; 103 | if (index == 0) page = AdaptiveGridPage(); 104 | if (index == 1) page = AdaptiveDataTablePage(); 105 | if (index == 2) page = AdaptiveReflowPage(); 106 | if (index == 3) page = FocusExamplesPage(); 107 | return FocusTraversalGroup(child: page ?? Container()); 108 | } 109 | } 110 | 111 | class _SideMenu extends StatelessWidget { 112 | const _SideMenu({Key? key, this.showPageButtons = true}) : super(key: key); 113 | 114 | final bool showPageButtons; 115 | 116 | @override 117 | Widget build(BuildContext context) { 118 | void _handleLogoutPressed() async { 119 | String message = "Are you sure you want to logout?"; 120 | bool? doLogout = await showDialog(context: context, builder: (_) => OkCancelDialog(message: message)); 121 | if (doLogout ?? false) { 122 | context.read().logout(); 123 | } 124 | } 125 | 126 | return Container( 127 | color: Colors.white, 128 | width: 250, 129 | child: Stack( 130 | children: [ 131 | // Buttons 132 | Column(children: [ 133 | SizedBox(height: Insets.extraLarge), 134 | if (showPageButtons)...getMainMenuChildren(context), 135 | SizedBox(height: Insets.extraLarge), 136 | SecondaryMenuButton(label: "Submenu Item 1"), 137 | SecondaryMenuButton(label: "Submenu Item 2"), 138 | SecondaryMenuButton(label: "Submenu Item 3"), 139 | Spacer(), 140 | OutlinedButton(child: Text("Logout"), onPressed: _handleLogoutPressed), 141 | SizedBox(height: Insets.large), 142 | ]), 143 | // Divider 144 | Align( 145 | alignment: Alignment.centerRight, 146 | child: Container(width: 1, height: double.infinity, color: Colors.blue)), 147 | ], 148 | ), 149 | ); 150 | } 151 | } 152 | 153 | class _TabMenu extends StatelessWidget { 154 | @override 155 | Widget build(BuildContext context) { 156 | // Wrap all the main menu buttons in Expanded() so they fill up the screen horizontally 157 | List tabButtons = getMainMenuChildren(context).map((btn) => Expanded(child: btn)).toList(); 158 | return Column( 159 | children: [ 160 | // Top Divider 161 | Container(width: double.infinity, height: 1, color: Colors.blue), 162 | // Tab buttons 163 | Row(children: tabButtons), 164 | ], 165 | ); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /lib/pages/adaptive_data_table_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:adaptive_app_demos/global/device_type.dart'; 2 | import 'package:adaptive_app_demos/global/styling.dart'; 3 | import 'package:adaptive_app_demos/widgets/scroll_view_with_scrollbars.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class AdaptiveDataTablePage extends StatelessWidget { 7 | final List items = List.generate(100, (index) => index); 8 | @override 9 | Widget build(BuildContext context) { 10 | return LayoutBuilder( 11 | builder: (BuildContext context, BoxConstraints constraints) { 12 | bool showCol2 = constraints.maxWidth > 300; 13 | bool showCol3 = constraints.maxWidth > 600; 14 | bool showCol4 = constraints.maxWidth > 900; 15 | return Column(children: [ 16 | Row( 17 | children: [ 18 | _TableHeader("Column 1"), 19 | if (showCol2) _TableHeader("Column 2"), 20 | if (showCol3) _TableHeader("Column 3"), 21 | if (showCol4) _TableHeader("Column 4"), 22 | ], 23 | ), 24 | Expanded( 25 | child: ScrollViewWithScrollbars( 26 | child: Column( 27 | children: items.map((i) { 28 | return Container( 29 | color: i % 2 == 0 ? Colors.grey.shade300 : null, 30 | child: Row( 31 | children: [ 32 | _TableRowItem("Item $i, Column 1"), 33 | if (showCol2) _TableRowItem("Item $i, Column 2"), 34 | if (showCol3) _TableRowItem("Item $i, Column 3"), 35 | if (showCol4) _TableRowItem("Item $i, Column 4"), 36 | ], 37 | ), 38 | ); 39 | }).toList(), 40 | ), 41 | ), 42 | ), 43 | ]); 44 | }, 45 | ); 46 | } 47 | } 48 | 49 | class _TableHeader extends StatelessWidget { 50 | const _TableHeader(this.label, {Key? key}) : super(key: key); 51 | final String label; 52 | 53 | @override 54 | Widget build(BuildContext context) => Expanded( 55 | child: Padding( 56 | padding: EdgeInsets.all(Insets.medium), 57 | child: Text( 58 | label, 59 | style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16), 60 | ), 61 | ), 62 | ); 63 | } 64 | 65 | class _TableRowItem extends StatelessWidget { 66 | const _TableRowItem(this.label, {Key? key}) : super(key: key); 67 | final String label; 68 | 69 | @override 70 | Widget build(BuildContext context) => Expanded( 71 | child: Padding( 72 | padding: EdgeInsets.symmetric(horizontal: Insets.medium, vertical: Insets.extraLarge), child: Text(label))); 73 | } 74 | -------------------------------------------------------------------------------- /lib/pages/adaptive_grid_page.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:adaptive_app_demos/global/device_type.dart'; 4 | import 'package:adaptive_app_demos/global/styling.dart'; 5 | import 'package:adaptive_app_demos/global/targeted_actions.dart'; 6 | import 'package:adaptive_app_demos/widgets/buttons.dart'; 7 | import 'package:flutter/material.dart'; 8 | 9 | class AdaptiveGridPage extends StatefulWidget { 10 | @override 11 | _AdaptiveGridPageState createState() => _AdaptiveGridPageState(); 12 | } 13 | 14 | class _AdaptiveGridPageState extends State { 15 | List _listItems = List.generate(100, (index) => index); 16 | ScrollController _scrollController = ScrollController(); 17 | 18 | List _selectedItems = []; 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | // Create a list of widgets to render, inject .isSelected into each item 23 | Widget buildGridItem(int index) => 24 | _GridItem(index, isSelected: _selectedItems.contains(index), onPressed: _handleItemPressed); 25 | List listChildren = _listItems.map(buildGridItem).toList(); 26 | return TargetedActionBinding( 27 | actions: { 28 | SelectAllIntent: CallbackAction(onInvoke: (Intent intent) => this._handleSelectAllPressed()), 29 | SelectNoneIntent: CallbackAction(onInvoke: (Intent intent) => this._handleSelectNonePressed()), 30 | DeleteIntent: CallbackAction(onInvoke: (Intent intent) => this._handleDeleteSelectedPressed()), 31 | }, 32 | child: Column( 33 | children: [ 34 | Row( 35 | children: [ 36 | StyledTextButton(onPressed: _handleSelectAllPressed, child: Text("Select All")), 37 | StyledTextButton(onPressed: _handleSelectNonePressed, child: Text("Select None")), 38 | ], 39 | ), 40 | Expanded( 41 | child: LayoutBuilder( 42 | builder: (BuildContext context, BoxConstraints constraints) { 43 | // Calculate how many columns we want depending on available space 44 | int colCount = max(1, (constraints.maxWidth / 250).floor()); 45 | return Scrollbar( 46 | isAlwaysShown: DeviceType.isDesktop, 47 | controller: _scrollController, 48 | child: GridView.count( 49 | controller: _scrollController, 50 | padding: EdgeInsets.all(Insets.extraLarge), 51 | childAspectRatio: 1, 52 | crossAxisCount: colCount, 53 | children: listChildren), 54 | ); 55 | }, 56 | ), 57 | ), 58 | ], 59 | ), 60 | ); 61 | } 62 | 63 | void _handleSelectAllPressed() => setState(() => _selectedItems = List.from(_listItems)); 64 | void _handleSelectNonePressed() => setState(() => _selectedItems.clear()); 65 | void _handleDeleteSelectedPressed() => setState(() => _selectedItems.clear()); 66 | 67 | void _handleItemPressed(int index) { 68 | setState(() { 69 | if (_selectedItems.contains(index)) 70 | _selectedItems.remove(index); 71 | else 72 | _selectedItems.add(index); 73 | }); 74 | } 75 | } 76 | 77 | class _GridItem extends StatelessWidget { 78 | const _GridItem(this.index, {Key? key, required this.isSelected, required this.onPressed}) : super(key: key); 79 | final int index; 80 | final bool isSelected; 81 | final void Function(int index) onPressed; 82 | 83 | @override 84 | Widget build(BuildContext context) { 85 | double borderWidth = isSelected ? 6 : 0; 86 | return Padding( 87 | padding: EdgeInsets.all(Insets.large), 88 | child: TextButton( 89 | onPressed: () => onPressed.call(index), 90 | child: Stack(children: [ 91 | Center(child: FlutterLogo(size: 64)), 92 | Container(color: Colors.grey.withOpacity(isSelected ? .5 : .7)), 93 | Align( 94 | alignment: Alignment.bottomCenter, 95 | child: Container( 96 | width: double.infinity, 97 | color: Colors.grey.shade600, 98 | height: 50, 99 | alignment: Alignment.center, 100 | child: Text("Grid Item $index", style: TextStyle(color: Colors.white)))), 101 | // Selected border 102 | Container(decoration: BoxDecoration(border: Border.all(color: Colors.blue.shade200, width: borderWidth))), 103 | ]), 104 | ), 105 | ); 106 | } 107 | } 108 | 109 | /// Intents to support keyboard shortcuts 110 | class DeleteIntent extends Intent {} 111 | 112 | class SelectAllIntent extends Intent {} 113 | 114 | class SelectNoneIntent extends Intent {} 115 | -------------------------------------------------------------------------------- /lib/pages/adaptive_reflow_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:adaptive_app_demos/app_model.dart'; 2 | import 'package:adaptive_app_demos/global/device_type.dart'; 3 | import 'package:adaptive_app_demos/global/styling.dart'; 4 | import 'package:adaptive_app_demos/widgets/scroll_view_with_scrollbars.dart'; 5 | import 'package:flextras/flextras.dart'; 6 | import 'package:flutter/material.dart'; 7 | import 'package:provider/provider.dart'; 8 | 9 | /// Shows 3 types of layout, a vertical for narrow screens, wide for wide screens, and a mixed mode. 10 | 11 | enum ReflowMode { Vertical, Horizontal, Mixed } 12 | 13 | class AdaptiveReflowPage extends StatelessWidget { 14 | @override 15 | Widget build(BuildContext context) { 16 | return LayoutBuilder(builder: (_, constraints) { 17 | /// Decide which mode to show in 18 | ReflowMode reflowMode = ReflowMode.Mixed; 19 | if (constraints.maxWidth < 800) { 20 | reflowMode = ReflowMode.Vertical; 21 | } else if (constraints.maxHeight < 800) { 22 | reflowMode = ReflowMode.Horizontal; 23 | } 24 | // In mixed mode, use a mix of Colum and Row 25 | if (reflowMode == ReflowMode.Mixed) { 26 | return Column( 27 | children: [ 28 | Expanded( 29 | child: Row( 30 | children: [ 31 | Expanded(child: _ContentPanel1()), 32 | Expanded(child: _ContentPanel2()), 33 | ], 34 | ), 35 | ), 36 | Expanded(child: _ContentPanel3()), 37 | ], 38 | ); 39 | } 40 | // In vertical or horizontal mode, use a ExpandedScrollingFlex with the same set of children 41 | else { 42 | Axis direction = reflowMode == ReflowMode.Horizontal ? Axis.horizontal : Axis.vertical; 43 | return ExpandedScrollingFlex( 44 | scrollViewBuilder: (axis, child) => ScrollViewWithScrollbars(axis: axis, child: child), 45 | direction: direction, 46 | children: [ 47 | _ContentPanel1(), 48 | _ContentPanel2(), 49 | _ContentPanel3(), 50 | ].map((c) => Expanded(child: c)).toList()); 51 | } 52 | }); 53 | } 54 | } 55 | 56 | /// For demo purposes, simulate 3 different content areas 57 | Widget _ContentPanel1() => _ContentPanel("Panel 1"); 58 | Widget _ContentPanel2() => _ContentPanel("Panel 2"); 59 | Widget _ContentPanel3() => _ContentPanel("Panel 3"); 60 | 61 | class _ContentPanel extends StatelessWidget { 62 | const _ContentPanel(this.label, {Key? key}) : super(key: key); 63 | final String label; 64 | 65 | @override 66 | Widget build(BuildContext context) { 67 | VisualDensity density = Theme.of(context).visualDensity; 68 | return ConstrainedBox( 69 | constraints: BoxConstraints(minHeight: 300, minWidth: 300), 70 | child: Padding( 71 | padding: EdgeInsets.all(Insets.large + density.vertical * 6), 72 | child: Container( 73 | alignment: Alignment.center, 74 | color: Colors.purple.shade100, 75 | child: Text(label), 76 | ), 77 | ), 78 | ); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lib/pages/focus_examples_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:adaptive_app_demos/global/styling.dart'; 2 | import 'package:flextras/flextras.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/rendering.dart'; 5 | import 'package:flutter/services.dart'; 6 | 7 | class FocusExamplesPage extends StatelessWidget { 8 | @override 9 | Widget build(BuildContext context) { 10 | return Padding( 11 | padding: EdgeInsets.all(Insets.extraLarge), 12 | child: Center( 13 | child: SeparatedColumn( 14 | separatorBuilder: () => SizedBox(height: 5), 15 | mainAxisSize: MainAxisSize.min, 16 | children: [ 17 | // Basic widget that can accept traversal, built with FocusableActionDetector 18 | Text("BasicActionDetector:"), 19 | BasicActionDetector(), 20 | SizedBox(height: 10), 21 | 22 | // Clickable widget that can accept traversal, built with FocusableActionDetector 23 | Text("AdvancedActionDetector:"), 24 | ClickableActionDetector(), 25 | SizedBox(height: 10), 26 | 27 | // A totally custom control, built by stacking together various widgets 28 | Text("CustomControl:"), 29 | ClickableControl(), 30 | _TextListener(), 31 | ], 32 | ), 33 | ), 34 | ); 35 | } 36 | } 37 | 38 | class _TextListener extends StatefulWidget { 39 | @override 40 | State<_TextListener> createState() => __TextListenerState(); 41 | } 42 | 43 | class __TextListenerState extends State<_TextListener> { 44 | FocusNode focusNode = FocusNode(); 45 | 46 | @override 47 | Widget build(BuildContext context) { 48 | return Focus( 49 | onKey: (FocusNode node, RawKeyEvent event) { 50 | if (event is RawKeyDownEvent) { 51 | print(event.logicalKey); 52 | } 53 | return KeyEventResult.ignored; 54 | }, 55 | child: ConstrainedBox( 56 | constraints: BoxConstraints(maxWidth: 400), 57 | child: TextField( 58 | decoration: InputDecoration( 59 | border: OutlineInputBorder(), 60 | ), 61 | ), 62 | ), 63 | ); 64 | } 65 | } 66 | 67 | class BasicActionDetector extends StatefulWidget { 68 | @override 69 | State createState() => _BasicActionDetectorState(); 70 | } 71 | 72 | class _BasicActionDetectorState extends State { 73 | bool _hasFocus = false; 74 | @override 75 | Widget build(BuildContext context) { 76 | return FocusableActionDetector( 77 | onFocusChange: (value) => setState(() => _hasFocus = value), 78 | actions: >{ 79 | ActivateIntent: CallbackAction(onInvoke: (Intent intent) { 80 | print("Enter or Space was pressed!"); 81 | }), 82 | }, 83 | child: Stack( 84 | clipBehavior: Clip.none, 85 | children: [ 86 | FlutterLogo(size: 100), 87 | // Position focus in the negative margin for a cool effect 88 | if (_hasFocus) Positioned(left: -4, top: -4, bottom: -4, right: -4, child: _roundedBorder()) 89 | ], 90 | ), 91 | ); 92 | } 93 | } 94 | 95 | /// Uses [FocusableActionDetector] 96 | class ClickableActionDetector extends StatefulWidget { 97 | @override 98 | _ClickableActionDetectorState createState() => _ClickableActionDetectorState(); 99 | } 100 | 101 | class _ClickableActionDetectorState extends State { 102 | bool _hasFocus = false; 103 | late FocusNode _focusNode = FocusNode(); 104 | 105 | @override 106 | Widget build(BuildContext context) { 107 | return FocusableActionDetector( 108 | focusNode: _focusNode, 109 | mouseCursor: SystemMouseCursors.click, 110 | onFocusChange: (value) => setState(() => _hasFocus = value), 111 | actions: >{ 112 | ActivateIntent: CallbackAction(onInvoke: (Intent intent) { 113 | _submit(); 114 | }), 115 | }, 116 | child: GestureDetector( 117 | onTap: () { 118 | _focusNode.requestFocus(); 119 | _submit(); 120 | }, 121 | child: Logo(showBorder: _hasFocus), 122 | ), 123 | ); 124 | } 125 | 126 | void _submit() => print("Submit!"); 127 | } 128 | 129 | // Example of a custom focus widget from scratch 130 | class ClickableControl extends StatelessWidget { 131 | @override 132 | Widget build(BuildContext _) { 133 | return Focus( 134 | // Process keyboard event 135 | onKey: _handleKeyDown, 136 | child: Builder( 137 | builder: (context) { 138 | // Check whether we have focus 139 | bool hasFocus = Focus.of(context).hasFocus; 140 | // Show hand cursor 141 | return MouseRegion( 142 | cursor: SystemMouseCursors.click, 143 | // Request focus when clicked 144 | child: GestureDetector( 145 | onTap: () { 146 | Focus.of(context).requestFocus(); 147 | _submit(); 148 | }, 149 | child: Logo(showBorder: hasFocus), 150 | ), 151 | ); 152 | }, 153 | ), 154 | ); 155 | } 156 | 157 | void _submit() => print("Submit!"); 158 | 159 | KeyEventResult _handleKeyDown(FocusNode node, RawKeyEvent event) { 160 | if (event is RawKeyDownEvent && [LogicalKeyboardKey.enter, LogicalKeyboardKey.space].contains(event.logicalKey)) { 161 | _submit(); 162 | return KeyEventResult.handled; 163 | } 164 | return KeyEventResult.ignored; 165 | } 166 | } 167 | 168 | class Logo extends StatelessWidget { 169 | const Logo({Key? key, required this.showBorder}) : super(key: key); 170 | final bool showBorder; 171 | 172 | @override 173 | Widget build(BuildContext context) { 174 | return Stack( 175 | clipBehavior: Clip.none, 176 | children: [ 177 | // Content 178 | FlutterLogo(size: 100), 179 | // Focus effect: 180 | if (showBorder) Positioned(left: 0, top: -4, bottom: -4, right: -4, child: _roundedBorder()) 181 | ], 182 | ); 183 | } 184 | } 185 | 186 | Widget _roundedBorder() => Container( 187 | decoration: BoxDecoration(border: Border.all(color: Colors.orange), borderRadius: BorderRadius.circular(6))); 188 | -------------------------------------------------------------------------------- /lib/pages/login_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:adaptive_app_demos/app_model.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:provider/provider.dart'; 4 | 5 | // Uses full-screen breakpoints to reflow the widget tree 6 | class LoginPage extends StatelessWidget { 7 | @override 8 | Widget build(BuildContext context) { 9 | Size screenSize = MediaQuery.of(context).size; 10 | // Reflow from Row to Col when in Portrait mode 11 | bool useVerticalLayout = screenSize.width < screenSize.height; 12 | // Hide an optional element if the screen gets too small. 13 | bool hideDetailPanel = screenSize.shortestSide < 250; 14 | return Scaffold( 15 | body: Flex(direction: useVerticalLayout ? Axis.vertical : Axis.horizontal, children: [ 16 | if (hideDetailPanel == false) ...[ 17 | Flexible(child: _LoginDetailPanel()), 18 | ], 19 | Flexible(child: _LoginForm()), 20 | ]), 21 | ); 22 | } 23 | } 24 | 25 | class _LoginDetailPanel extends StatelessWidget { 26 | @override 27 | Widget build(BuildContext context) => Container( 28 | alignment: Alignment.center, 29 | color: Colors.grey.shade300, 30 | child: Text( 31 | "LOGIN VIEW\nBRANDING", 32 | style: TextStyle(fontSize: 64), 33 | textAlign: TextAlign.center, 34 | ), 35 | ); 36 | } 37 | 38 | class _LoginForm extends StatelessWidget { 39 | @override 40 | Widget build(BuildContext context) { 41 | // When login button is pressed, show the Dashboard page. 42 | void handleLoginPressed() => context.read().login(); 43 | 44 | // Example Form, pressing the login button will show the Dashboard page 45 | return Center( 46 | // Use a maxWidth so the form is responsive, but does get not too large on bigger screens 47 | child: ConstrainedBox( 48 | constraints: BoxConstraints(maxWidth: 450), 49 | // Very small screens may require vertical scrolling of the form 50 | child: SingleChildScrollView( 51 | child: Padding( 52 | padding: const EdgeInsets.all(16.0), 53 | child: Column( 54 | children: [ 55 | TextField(decoration: _getTextDecoration("Enter email...")), 56 | SizedBox(height: 16), 57 | TextField(decoration: _getTextDecoration("Enter password..."), obscureText: true), 58 | SizedBox(height: 16), 59 | OutlinedButton( 60 | onPressed: handleLoginPressed, 61 | child: Container( 62 | width: double.infinity, 63 | alignment: Alignment.center, 64 | padding: EdgeInsets.all(16.0), 65 | child: Text("Log In"), 66 | )), 67 | ], 68 | ), 69 | ), 70 | ), 71 | ), 72 | ); 73 | } 74 | } 75 | 76 | InputDecoration _getTextDecoration(String hint) => InputDecoration(border: OutlineInputBorder(), hintText: hint); 77 | -------------------------------------------------------------------------------- /lib/widgets/app_title_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:adaptive_app_demos/app_model.dart'; 2 | import 'package:adaptive_app_demos/global/device_type.dart'; 3 | import 'package:adaptive_app_demos/global/styling.dart'; 4 | import 'package:bitsdojo_window/bitsdojo_window.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:provider/provider.dart'; 7 | 8 | class AppTitleBar extends StatelessWidget { 9 | @override 10 | Widget build(BuildContext context) { 11 | bool isLinuxOrWindows = DeviceType.isWindows || DeviceType.isLinux; 12 | bool enableTouch = context.select((AppModel m) => m.touchMode); 13 | bool useSmallHeader = MediaQuery.of(context).size.width < 600; 14 | bool hideTitle = MediaQuery.of(context).size.width < 400; 15 | TextStyle style = useSmallHeader ? TextStyles.h2 : TextStyles.h1; 16 | return Material( 17 | child: Stack( 18 | children: [ 19 | // Sets background and height for title bar 20 | Positioned.fill(child: Container(color: Colors.blue)), 21 | 22 | // App Logo or Title 23 | if (hideTitle == false) 24 | Positioned.fill( 25 | child: Center( 26 | child: Text("Adaptive Scaffold", style: style.copyWith(color: Colors.white)), 27 | ), 28 | ), 29 | 30 | Positioned.fill(child: MoveWindow()), 31 | 32 | // Enable Touch Mode Button 33 | Row( 34 | // Touch button should be right-aligned on macOS to avoid the native buttons 35 | textDirection: DeviceType.isMacOS ? TextDirection.rtl : TextDirection.ltr, 36 | children: [ 37 | IconButton( 38 | onPressed: () => context.read().toggleTouchMode(), 39 | icon: Icon(enableTouch ? Icons.mouse : Icons.fingerprint)), 40 | Spacer(), 41 | ], 42 | ), 43 | 44 | // Add window controls for Linux/Windows 45 | if (isLinuxOrWindows) ...[ 46 | Row( 47 | children: [ 48 | Spacer(), 49 | MinimizeWindowButton(colors: buttonColors), 50 | MaximizeWindowButton(colors: buttonColors), 51 | CloseWindowButton(colors: closeButtonColors), 52 | ], 53 | ) 54 | ] 55 | ], 56 | ), 57 | ); 58 | } 59 | } 60 | 61 | final buttonColors = WindowButtonColors(iconNormal: Colors.white); 62 | 63 | final closeButtonColors = WindowButtonColors(iconNormal: Colors.white); 64 | -------------------------------------------------------------------------------- /lib/widgets/buttons.dart: -------------------------------------------------------------------------------- 1 | import 'package:adaptive_app_demos/global/styling.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class StyledTextButton extends StatelessWidget { 5 | const StyledTextButton({Key? key, required this.onPressed, required this.child}) : super(key: key); 6 | final VoidCallback onPressed; 7 | final Widget child; 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return TextButton( 12 | style: ButtonStyle( 13 | padding: MaterialStateProperty.all(EdgeInsets.all(Insets.small)), 14 | textStyle: MaterialStateProperty.all(TextStyles.buttonText2)), 15 | onPressed: onPressed, 16 | child: child); 17 | } 18 | } 19 | 20 | class SecondaryMenuButton extends StatelessWidget { 21 | const SecondaryMenuButton({Key? key, required this.label}) : super(key: key); 22 | final String label; 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | return StyledTextButton( 27 | onPressed: () {}, 28 | child: Container( 29 | width: double.infinity, 30 | padding: EdgeInsets.all(16), 31 | child: Text(label), 32 | ), 33 | ); 34 | } 35 | } 36 | 37 | class SelectedPageButton extends StatelessWidget { 38 | const SelectedPageButton({Key? key, required this.onPressed, required this.label, required this.isSelected}) 39 | : super(key: key); 40 | final VoidCallback? onPressed; 41 | final String label; 42 | final bool isSelected; 43 | 44 | @override 45 | Widget build(BuildContext context) { 46 | return Container( 47 | color: isSelected ? Colors.grey.shade200 : null, 48 | child: TextButton( 49 | onPressed: onPressed, 50 | child: Container( 51 | width: double.infinity, 52 | padding: EdgeInsets.all(Insets.large), 53 | child: Text(label, style: TextStyles.buttonText1, maxLines: 1), 54 | ), 55 | ), 56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/widgets/ok_cancel_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:adaptive_app_demos/global/device_type.dart'; 2 | import 'package:adaptive_app_demos/global/styling.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class OkCancelDialog extends StatelessWidget { 6 | const OkCancelDialog({Key? key, required this.message}) : super(key: key); 7 | final String message; 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return Dialog( 12 | child: ConstrainedBox( 13 | constraints: BoxConstraints(maxWidth: 400), 14 | child: Padding( 15 | padding: EdgeInsets.all(Insets.large), 16 | child: Column( 17 | mainAxisSize: MainAxisSize.min, 18 | children: [ 19 | Text(message), 20 | SizedBox(height: Insets.large), 21 | _OkCancelButtons(), 22 | ], 23 | ), 24 | ), 25 | ), 26 | ); 27 | } 28 | } 29 | 30 | class _OkCancelButtons extends StatelessWidget { 31 | @override 32 | Widget build(BuildContext context) { 33 | TextDirection btnDirection = DeviceType.isWindows && false ? TextDirection.rtl : TextDirection.ltr; 34 | return Row( 35 | children: [ 36 | Spacer(), 37 | Row( 38 | textDirection: btnDirection, 39 | children: [ 40 | DialogButton(label: "Cancel", onPressed: () => Navigator.pop(context, false)), 41 | DialogButton(label: "Ok", onPressed: () => Navigator.pop(context, true)), 42 | ], 43 | ), 44 | ], 45 | ); 46 | } 47 | } 48 | 49 | class DialogButton extends StatelessWidget { 50 | const DialogButton({Key? key, required this.onPressed, required this.label}) : super(key: key); 51 | final VoidCallback onPressed; 52 | final String label; 53 | 54 | @override 55 | Widget build(BuildContext context) { 56 | return TextButton( 57 | onPressed: onPressed, 58 | child: Padding( 59 | padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), 60 | child: Text(label), 61 | )); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/widgets/scroll_view_with_scrollbars.dart: -------------------------------------------------------------------------------- 1 | import 'package:adaptive_app_demos/app_model.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:provider/provider.dart'; 4 | 5 | class ScrollViewWithScrollbars extends StatefulWidget { 6 | const ScrollViewWithScrollbars({Key? key, required this.child, this.axis = Axis.vertical}) : super(key: key); 7 | final Widget child; 8 | final Axis axis; 9 | @override 10 | _ScrollViewWithScrollbarsState createState() => _ScrollViewWithScrollbarsState(); 11 | } 12 | 13 | class _ScrollViewWithScrollbarsState extends State { 14 | ScrollController _scrollController = ScrollController(); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return Scrollbar( 19 | controller: _scrollController, 20 | isAlwaysShown: context.select((AppModel m) => m.touchMode), 21 | child: SingleChildScrollView( 22 | scrollDirection: widget.axis, 23 | controller: _scrollController, 24 | child: widget.child, 25 | ), 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /linux/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(runner LANGUAGES CXX) 3 | 4 | set(BINARY_NAME "adaptive_app_demos") 5 | set(APPLICATION_ID "com.example.adaptive_app_demos") 6 | 7 | cmake_policy(SET CMP0063 NEW) 8 | 9 | set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") 10 | 11 | # Root filesystem for cross-building. 12 | if(FLUTTER_TARGET_PLATFORM_SYSROOT) 13 | set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) 14 | set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) 15 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 16 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 17 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 18 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 19 | endif() 20 | 21 | # Configure build options. 22 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 23 | set(CMAKE_BUILD_TYPE "Debug" CACHE 24 | STRING "Flutter build mode" FORCE) 25 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 26 | "Debug" "Profile" "Release") 27 | endif() 28 | 29 | # Compilation settings that should be applied to most targets. 30 | function(APPLY_STANDARD_SETTINGS TARGET) 31 | target_compile_features(${TARGET} PUBLIC cxx_std_14) 32 | target_compile_options(${TARGET} PRIVATE -Wall -Werror) 33 | target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") 34 | target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") 35 | endfunction() 36 | 37 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 38 | 39 | # Flutter library and tool build rules. 40 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 41 | 42 | # System-level dependencies. 43 | find_package(PkgConfig REQUIRED) 44 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 45 | 46 | add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") 47 | 48 | # Application build 49 | add_executable(${BINARY_NAME} 50 | "main.cc" 51 | "my_application.cc" 52 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 53 | ) 54 | apply_standard_settings(${BINARY_NAME}) 55 | target_link_libraries(${BINARY_NAME} PRIVATE flutter) 56 | target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) 57 | add_dependencies(${BINARY_NAME} flutter_assemble) 58 | # Only the install-generated bundle's copy of the executable will launch 59 | # correctly, since the resources must in the right relative locations. To avoid 60 | # people trying to run the unbundled copy, put it in a subdirectory instead of 61 | # the default top-level location. 62 | set_target_properties(${BINARY_NAME} 63 | PROPERTIES 64 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" 65 | ) 66 | 67 | # Generated plugin build rules, which manage building the plugins and adding 68 | # them to the application. 69 | include(flutter/generated_plugins.cmake) 70 | 71 | 72 | # === Installation === 73 | # By default, "installing" just makes a relocatable bundle in the build 74 | # directory. 75 | set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") 76 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 77 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 78 | endif() 79 | 80 | # Start with a clean build bundle directory every time. 81 | install(CODE " 82 | file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") 83 | " COMPONENT Runtime) 84 | 85 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 86 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") 87 | 88 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 89 | COMPONENT Runtime) 90 | 91 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 92 | COMPONENT Runtime) 93 | 94 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 95 | COMPONENT Runtime) 96 | 97 | if(PLUGIN_BUNDLED_LIBRARIES) 98 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" 99 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 100 | COMPONENT Runtime) 101 | endif() 102 | 103 | # Fully re-copy the assets directory on each build to avoid having stale files 104 | # from a previous install. 105 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 106 | install(CODE " 107 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 108 | " COMPONENT Runtime) 109 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 110 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 111 | 112 | # Install the AOT library on non-Debug builds only. 113 | if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") 114 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 115 | COMPONENT Runtime) 116 | endif() 117 | -------------------------------------------------------------------------------- /linux/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 4 | 5 | # Configuration provided via flutter tool. 6 | include(${EPHEMERAL_DIR}/generated_config.cmake) 7 | 8 | # TODO: Move the rest of this into files in ephemeral. See 9 | # https://github.com/flutter/flutter/issues/57146. 10 | 11 | # Serves the same purpose as list(TRANSFORM ... PREPEND ...), 12 | # which isn't available in 3.10. 13 | function(list_prepend LIST_NAME PREFIX) 14 | set(NEW_LIST "") 15 | foreach(element ${${LIST_NAME}}) 16 | list(APPEND NEW_LIST "${PREFIX}${element}") 17 | endforeach(element) 18 | set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) 19 | endfunction() 20 | 21 | # === Flutter Library === 22 | # System-level dependencies. 23 | find_package(PkgConfig REQUIRED) 24 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 25 | pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) 26 | pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) 27 | pkg_check_modules(BLKID REQUIRED IMPORTED_TARGET blkid) 28 | pkg_check_modules(LZMA REQUIRED IMPORTED_TARGET liblzma) 29 | 30 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") 31 | 32 | # Published to parent scope for install step. 33 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 34 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 35 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 36 | set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) 37 | 38 | list(APPEND FLUTTER_LIBRARY_HEADERS 39 | "fl_basic_message_channel.h" 40 | "fl_binary_codec.h" 41 | "fl_binary_messenger.h" 42 | "fl_dart_project.h" 43 | "fl_engine.h" 44 | "fl_json_message_codec.h" 45 | "fl_json_method_codec.h" 46 | "fl_message_codec.h" 47 | "fl_method_call.h" 48 | "fl_method_channel.h" 49 | "fl_method_codec.h" 50 | "fl_method_response.h" 51 | "fl_plugin_registrar.h" 52 | "fl_plugin_registry.h" 53 | "fl_standard_message_codec.h" 54 | "fl_standard_method_codec.h" 55 | "fl_string_codec.h" 56 | "fl_value.h" 57 | "fl_view.h" 58 | "flutter_linux.h" 59 | ) 60 | list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") 61 | add_library(flutter INTERFACE) 62 | target_include_directories(flutter INTERFACE 63 | "${EPHEMERAL_DIR}" 64 | ) 65 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") 66 | target_link_libraries(flutter INTERFACE 67 | PkgConfig::GTK 68 | PkgConfig::GLIB 69 | PkgConfig::GIO 70 | PkgConfig::BLKID 71 | PkgConfig::LZMA 72 | ) 73 | add_dependencies(flutter flutter_assemble) 74 | 75 | # === Flutter tool backend === 76 | # _phony_ is a non-existent file to force this command to run every time, 77 | # since currently there's no way to get a full input/output list from the 78 | # flutter tool. 79 | add_custom_command( 80 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 81 | ${CMAKE_CURRENT_BINARY_DIR}/_phony_ 82 | COMMAND ${CMAKE_COMMAND} -E env 83 | ${FLUTTER_TOOL_ENVIRONMENT} 84 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" 85 | ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} 86 | VERBATIM 87 | ) 88 | add_custom_target(flutter_assemble DEPENDS 89 | "${FLUTTER_LIBRARY}" 90 | ${FLUTTER_LIBRARY_HEADERS} 91 | ) 92 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | #include "generated_plugin_registrant.h" 6 | 7 | #include 8 | 9 | void fl_register_plugins(FlPluginRegistry* registry) { 10 | g_autoptr(FlPluginRegistrar) bitsdojo_window_linux_registrar = 11 | fl_plugin_registry_get_registrar_for_plugin(registry, "BitsdojoWindowPlugin"); 12 | bitsdojo_window_plugin_register_with_registrar(bitsdojo_window_linux_registrar); 13 | } 14 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 6 | #define GENERATED_PLUGIN_REGISTRANT_ 7 | 8 | #include 9 | 10 | // Registers Flutter plugins. 11 | void fl_register_plugins(FlPluginRegistry* registry); 12 | 13 | #endif // GENERATED_PLUGIN_REGISTRANT_ 14 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | bitsdojo_window_linux 7 | ) 8 | 9 | set(PLUGIN_BUNDLED_LIBRARIES) 10 | 11 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 12 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) 13 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 14 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 15 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 16 | endforeach(plugin) 17 | -------------------------------------------------------------------------------- /linux/main.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | int main(int argc, char** argv) { 4 | g_autoptr(MyApplication) app = my_application_new(); 5 | return g_application_run(G_APPLICATION(app), argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /linux/my_application.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | #include 4 | #ifdef GDK_WINDOWING_X11 5 | #include 6 | #endif 7 | 8 | #include "flutter/generated_plugin_registrant.h" 9 | 10 | struct _MyApplication { 11 | GtkApplication parent_instance; 12 | char** dart_entrypoint_arguments; 13 | }; 14 | 15 | G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) 16 | 17 | // Implements GApplication::activate. 18 | static void my_application_activate(GApplication* application) { 19 | MyApplication* self = MY_APPLICATION(application); 20 | GtkWindow* window = 21 | GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); 22 | 23 | // Use a header bar when running in GNOME as this is the common style used 24 | // by applications and is the setup most users will be using (e.g. Ubuntu 25 | // desktop). 26 | // If running on X and not using GNOME then just use a traditional title bar 27 | // in case the window manager does more exotic layout, e.g. tiling. 28 | // If running on Wayland assume the header bar will work (may need changing 29 | // if future cases occur). 30 | gboolean use_header_bar = TRUE; 31 | #ifdef GDK_WINDOWING_X11 32 | GdkScreen *screen = gtk_window_get_screen(window); 33 | if (GDK_IS_X11_SCREEN(screen)) { 34 | const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); 35 | if (g_strcmp0(wm_name, "GNOME Shell") != 0) { 36 | use_header_bar = FALSE; 37 | } 38 | } 39 | #endif 40 | if (use_header_bar) { 41 | GtkHeaderBar *header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); 42 | gtk_widget_show(GTK_WIDGET(header_bar)); 43 | gtk_header_bar_set_title(header_bar, "adaptive_app_demos"); 44 | gtk_header_bar_set_show_close_button(header_bar, TRUE); 45 | gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); 46 | } 47 | else { 48 | gtk_window_set_title(window, "adaptive_app_demos"); 49 | } 50 | 51 | gtk_window_set_default_size(window, 1280, 720); 52 | gtk_widget_show(GTK_WIDGET(window)); 53 | 54 | g_autoptr(FlDartProject) project = fl_dart_project_new(); 55 | fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); 56 | 57 | FlView* view = fl_view_new(project); 58 | gtk_widget_show(GTK_WIDGET(view)); 59 | gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); 60 | 61 | fl_register_plugins(FL_PLUGIN_REGISTRY(view)); 62 | 63 | gtk_widget_grab_focus(GTK_WIDGET(view)); 64 | } 65 | 66 | // Implements GApplication::local_command_line. 67 | static gboolean my_application_local_command_line(GApplication* application, gchar ***arguments, int *exit_status) { 68 | MyApplication* self = MY_APPLICATION(application); 69 | // Strip out the first argument as it is the binary name. 70 | self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); 71 | 72 | g_autoptr(GError) error = nullptr; 73 | if (!g_application_register(application, nullptr, &error)) { 74 | g_warning("Failed to register: %s", error->message); 75 | *exit_status = 1; 76 | return TRUE; 77 | } 78 | 79 | g_application_activate(application); 80 | *exit_status = 0; 81 | 82 | return TRUE; 83 | } 84 | 85 | // Implements GObject::dispose. 86 | static void my_application_dispose(GObject *object) { 87 | MyApplication* self = MY_APPLICATION(object); 88 | g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); 89 | G_OBJECT_CLASS(my_application_parent_class)->dispose(object); 90 | } 91 | 92 | static void my_application_class_init(MyApplicationClass* klass) { 93 | G_APPLICATION_CLASS(klass)->activate = my_application_activate; 94 | G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; 95 | G_OBJECT_CLASS(klass)->dispose = my_application_dispose; 96 | } 97 | 98 | static void my_application_init(MyApplication* self) {} 99 | 100 | MyApplication* my_application_new() { 101 | return MY_APPLICATION(g_object_new(my_application_get_type(), 102 | "application-id", APPLICATION_ID, 103 | "flags", G_APPLICATION_NON_UNIQUE, 104 | nullptr)); 105 | } 106 | -------------------------------------------------------------------------------- /linux/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/xcuserdata/ 7 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "ephemeral/Flutter-Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "ephemeral/Flutter-Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /macos/Flutter/GeneratedPluginRegistrant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | import FlutterMacOS 6 | import Foundation 7 | 8 | import bitsdojo_window_macos 9 | 10 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 11 | BitsdojoWindowPlugin.register(with: registry.registrar(forPlugin: "BitsdojoWindowPlugin")) 12 | } 13 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 51; 7 | objects = { 8 | 9 | /* Begin PBXAggregateTarget section */ 10 | 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { 11 | isa = PBXAggregateTarget; 12 | buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; 13 | buildPhases = ( 14 | 33CC111E2044C6BF0003C045 /* ShellScript */, 15 | ); 16 | dependencies = ( 17 | ); 18 | name = "Flutter Assemble"; 19 | productName = FLX; 20 | }; 21 | /* End PBXAggregateTarget section */ 22 | 23 | /* Begin PBXBuildFile section */ 24 | 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; 25 | 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; 26 | 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 27 | 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 28 | 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; 29 | /* End PBXBuildFile section */ 30 | 31 | /* Begin PBXContainerItemProxy section */ 32 | 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { 33 | isa = PBXContainerItemProxy; 34 | containerPortal = 33CC10E52044A3C60003C045 /* Project object */; 35 | proxyType = 1; 36 | remoteGlobalIDString = 33CC111A2044C6BA0003C045; 37 | remoteInfo = FLX; 38 | }; 39 | /* End PBXContainerItemProxy section */ 40 | 41 | /* Begin PBXCopyFilesBuildPhase section */ 42 | 33CC110E2044A8840003C045 /* Bundle Framework */ = { 43 | isa = PBXCopyFilesBuildPhase; 44 | buildActionMask = 2147483647; 45 | dstPath = ""; 46 | dstSubfolderSpec = 10; 47 | files = ( 48 | ); 49 | name = "Bundle Framework"; 50 | runOnlyForDeploymentPostprocessing = 0; 51 | }; 52 | /* End PBXCopyFilesBuildPhase section */ 53 | 54 | /* Begin PBXFileReference section */ 55 | 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; 56 | 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; 57 | 33CC10ED2044A3C60003C045 /* adaptive_app_demos.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "adaptive_app_demos.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 58 | 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 59 | 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; 60 | 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 61 | 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; 62 | 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; 63 | 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; 64 | 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; 65 | 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; 66 | 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; 67 | 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; 68 | 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; 69 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; 70 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; 71 | /* End PBXFileReference section */ 72 | 73 | /* Begin PBXFrameworksBuildPhase section */ 74 | 33CC10EA2044A3C60003C045 /* Frameworks */ = { 75 | isa = PBXFrameworksBuildPhase; 76 | buildActionMask = 2147483647; 77 | files = ( 78 | ); 79 | runOnlyForDeploymentPostprocessing = 0; 80 | }; 81 | /* End PBXFrameworksBuildPhase section */ 82 | 83 | /* Begin PBXGroup section */ 84 | 33BA886A226E78AF003329D5 /* Configs */ = { 85 | isa = PBXGroup; 86 | children = ( 87 | 33E5194F232828860026EE4D /* AppInfo.xcconfig */, 88 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 89 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 90 | 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, 91 | ); 92 | path = Configs; 93 | sourceTree = ""; 94 | }; 95 | 33CC10E42044A3C60003C045 = { 96 | isa = PBXGroup; 97 | children = ( 98 | 33FAB671232836740065AC1E /* Runner */, 99 | 33CEB47122A05771004F2AC0 /* Flutter */, 100 | 33CC10EE2044A3C60003C045 /* Products */, 101 | D73912EC22F37F3D000D13A0 /* Frameworks */, 102 | ); 103 | sourceTree = ""; 104 | }; 105 | 33CC10EE2044A3C60003C045 /* Products */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 33CC10ED2044A3C60003C045 /* adaptive_app_demos.app */, 109 | ); 110 | name = Products; 111 | sourceTree = ""; 112 | }; 113 | 33CC11242044D66E0003C045 /* Resources */ = { 114 | isa = PBXGroup; 115 | children = ( 116 | 33CC10F22044A3C60003C045 /* Assets.xcassets */, 117 | 33CC10F42044A3C60003C045 /* MainMenu.xib */, 118 | 33CC10F72044A3C60003C045 /* Info.plist */, 119 | ); 120 | name = Resources; 121 | path = ..; 122 | sourceTree = ""; 123 | }; 124 | 33CEB47122A05771004F2AC0 /* Flutter */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, 128 | 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, 129 | 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, 130 | 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, 131 | ); 132 | path = Flutter; 133 | sourceTree = ""; 134 | }; 135 | 33FAB671232836740065AC1E /* Runner */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | 33CC10F02044A3C60003C045 /* AppDelegate.swift */, 139 | 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, 140 | 33E51913231747F40026EE4D /* DebugProfile.entitlements */, 141 | 33E51914231749380026EE4D /* Release.entitlements */, 142 | 33CC11242044D66E0003C045 /* Resources */, 143 | 33BA886A226E78AF003329D5 /* Configs */, 144 | ); 145 | path = Runner; 146 | sourceTree = ""; 147 | }; 148 | D73912EC22F37F3D000D13A0 /* Frameworks */ = { 149 | isa = PBXGroup; 150 | children = ( 151 | ); 152 | name = Frameworks; 153 | sourceTree = ""; 154 | }; 155 | /* End PBXGroup section */ 156 | 157 | /* Begin PBXNativeTarget section */ 158 | 33CC10EC2044A3C60003C045 /* Runner */ = { 159 | isa = PBXNativeTarget; 160 | buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; 161 | buildPhases = ( 162 | 33CC10E92044A3C60003C045 /* Sources */, 163 | 33CC10EA2044A3C60003C045 /* Frameworks */, 164 | 33CC10EB2044A3C60003C045 /* Resources */, 165 | 33CC110E2044A8840003C045 /* Bundle Framework */, 166 | 3399D490228B24CF009A79C7 /* ShellScript */, 167 | ); 168 | buildRules = ( 169 | ); 170 | dependencies = ( 171 | 33CC11202044C79F0003C045 /* PBXTargetDependency */, 172 | ); 173 | name = Runner; 174 | productName = Runner; 175 | productReference = 33CC10ED2044A3C60003C045 /* adaptive_app_demos.app */; 176 | productType = "com.apple.product-type.application"; 177 | }; 178 | /* End PBXNativeTarget section */ 179 | 180 | /* Begin PBXProject section */ 181 | 33CC10E52044A3C60003C045 /* Project object */ = { 182 | isa = PBXProject; 183 | attributes = { 184 | LastSwiftUpdateCheck = 0920; 185 | LastUpgradeCheck = 0930; 186 | ORGANIZATIONNAME = ""; 187 | TargetAttributes = { 188 | 33CC10EC2044A3C60003C045 = { 189 | CreatedOnToolsVersion = 9.2; 190 | LastSwiftMigration = 1100; 191 | ProvisioningStyle = Automatic; 192 | SystemCapabilities = { 193 | com.apple.Sandbox = { 194 | enabled = 1; 195 | }; 196 | }; 197 | }; 198 | 33CC111A2044C6BA0003C045 = { 199 | CreatedOnToolsVersion = 9.2; 200 | ProvisioningStyle = Manual; 201 | }; 202 | }; 203 | }; 204 | buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; 205 | compatibilityVersion = "Xcode 9.3"; 206 | developmentRegion = en; 207 | hasScannedForEncodings = 0; 208 | knownRegions = ( 209 | en, 210 | Base, 211 | ); 212 | mainGroup = 33CC10E42044A3C60003C045; 213 | productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; 214 | projectDirPath = ""; 215 | projectRoot = ""; 216 | targets = ( 217 | 33CC10EC2044A3C60003C045 /* Runner */, 218 | 33CC111A2044C6BA0003C045 /* Flutter Assemble */, 219 | ); 220 | }; 221 | /* End PBXProject section */ 222 | 223 | /* Begin PBXResourcesBuildPhase section */ 224 | 33CC10EB2044A3C60003C045 /* Resources */ = { 225 | isa = PBXResourcesBuildPhase; 226 | buildActionMask = 2147483647; 227 | files = ( 228 | 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, 229 | 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, 230 | ); 231 | runOnlyForDeploymentPostprocessing = 0; 232 | }; 233 | /* End PBXResourcesBuildPhase section */ 234 | 235 | /* Begin PBXShellScriptBuildPhase section */ 236 | 3399D490228B24CF009A79C7 /* ShellScript */ = { 237 | isa = PBXShellScriptBuildPhase; 238 | buildActionMask = 2147483647; 239 | files = ( 240 | ); 241 | inputFileListPaths = ( 242 | ); 243 | inputPaths = ( 244 | ); 245 | outputFileListPaths = ( 246 | ); 247 | outputPaths = ( 248 | ); 249 | runOnlyForDeploymentPostprocessing = 0; 250 | shellPath = /bin/sh; 251 | shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; 252 | }; 253 | 33CC111E2044C6BF0003C045 /* ShellScript */ = { 254 | isa = PBXShellScriptBuildPhase; 255 | buildActionMask = 2147483647; 256 | files = ( 257 | ); 258 | inputFileListPaths = ( 259 | Flutter/ephemeral/FlutterInputs.xcfilelist, 260 | ); 261 | inputPaths = ( 262 | Flutter/ephemeral/tripwire, 263 | ); 264 | outputFileListPaths = ( 265 | Flutter/ephemeral/FlutterOutputs.xcfilelist, 266 | ); 267 | outputPaths = ( 268 | ); 269 | runOnlyForDeploymentPostprocessing = 0; 270 | shellPath = /bin/sh; 271 | shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; 272 | }; 273 | /* End PBXShellScriptBuildPhase section */ 274 | 275 | /* Begin PBXSourcesBuildPhase section */ 276 | 33CC10E92044A3C60003C045 /* Sources */ = { 277 | isa = PBXSourcesBuildPhase; 278 | buildActionMask = 2147483647; 279 | files = ( 280 | 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, 281 | 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, 282 | 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, 283 | ); 284 | runOnlyForDeploymentPostprocessing = 0; 285 | }; 286 | /* End PBXSourcesBuildPhase section */ 287 | 288 | /* Begin PBXTargetDependency section */ 289 | 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { 290 | isa = PBXTargetDependency; 291 | target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; 292 | targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; 293 | }; 294 | /* End PBXTargetDependency section */ 295 | 296 | /* Begin PBXVariantGroup section */ 297 | 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { 298 | isa = PBXVariantGroup; 299 | children = ( 300 | 33CC10F52044A3C60003C045 /* Base */, 301 | ); 302 | name = MainMenu.xib; 303 | path = Runner; 304 | sourceTree = ""; 305 | }; 306 | /* End PBXVariantGroup section */ 307 | 308 | /* Begin XCBuildConfiguration section */ 309 | 338D0CE9231458BD00FA5F75 /* Profile */ = { 310 | isa = XCBuildConfiguration; 311 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 312 | buildSettings = { 313 | ALWAYS_SEARCH_USER_PATHS = NO; 314 | CLANG_ANALYZER_NONNULL = YES; 315 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 316 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 317 | CLANG_CXX_LIBRARY = "libc++"; 318 | CLANG_ENABLE_MODULES = YES; 319 | CLANG_ENABLE_OBJC_ARC = YES; 320 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 321 | CLANG_WARN_BOOL_CONVERSION = YES; 322 | CLANG_WARN_CONSTANT_CONVERSION = YES; 323 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 324 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 325 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 326 | CLANG_WARN_EMPTY_BODY = YES; 327 | CLANG_WARN_ENUM_CONVERSION = YES; 328 | CLANG_WARN_INFINITE_RECURSION = YES; 329 | CLANG_WARN_INT_CONVERSION = YES; 330 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 331 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 332 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 333 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 334 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 335 | CODE_SIGN_IDENTITY = "-"; 336 | COPY_PHASE_STRIP = NO; 337 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 338 | ENABLE_NS_ASSERTIONS = NO; 339 | ENABLE_STRICT_OBJC_MSGSEND = YES; 340 | GCC_C_LANGUAGE_STANDARD = gnu11; 341 | GCC_NO_COMMON_BLOCKS = YES; 342 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 343 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 344 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 345 | GCC_WARN_UNUSED_FUNCTION = YES; 346 | GCC_WARN_UNUSED_VARIABLE = YES; 347 | MACOSX_DEPLOYMENT_TARGET = 10.11; 348 | MTL_ENABLE_DEBUG_INFO = NO; 349 | SDKROOT = macosx; 350 | SWIFT_COMPILATION_MODE = wholemodule; 351 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 352 | }; 353 | name = Profile; 354 | }; 355 | 338D0CEA231458BD00FA5F75 /* Profile */ = { 356 | isa = XCBuildConfiguration; 357 | baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; 358 | buildSettings = { 359 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 360 | CLANG_ENABLE_MODULES = YES; 361 | CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; 362 | CODE_SIGN_STYLE = Automatic; 363 | COMBINE_HIDPI_IMAGES = YES; 364 | INFOPLIST_FILE = Runner/Info.plist; 365 | LD_RUNPATH_SEARCH_PATHS = ( 366 | "$(inherited)", 367 | "@executable_path/../Frameworks", 368 | ); 369 | PROVISIONING_PROFILE_SPECIFIER = ""; 370 | SWIFT_VERSION = 5.0; 371 | }; 372 | name = Profile; 373 | }; 374 | 338D0CEB231458BD00FA5F75 /* Profile */ = { 375 | isa = XCBuildConfiguration; 376 | buildSettings = { 377 | CODE_SIGN_STYLE = Manual; 378 | PRODUCT_NAME = "$(TARGET_NAME)"; 379 | }; 380 | name = Profile; 381 | }; 382 | 33CC10F92044A3C60003C045 /* Debug */ = { 383 | isa = XCBuildConfiguration; 384 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 385 | buildSettings = { 386 | ALWAYS_SEARCH_USER_PATHS = NO; 387 | CLANG_ANALYZER_NONNULL = YES; 388 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 389 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 390 | CLANG_CXX_LIBRARY = "libc++"; 391 | CLANG_ENABLE_MODULES = YES; 392 | CLANG_ENABLE_OBJC_ARC = YES; 393 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 394 | CLANG_WARN_BOOL_CONVERSION = YES; 395 | CLANG_WARN_CONSTANT_CONVERSION = YES; 396 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 397 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 398 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 399 | CLANG_WARN_EMPTY_BODY = YES; 400 | CLANG_WARN_ENUM_CONVERSION = YES; 401 | CLANG_WARN_INFINITE_RECURSION = YES; 402 | CLANG_WARN_INT_CONVERSION = YES; 403 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 404 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 405 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 406 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 407 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 408 | CODE_SIGN_IDENTITY = "-"; 409 | COPY_PHASE_STRIP = NO; 410 | DEBUG_INFORMATION_FORMAT = dwarf; 411 | ENABLE_STRICT_OBJC_MSGSEND = YES; 412 | ENABLE_TESTABILITY = YES; 413 | GCC_C_LANGUAGE_STANDARD = gnu11; 414 | GCC_DYNAMIC_NO_PIC = NO; 415 | GCC_NO_COMMON_BLOCKS = YES; 416 | GCC_OPTIMIZATION_LEVEL = 0; 417 | GCC_PREPROCESSOR_DEFINITIONS = ( 418 | "DEBUG=1", 419 | "$(inherited)", 420 | ); 421 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 422 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 423 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 424 | GCC_WARN_UNUSED_FUNCTION = YES; 425 | GCC_WARN_UNUSED_VARIABLE = YES; 426 | MACOSX_DEPLOYMENT_TARGET = 10.11; 427 | MTL_ENABLE_DEBUG_INFO = YES; 428 | ONLY_ACTIVE_ARCH = YES; 429 | SDKROOT = macosx; 430 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 431 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 432 | }; 433 | name = Debug; 434 | }; 435 | 33CC10FA2044A3C60003C045 /* Release */ = { 436 | isa = XCBuildConfiguration; 437 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 438 | buildSettings = { 439 | ALWAYS_SEARCH_USER_PATHS = NO; 440 | CLANG_ANALYZER_NONNULL = YES; 441 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 442 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 443 | CLANG_CXX_LIBRARY = "libc++"; 444 | CLANG_ENABLE_MODULES = YES; 445 | CLANG_ENABLE_OBJC_ARC = YES; 446 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 447 | CLANG_WARN_BOOL_CONVERSION = YES; 448 | CLANG_WARN_CONSTANT_CONVERSION = YES; 449 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 450 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 451 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 452 | CLANG_WARN_EMPTY_BODY = YES; 453 | CLANG_WARN_ENUM_CONVERSION = YES; 454 | CLANG_WARN_INFINITE_RECURSION = YES; 455 | CLANG_WARN_INT_CONVERSION = YES; 456 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 457 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 458 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 459 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 460 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 461 | CODE_SIGN_IDENTITY = "-"; 462 | COPY_PHASE_STRIP = NO; 463 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 464 | ENABLE_NS_ASSERTIONS = NO; 465 | ENABLE_STRICT_OBJC_MSGSEND = YES; 466 | GCC_C_LANGUAGE_STANDARD = gnu11; 467 | GCC_NO_COMMON_BLOCKS = YES; 468 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 469 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 470 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 471 | GCC_WARN_UNUSED_FUNCTION = YES; 472 | GCC_WARN_UNUSED_VARIABLE = YES; 473 | MACOSX_DEPLOYMENT_TARGET = 10.11; 474 | MTL_ENABLE_DEBUG_INFO = NO; 475 | SDKROOT = macosx; 476 | SWIFT_COMPILATION_MODE = wholemodule; 477 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 478 | }; 479 | name = Release; 480 | }; 481 | 33CC10FC2044A3C60003C045 /* Debug */ = { 482 | isa = XCBuildConfiguration; 483 | baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; 484 | buildSettings = { 485 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 486 | CLANG_ENABLE_MODULES = YES; 487 | CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; 488 | CODE_SIGN_STYLE = Automatic; 489 | COMBINE_HIDPI_IMAGES = YES; 490 | INFOPLIST_FILE = Runner/Info.plist; 491 | LD_RUNPATH_SEARCH_PATHS = ( 492 | "$(inherited)", 493 | "@executable_path/../Frameworks", 494 | ); 495 | PROVISIONING_PROFILE_SPECIFIER = ""; 496 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 497 | SWIFT_VERSION = 5.0; 498 | }; 499 | name = Debug; 500 | }; 501 | 33CC10FD2044A3C60003C045 /* Release */ = { 502 | isa = XCBuildConfiguration; 503 | baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; 504 | buildSettings = { 505 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 506 | CLANG_ENABLE_MODULES = YES; 507 | CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; 508 | CODE_SIGN_STYLE = Automatic; 509 | COMBINE_HIDPI_IMAGES = YES; 510 | INFOPLIST_FILE = Runner/Info.plist; 511 | LD_RUNPATH_SEARCH_PATHS = ( 512 | "$(inherited)", 513 | "@executable_path/../Frameworks", 514 | ); 515 | PROVISIONING_PROFILE_SPECIFIER = ""; 516 | SWIFT_VERSION = 5.0; 517 | }; 518 | name = Release; 519 | }; 520 | 33CC111C2044C6BA0003C045 /* Debug */ = { 521 | isa = XCBuildConfiguration; 522 | buildSettings = { 523 | CODE_SIGN_STYLE = Manual; 524 | PRODUCT_NAME = "$(TARGET_NAME)"; 525 | }; 526 | name = Debug; 527 | }; 528 | 33CC111D2044C6BA0003C045 /* Release */ = { 529 | isa = XCBuildConfiguration; 530 | buildSettings = { 531 | CODE_SIGN_STYLE = Automatic; 532 | PRODUCT_NAME = "$(TARGET_NAME)"; 533 | }; 534 | name = Release; 535 | }; 536 | /* End XCBuildConfiguration section */ 537 | 538 | /* Begin XCConfigurationList section */ 539 | 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { 540 | isa = XCConfigurationList; 541 | buildConfigurations = ( 542 | 33CC10F92044A3C60003C045 /* Debug */, 543 | 33CC10FA2044A3C60003C045 /* Release */, 544 | 338D0CE9231458BD00FA5F75 /* Profile */, 545 | ); 546 | defaultConfigurationIsVisible = 0; 547 | defaultConfigurationName = Release; 548 | }; 549 | 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { 550 | isa = XCConfigurationList; 551 | buildConfigurations = ( 552 | 33CC10FC2044A3C60003C045 /* Debug */, 553 | 33CC10FD2044A3C60003C045 /* Release */, 554 | 338D0CEA231458BD00FA5F75 /* Profile */, 555 | ); 556 | defaultConfigurationIsVisible = 0; 557 | defaultConfigurationName = Release; 558 | }; 559 | 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { 560 | isa = XCConfigurationList; 561 | buildConfigurations = ( 562 | 33CC111C2044C6BA0003C045 /* Debug */, 563 | 33CC111D2044C6BA0003C045 /* Release */, 564 | 338D0CEB231458BD00FA5F75 /* Profile */, 565 | ); 566 | defaultConfigurationIsVisible = 0; 567 | defaultConfigurationName = Release; 568 | }; 569 | /* End XCConfigurationList section */ 570 | }; 571 | rootObject = 33CC10E52044A3C60003C045 /* Project object */; 572 | } 573 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 64 | 65 | 71 | 73 | 79 | 80 | 81 | 82 | 84 | 85 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @NSApplicationMain 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "app_icon_16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "app_icon_32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "app_icon_32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "app_icon_64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "app_icon_128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "app_icon_256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "app_icon_256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "app_icon_512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "app_icon_512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "app_icon_1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = adaptive_app_demos 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = com.example.adaptiveAppDemos 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2021 com.example. All rights reserved. 15 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.server 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | import bitsdojo_window_macos 4 | 5 | class MainFlutterWindow: BitsdojoWindow { 6 | 7 | override func bitsdojo_window_configure() -> UInt { 8 | return BDW_CUSTOM_FRAME | BDW_HIDE_ON_STARTUP 9 | } 10 | 11 | override func awakeFromNib() { 12 | let flutterViewController = FlutterViewController.init() 13 | let windowFrame = self.frame 14 | self.contentViewController = flutterViewController 15 | self.setFrame(windowFrame, display: true) 16 | 17 | RegisterGeneratedPlugins(registry: flutterViewController) 18 | 19 | super.awakeFromNib() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.6.1" 11 | bitsdojo_window: 12 | dependency: "direct main" 13 | description: 14 | name: bitsdojo_window 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "0.1.0+1" 18 | bitsdojo_window_linux: 19 | dependency: transitive 20 | description: 21 | name: bitsdojo_window_linux 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "0.1.0+1" 25 | bitsdojo_window_macos: 26 | dependency: transitive 27 | description: 28 | name: bitsdojo_window_macos 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "0.1.0" 32 | bitsdojo_window_platform_interface: 33 | dependency: transitive 34 | description: 35 | name: bitsdojo_window_platform_interface 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "0.1.0" 39 | bitsdojo_window_windows: 40 | dependency: transitive 41 | description: 42 | name: bitsdojo_window_windows 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "0.1.0" 46 | boolean_selector: 47 | dependency: transitive 48 | description: 49 | name: boolean_selector 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "2.1.0" 53 | characters: 54 | dependency: transitive 55 | description: 56 | name: characters 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "1.1.0" 60 | charcode: 61 | dependency: transitive 62 | description: 63 | name: charcode 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "1.2.0" 67 | clock: 68 | dependency: transitive 69 | description: 70 | name: clock 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "1.1.0" 74 | collection: 75 | dependency: transitive 76 | description: 77 | name: collection 78 | url: "https://pub.dartlang.org" 79 | source: hosted 80 | version: "1.15.0" 81 | fake_async: 82 | dependency: transitive 83 | description: 84 | name: fake_async 85 | url: "https://pub.dartlang.org" 86 | source: hosted 87 | version: "1.2.0" 88 | ffi: 89 | dependency: transitive 90 | description: 91 | name: ffi 92 | url: "https://pub.dartlang.org" 93 | source: hosted 94 | version: "1.0.0" 95 | flextras: 96 | dependency: "direct main" 97 | description: 98 | name: flextras 99 | url: "https://pub.dartlang.org" 100 | source: hosted 101 | version: "0.0.2" 102 | flutter: 103 | dependency: "direct main" 104 | description: flutter 105 | source: sdk 106 | version: "0.0.0" 107 | flutter_test: 108 | dependency: "direct dev" 109 | description: flutter 110 | source: sdk 111 | version: "0.0.0" 112 | matcher: 113 | dependency: transitive 114 | description: 115 | name: matcher 116 | url: "https://pub.dartlang.org" 117 | source: hosted 118 | version: "0.12.10" 119 | meta: 120 | dependency: transitive 121 | description: 122 | name: meta 123 | url: "https://pub.dartlang.org" 124 | source: hosted 125 | version: "1.3.0" 126 | nested: 127 | dependency: transitive 128 | description: 129 | name: nested 130 | url: "https://pub.dartlang.org" 131 | source: hosted 132 | version: "1.0.0" 133 | path: 134 | dependency: transitive 135 | description: 136 | name: path 137 | url: "https://pub.dartlang.org" 138 | source: hosted 139 | version: "1.8.0" 140 | plugin_platform_interface: 141 | dependency: transitive 142 | description: 143 | name: plugin_platform_interface 144 | url: "https://pub.dartlang.org" 145 | source: hosted 146 | version: "2.0.0" 147 | provider: 148 | dependency: "direct main" 149 | description: 150 | name: provider 151 | url: "https://pub.dartlang.org" 152 | source: hosted 153 | version: "5.0.0" 154 | sky_engine: 155 | dependency: transitive 156 | description: flutter 157 | source: sdk 158 | version: "0.0.99" 159 | source_span: 160 | dependency: transitive 161 | description: 162 | name: source_span 163 | url: "https://pub.dartlang.org" 164 | source: hosted 165 | version: "1.8.1" 166 | stack_trace: 167 | dependency: transitive 168 | description: 169 | name: stack_trace 170 | url: "https://pub.dartlang.org" 171 | source: hosted 172 | version: "1.10.0" 173 | stream_channel: 174 | dependency: transitive 175 | description: 176 | name: stream_channel 177 | url: "https://pub.dartlang.org" 178 | source: hosted 179 | version: "2.1.0" 180 | string_scanner: 181 | dependency: transitive 182 | description: 183 | name: string_scanner 184 | url: "https://pub.dartlang.org" 185 | source: hosted 186 | version: "1.1.0" 187 | term_glyph: 188 | dependency: transitive 189 | description: 190 | name: term_glyph 191 | url: "https://pub.dartlang.org" 192 | source: hosted 193 | version: "1.2.0" 194 | test_api: 195 | dependency: transitive 196 | description: 197 | name: test_api 198 | url: "https://pub.dartlang.org" 199 | source: hosted 200 | version: "0.3.0" 201 | typed_data: 202 | dependency: transitive 203 | description: 204 | name: typed_data 205 | url: "https://pub.dartlang.org" 206 | source: hosted 207 | version: "1.3.0" 208 | universal_platform: 209 | dependency: "direct main" 210 | description: 211 | name: universal_platform 212 | url: "https://pub.dartlang.org" 213 | source: hosted 214 | version: "1.0.0-nullsafety" 215 | vector_math: 216 | dependency: transitive 217 | description: 218 | name: vector_math 219 | url: "https://pub.dartlang.org" 220 | source: hosted 221 | version: "2.1.0" 222 | win32: 223 | dependency: transitive 224 | description: 225 | name: win32 226 | url: "https://pub.dartlang.org" 227 | source: hosted 228 | version: "2.0.5" 229 | sdks: 230 | dart: ">=2.12.0 <3.0.0" 231 | flutter: ">=1.22.0" 232 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: adaptive_app_demos 2 | description: Demo code for adaptive app development techniques. 3 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 4 | 5 | version: 1.0.0+1 6 | 7 | environment: 8 | sdk: ">=2.12.0 <3.0.0" 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | 14 | bitsdojo_window: ^0.1.0+1 15 | flextras: ^0.0.2 16 | provider: ^5.0.0 17 | universal_platform: ^1.0.0-nullsafety 18 | 19 | dev_dependencies: 20 | flutter_test: 21 | sdk: flutter 22 | 23 | flutter: 24 | uses-material-design: true 25 | -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/web/favicon.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/web/icons/Icon-512.png -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | adaptive_app_demos 27 | 28 | 29 | 30 | 33 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "adaptive_app_demos", 3 | "short_name": "adaptive_app_demos", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "Demo code for adaptive app development techniques.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /windows/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | project(adaptive_app_demos LANGUAGES CXX) 3 | 4 | set(BINARY_NAME "adaptive_app_demos") 5 | 6 | cmake_policy(SET CMP0063 NEW) 7 | 8 | set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") 9 | 10 | # Configure build options. 11 | get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) 12 | if(IS_MULTICONFIG) 13 | set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" 14 | CACHE STRING "" FORCE) 15 | else() 16 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 17 | set(CMAKE_BUILD_TYPE "Debug" CACHE 18 | STRING "Flutter build mode" FORCE) 19 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 20 | "Debug" "Profile" "Release") 21 | endif() 22 | endif() 23 | 24 | set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") 25 | set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") 26 | set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") 27 | set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") 28 | 29 | # Use Unicode for all projects. 30 | add_definitions(-DUNICODE -D_UNICODE) 31 | 32 | # Compilation settings that should be applied to most targets. 33 | function(APPLY_STANDARD_SETTINGS TARGET) 34 | target_compile_features(${TARGET} PUBLIC cxx_std_17) 35 | target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") 36 | target_compile_options(${TARGET} PRIVATE /EHsc) 37 | target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") 38 | target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") 39 | endfunction() 40 | 41 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 42 | 43 | # Flutter library and tool build rules. 44 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 45 | 46 | # Application build 47 | add_subdirectory("runner") 48 | 49 | # Generated plugin build rules, which manage building the plugins and adding 50 | # them to the application. 51 | include(flutter/generated_plugins.cmake) 52 | 53 | 54 | # === Installation === 55 | # Support files are copied into place next to the executable, so that it can 56 | # run in place. This is done instead of making a separate bundle (as on Linux) 57 | # so that building and running from within Visual Studio will work. 58 | set(BUILD_BUNDLE_DIR "$") 59 | # Make the "install" step default, as it's required to run. 60 | set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) 61 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 62 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 63 | endif() 64 | 65 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 66 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") 67 | 68 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 69 | COMPONENT Runtime) 70 | 71 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 72 | COMPONENT Runtime) 73 | 74 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 75 | COMPONENT Runtime) 76 | 77 | if(PLUGIN_BUNDLED_LIBRARIES) 78 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" 79 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 80 | COMPONENT Runtime) 81 | endif() 82 | 83 | # Fully re-copy the assets directory on each build to avoid having stale files 84 | # from a previous install. 85 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 86 | install(CODE " 87 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 88 | " COMPONENT Runtime) 89 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 90 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 91 | 92 | # Install the AOT library on non-Debug builds only. 93 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 94 | CONFIGURATIONS Profile;Release 95 | COMPONENT Runtime) 96 | -------------------------------------------------------------------------------- /windows/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 4 | 5 | # Configuration provided via flutter tool. 6 | include(${EPHEMERAL_DIR}/generated_config.cmake) 7 | 8 | # TODO: Move the rest of this into files in ephemeral. See 9 | # https://github.com/flutter/flutter/issues/57146. 10 | set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") 11 | 12 | # === Flutter Library === 13 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") 14 | 15 | # Published to parent scope for install step. 16 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 17 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 18 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 19 | set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) 20 | 21 | list(APPEND FLUTTER_LIBRARY_HEADERS 22 | "flutter_export.h" 23 | "flutter_windows.h" 24 | "flutter_messenger.h" 25 | "flutter_plugin_registrar.h" 26 | "flutter_texture_registrar.h" 27 | ) 28 | list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") 29 | add_library(flutter INTERFACE) 30 | target_include_directories(flutter INTERFACE 31 | "${EPHEMERAL_DIR}" 32 | ) 33 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") 34 | add_dependencies(flutter flutter_assemble) 35 | 36 | # === Wrapper === 37 | list(APPEND CPP_WRAPPER_SOURCES_CORE 38 | "core_implementations.cc" 39 | "standard_codec.cc" 40 | ) 41 | list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") 42 | list(APPEND CPP_WRAPPER_SOURCES_PLUGIN 43 | "plugin_registrar.cc" 44 | ) 45 | list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") 46 | list(APPEND CPP_WRAPPER_SOURCES_APP 47 | "flutter_engine.cc" 48 | "flutter_view_controller.cc" 49 | ) 50 | list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") 51 | 52 | # Wrapper sources needed for a plugin. 53 | add_library(flutter_wrapper_plugin STATIC 54 | ${CPP_WRAPPER_SOURCES_CORE} 55 | ${CPP_WRAPPER_SOURCES_PLUGIN} 56 | ) 57 | apply_standard_settings(flutter_wrapper_plugin) 58 | set_target_properties(flutter_wrapper_plugin PROPERTIES 59 | POSITION_INDEPENDENT_CODE ON) 60 | set_target_properties(flutter_wrapper_plugin PROPERTIES 61 | CXX_VISIBILITY_PRESET hidden) 62 | target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) 63 | target_include_directories(flutter_wrapper_plugin PUBLIC 64 | "${WRAPPER_ROOT}/include" 65 | ) 66 | add_dependencies(flutter_wrapper_plugin flutter_assemble) 67 | 68 | # Wrapper sources needed for the runner. 69 | add_library(flutter_wrapper_app STATIC 70 | ${CPP_WRAPPER_SOURCES_CORE} 71 | ${CPP_WRAPPER_SOURCES_APP} 72 | ) 73 | apply_standard_settings(flutter_wrapper_app) 74 | target_link_libraries(flutter_wrapper_app PUBLIC flutter) 75 | target_include_directories(flutter_wrapper_app PUBLIC 76 | "${WRAPPER_ROOT}/include" 77 | ) 78 | add_dependencies(flutter_wrapper_app flutter_assemble) 79 | 80 | # === Flutter tool backend === 81 | # _phony_ is a non-existent file to force this command to run every time, 82 | # since currently there's no way to get a full input/output list from the 83 | # flutter tool. 84 | set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") 85 | set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) 86 | add_custom_command( 87 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 88 | ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} 89 | ${CPP_WRAPPER_SOURCES_APP} 90 | ${PHONY_OUTPUT} 91 | COMMAND ${CMAKE_COMMAND} -E env 92 | ${FLUTTER_TOOL_ENVIRONMENT} 93 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" 94 | windows-x64 $ 95 | VERBATIM 96 | ) 97 | add_custom_target(flutter_assemble DEPENDS 98 | "${FLUTTER_LIBRARY}" 99 | ${FLUTTER_LIBRARY_HEADERS} 100 | ${CPP_WRAPPER_SOURCES_CORE} 101 | ${CPP_WRAPPER_SOURCES_PLUGIN} 102 | ${CPP_WRAPPER_SOURCES_APP} 103 | ) 104 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | #include "generated_plugin_registrant.h" 6 | 7 | #include 8 | 9 | void RegisterPlugins(flutter::PluginRegistry* registry) { 10 | BitsdojoWindowPluginRegisterWithRegistrar( 11 | registry->GetRegistrarForPlugin("BitsdojoWindowPlugin")); 12 | } 13 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 6 | #define GENERATED_PLUGIN_REGISTRANT_ 7 | 8 | #include 9 | 10 | // Registers Flutter plugins. 11 | void RegisterPlugins(flutter::PluginRegistry* registry); 12 | 13 | #endif // GENERATED_PLUGIN_REGISTRANT_ 14 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | bitsdojo_window_windows 7 | ) 8 | 9 | set(PLUGIN_BUNDLED_LIBRARIES) 10 | 11 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 12 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) 13 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 14 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 15 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 16 | endforeach(plugin) 17 | -------------------------------------------------------------------------------- /windows/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | project(runner LANGUAGES CXX) 3 | 4 | add_executable(${BINARY_NAME} WIN32 5 | "flutter_window.cpp" 6 | "main.cpp" 7 | "run_loop.cpp" 8 | "utils.cpp" 9 | "win32_window.cpp" 10 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 11 | "Runner.rc" 12 | "runner.exe.manifest" 13 | ) 14 | apply_standard_settings(${BINARY_NAME}) 15 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") 16 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) 17 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 18 | add_dependencies(${BINARY_NAME} flutter_assemble) 19 | -------------------------------------------------------------------------------- /windows/runner/Runner.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #pragma code_page(65001) 4 | #include "resource.h" 5 | 6 | #define APSTUDIO_READONLY_SYMBOLS 7 | ///////////////////////////////////////////////////////////////////////////// 8 | // 9 | // Generated from the TEXTINCLUDE 2 resource. 10 | // 11 | #include "winres.h" 12 | 13 | ///////////////////////////////////////////////////////////////////////////// 14 | #undef APSTUDIO_READONLY_SYMBOLS 15 | 16 | ///////////////////////////////////////////////////////////////////////////// 17 | // English (United States) resources 18 | 19 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 20 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Icon 51 | // 52 | 53 | // Icon with lowest ID value placed first to ensure application icon 54 | // remains consistent on all systems. 55 | IDI_APP_ICON ICON "resources\\app_icon.ico" 56 | 57 | 58 | ///////////////////////////////////////////////////////////////////////////// 59 | // 60 | // Version 61 | // 62 | 63 | #ifdef FLUTTER_BUILD_NUMBER 64 | #define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER 65 | #else 66 | #define VERSION_AS_NUMBER 1,0,0 67 | #endif 68 | 69 | #ifdef FLUTTER_BUILD_NAME 70 | #define VERSION_AS_STRING #FLUTTER_BUILD_NAME 71 | #else 72 | #define VERSION_AS_STRING "1.0.0" 73 | #endif 74 | 75 | VS_VERSION_INFO VERSIONINFO 76 | FILEVERSION VERSION_AS_NUMBER 77 | PRODUCTVERSION VERSION_AS_NUMBER 78 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 79 | #ifdef _DEBUG 80 | FILEFLAGS VS_FF_DEBUG 81 | #else 82 | FILEFLAGS 0x0L 83 | #endif 84 | FILEOS VOS__WINDOWS32 85 | FILETYPE VFT_APP 86 | FILESUBTYPE 0x0L 87 | BEGIN 88 | BLOCK "StringFileInfo" 89 | BEGIN 90 | BLOCK "040904e4" 91 | BEGIN 92 | VALUE "CompanyName", "com.example" "\0" 93 | VALUE "FileDescription", "Demo code for adaptive app development techniques." "\0" 94 | VALUE "FileVersion", VERSION_AS_STRING "\0" 95 | VALUE "InternalName", "adaptive_app_demos" "\0" 96 | VALUE "LegalCopyright", "Copyright (C) 2021 com.example. All rights reserved." "\0" 97 | VALUE "OriginalFilename", "adaptive_app_demos.exe" "\0" 98 | VALUE "ProductName", "adaptive_app_demos" "\0" 99 | VALUE "ProductVersion", VERSION_AS_STRING "\0" 100 | END 101 | END 102 | BLOCK "VarFileInfo" 103 | BEGIN 104 | VALUE "Translation", 0x409, 1252 105 | END 106 | END 107 | 108 | #endif // English (United States) resources 109 | ///////////////////////////////////////////////////////////////////////////// 110 | 111 | 112 | 113 | #ifndef APSTUDIO_INVOKED 114 | ///////////////////////////////////////////////////////////////////////////// 115 | // 116 | // Generated from the TEXTINCLUDE 3 resource. 117 | // 118 | 119 | 120 | ///////////////////////////////////////////////////////////////////////////// 121 | #endif // not APSTUDIO_INVOKED 122 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.cpp: -------------------------------------------------------------------------------- 1 | #include "flutter_window.h" 2 | 3 | #include 4 | 5 | #include "flutter/generated_plugin_registrant.h" 6 | 7 | FlutterWindow::FlutterWindow(RunLoop* run_loop, 8 | const flutter::DartProject& project) 9 | : run_loop_(run_loop), project_(project) {} 10 | 11 | FlutterWindow::~FlutterWindow() {} 12 | 13 | bool FlutterWindow::OnCreate() { 14 | if (!Win32Window::OnCreate()) { 15 | return false; 16 | } 17 | 18 | RECT frame = GetClientArea(); 19 | 20 | // The size here must match the window dimensions to avoid unnecessary surface 21 | // creation / destruction in the startup path. 22 | flutter_controller_ = std::make_unique( 23 | frame.right - frame.left, frame.bottom - frame.top, project_); 24 | // Ensure that basic setup of the controller was successful. 25 | if (!flutter_controller_->engine() || !flutter_controller_->view()) { 26 | return false; 27 | } 28 | RegisterPlugins(flutter_controller_->engine()); 29 | run_loop_->RegisterFlutterInstance(flutter_controller_->engine()); 30 | SetChildContent(flutter_controller_->view()->GetNativeWindow()); 31 | return true; 32 | } 33 | 34 | void FlutterWindow::OnDestroy() { 35 | if (flutter_controller_) { 36 | run_loop_->UnregisterFlutterInstance(flutter_controller_->engine()); 37 | flutter_controller_ = nullptr; 38 | } 39 | 40 | Win32Window::OnDestroy(); 41 | } 42 | 43 | LRESULT 44 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message, 45 | WPARAM const wparam, 46 | LPARAM const lparam) noexcept { 47 | // Give Flutter, including plugins, an opportunity to handle window messages. 48 | if (flutter_controller_) { 49 | std::optional result = 50 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, 51 | lparam); 52 | if (result) { 53 | return *result; 54 | } 55 | } 56 | 57 | switch (message) { 58 | case WM_FONTCHANGE: 59 | flutter_controller_->engine()->ReloadSystemFonts(); 60 | break; 61 | } 62 | 63 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam); 64 | } 65 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "run_loop.h" 10 | #include "win32_window.h" 11 | 12 | // A window that does nothing but host a Flutter view. 13 | class FlutterWindow : public Win32Window { 14 | public: 15 | // Creates a new FlutterWindow driven by the |run_loop|, hosting a 16 | // Flutter view running |project|. 17 | explicit FlutterWindow(RunLoop* run_loop, 18 | const flutter::DartProject& project); 19 | virtual ~FlutterWindow(); 20 | 21 | protected: 22 | // Win32Window: 23 | bool OnCreate() override; 24 | void OnDestroy() override; 25 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 26 | LPARAM const lparam) noexcept override; 27 | 28 | private: 29 | // The run loop driving events for this window. 30 | RunLoop* run_loop_; 31 | 32 | // The project to run. 33 | flutter::DartProject project_; 34 | 35 | // The Flutter instance hosted by this window. 36 | std::unique_ptr flutter_controller_; 37 | }; 38 | 39 | #endif // RUNNER_FLUTTER_WINDOW_H_ 40 | -------------------------------------------------------------------------------- /windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | auto bdw = bitsdojo_window_configure(BDW_CUSTOM_FRAME | BDW_HIDE_ON_STARTUP); 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "flutter_window.h" 9 | #include "run_loop.h" 10 | #include "utils.h" 11 | 12 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 13 | _In_ wchar_t *command_line, _In_ int show_command) { 14 | // Attach to console when present (e.g., 'flutter run') or create a 15 | // new console when running with a debugger. 16 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 17 | CreateAndAttachConsole(); 18 | } 19 | 20 | // Initialize COM, so that it is available for use in the library and/or 21 | // plugins. 22 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 23 | 24 | RunLoop run_loop; 25 | 26 | flutter::DartProject project(L"data"); 27 | 28 | std::vector command_line_arguments = 29 | GetCommandLineArguments(); 30 | 31 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); 32 | 33 | FlutterWindow window(&run_loop, project); 34 | Win32Window::Point origin(10, 10); 35 | Win32Window::Size size(1280, 720); 36 | if (!window.CreateAndShow(L"adaptive_app_demos", origin, size)) { 37 | return EXIT_FAILURE; 38 | } 39 | window.SetQuitOnClose(true); 40 | 41 | run_loop.Run(); 42 | 43 | ::CoUninitialize(); 44 | return EXIT_SUCCESS; 45 | } 46 | -------------------------------------------------------------------------------- /windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gskinnerTeam/flutter-adaptive-demo/e8f05b33c0d37744a453f67bd73912e7e5fcfc4f/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /windows/runner/run_loop.cpp: -------------------------------------------------------------------------------- 1 | #include "run_loop.h" 2 | 3 | #include 4 | 5 | #include 6 | 7 | RunLoop::RunLoop() {} 8 | 9 | RunLoop::~RunLoop() {} 10 | 11 | void RunLoop::Run() { 12 | bool keep_running = true; 13 | TimePoint next_flutter_event_time = TimePoint::clock::now(); 14 | while (keep_running) { 15 | std::chrono::nanoseconds wait_duration = 16 | std::max(std::chrono::nanoseconds(0), 17 | next_flutter_event_time - TimePoint::clock::now()); 18 | ::MsgWaitForMultipleObjects( 19 | 0, nullptr, FALSE, static_cast(wait_duration.count() / 1000), 20 | QS_ALLINPUT); 21 | bool processed_events = false; 22 | MSG message; 23 | // All pending Windows messages must be processed; MsgWaitForMultipleObjects 24 | // won't return again for items left in the queue after PeekMessage. 25 | while (::PeekMessage(&message, nullptr, 0, 0, PM_REMOVE)) { 26 | processed_events = true; 27 | if (message.message == WM_QUIT) { 28 | keep_running = false; 29 | break; 30 | } 31 | ::TranslateMessage(&message); 32 | ::DispatchMessage(&message); 33 | // Allow Flutter to process messages each time a Windows message is 34 | // processed, to prevent starvation. 35 | next_flutter_event_time = 36 | std::min(next_flutter_event_time, ProcessFlutterMessages()); 37 | } 38 | // If the PeekMessage loop didn't run, process Flutter messages. 39 | if (!processed_events) { 40 | next_flutter_event_time = 41 | std::min(next_flutter_event_time, ProcessFlutterMessages()); 42 | } 43 | } 44 | } 45 | 46 | void RunLoop::RegisterFlutterInstance( 47 | flutter::FlutterEngine* flutter_instance) { 48 | flutter_instances_.insert(flutter_instance); 49 | } 50 | 51 | void RunLoop::UnregisterFlutterInstance( 52 | flutter::FlutterEngine* flutter_instance) { 53 | flutter_instances_.erase(flutter_instance); 54 | } 55 | 56 | RunLoop::TimePoint RunLoop::ProcessFlutterMessages() { 57 | TimePoint next_event_time = TimePoint::max(); 58 | for (auto instance : flutter_instances_) { 59 | std::chrono::nanoseconds wait_duration = instance->ProcessMessages(); 60 | if (wait_duration != std::chrono::nanoseconds::max()) { 61 | next_event_time = 62 | std::min(next_event_time, TimePoint::clock::now() + wait_duration); 63 | } 64 | } 65 | return next_event_time; 66 | } 67 | -------------------------------------------------------------------------------- /windows/runner/run_loop.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_RUN_LOOP_H_ 2 | #define RUNNER_RUN_LOOP_H_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | // A runloop that will service events for Flutter instances as well 10 | // as native messages. 11 | class RunLoop { 12 | public: 13 | RunLoop(); 14 | ~RunLoop(); 15 | 16 | // Prevent copying 17 | RunLoop(RunLoop const&) = delete; 18 | RunLoop& operator=(RunLoop const&) = delete; 19 | 20 | // Runs the run loop until the application quits. 21 | void Run(); 22 | 23 | // Registers the given Flutter instance for event servicing. 24 | void RegisterFlutterInstance( 25 | flutter::FlutterEngine* flutter_instance); 26 | 27 | // Unregisters the given Flutter instance from event servicing. 28 | void UnregisterFlutterInstance( 29 | flutter::FlutterEngine* flutter_instance); 30 | 31 | private: 32 | using TimePoint = std::chrono::steady_clock::time_point; 33 | 34 | // Processes all currently pending messages for registered Flutter instances. 35 | TimePoint ProcessFlutterMessages(); 36 | 37 | std::set flutter_instances_; 38 | }; 39 | 40 | #endif // RUNNER_RUN_LOOP_H_ 41 | -------------------------------------------------------------------------------- /windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /windows/runner/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void CreateAndAttachConsole() { 11 | if (::AllocConsole()) { 12 | FILE *unused; 13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) { 14 | _dup2(_fileno(stdout), 1); 15 | } 16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) { 17 | _dup2(_fileno(stdout), 2); 18 | } 19 | std::ios::sync_with_stdio(); 20 | FlutterDesktopResyncOutputStreams(); 21 | } 22 | } 23 | 24 | std::vector GetCommandLineArguments() { 25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. 26 | int argc; 27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); 28 | if (argv == nullptr) { 29 | return std::vector(); 30 | } 31 | 32 | std::vector command_line_arguments; 33 | 34 | // Skip the first argument as it's the binary name. 35 | for (int i = 1; i < argc; i++) { 36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i])); 37 | } 38 | 39 | ::LocalFree(argv); 40 | 41 | return command_line_arguments; 42 | } 43 | 44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) { 45 | if (utf16_string == nullptr) { 46 | return std::string(); 47 | } 48 | int target_length = ::WideCharToMultiByte( 49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 50 | -1, nullptr, 0, nullptr, nullptr); 51 | if (target_length == 0) { 52 | return std::string(); 53 | } 54 | std::string utf8_string; 55 | utf8_string.resize(target_length); 56 | int converted_length = ::WideCharToMultiByte( 57 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 58 | -1, utf8_string.data(), 59 | target_length, nullptr, nullptr); 60 | if (converted_length == 0) { 61 | return std::string(); 62 | } 63 | return utf8_string; 64 | } 65 | -------------------------------------------------------------------------------- /windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | -------------------------------------------------------------------------------- /windows/runner/win32_window.cpp: -------------------------------------------------------------------------------- 1 | #include "win32_window.h" 2 | 3 | #include 4 | 5 | #include "resource.h" 6 | 7 | namespace { 8 | 9 | constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; 10 | 11 | // The number of Win32Window objects that currently exist. 12 | static int g_active_window_count = 0; 13 | 14 | using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); 15 | 16 | // Scale helper to convert logical scaler values to physical using passed in 17 | // scale factor 18 | int Scale(int source, double scale_factor) { 19 | return static_cast(source * scale_factor); 20 | } 21 | 22 | // Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. 23 | // This API is only needed for PerMonitor V1 awareness mode. 24 | void EnableFullDpiSupportIfAvailable(HWND hwnd) { 25 | HMODULE user32_module = LoadLibraryA("User32.dll"); 26 | if (!user32_module) { 27 | return; 28 | } 29 | auto enable_non_client_dpi_scaling = 30 | reinterpret_cast( 31 | GetProcAddress(user32_module, "EnableNonClientDpiScaling")); 32 | if (enable_non_client_dpi_scaling != nullptr) { 33 | enable_non_client_dpi_scaling(hwnd); 34 | FreeLibrary(user32_module); 35 | } 36 | } 37 | 38 | } // namespace 39 | 40 | // Manages the Win32Window's window class registration. 41 | class WindowClassRegistrar { 42 | public: 43 | ~WindowClassRegistrar() = default; 44 | 45 | // Returns the singleton registar instance. 46 | static WindowClassRegistrar* GetInstance() { 47 | if (!instance_) { 48 | instance_ = new WindowClassRegistrar(); 49 | } 50 | return instance_; 51 | } 52 | 53 | // Returns the name of the window class, registering the class if it hasn't 54 | // previously been registered. 55 | const wchar_t* GetWindowClass(); 56 | 57 | // Unregisters the window class. Should only be called if there are no 58 | // instances of the window. 59 | void UnregisterWindowClass(); 60 | 61 | private: 62 | WindowClassRegistrar() = default; 63 | 64 | static WindowClassRegistrar* instance_; 65 | 66 | bool class_registered_ = false; 67 | }; 68 | 69 | WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; 70 | 71 | const wchar_t* WindowClassRegistrar::GetWindowClass() { 72 | if (!class_registered_) { 73 | WNDCLASS window_class{}; 74 | window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); 75 | window_class.lpszClassName = kWindowClassName; 76 | window_class.style = CS_HREDRAW | CS_VREDRAW; 77 | window_class.cbClsExtra = 0; 78 | window_class.cbWndExtra = 0; 79 | window_class.hInstance = GetModuleHandle(nullptr); 80 | window_class.hIcon = 81 | LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); 82 | window_class.hbrBackground = 0; 83 | window_class.lpszMenuName = nullptr; 84 | window_class.lpfnWndProc = Win32Window::WndProc; 85 | RegisterClass(&window_class); 86 | class_registered_ = true; 87 | } 88 | return kWindowClassName; 89 | } 90 | 91 | void WindowClassRegistrar::UnregisterWindowClass() { 92 | UnregisterClass(kWindowClassName, nullptr); 93 | class_registered_ = false; 94 | } 95 | 96 | Win32Window::Win32Window() { 97 | ++g_active_window_count; 98 | } 99 | 100 | Win32Window::~Win32Window() { 101 | --g_active_window_count; 102 | Destroy(); 103 | } 104 | 105 | bool Win32Window::CreateAndShow(const std::wstring& title, 106 | const Point& origin, 107 | const Size& size) { 108 | Destroy(); 109 | 110 | const wchar_t* window_class = 111 | WindowClassRegistrar::GetInstance()->GetWindowClass(); 112 | 113 | const POINT target_point = {static_cast(origin.x), 114 | static_cast(origin.y)}; 115 | HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); 116 | UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); 117 | double scale_factor = dpi / 96.0; 118 | 119 | HWND window = CreateWindow( 120 | window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, 121 | Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), 122 | Scale(size.width, scale_factor), Scale(size.height, scale_factor), 123 | nullptr, nullptr, GetModuleHandle(nullptr), this); 124 | 125 | if (!window) { 126 | return false; 127 | } 128 | 129 | return OnCreate(); 130 | } 131 | 132 | // static 133 | LRESULT CALLBACK Win32Window::WndProc(HWND const window, 134 | UINT const message, 135 | WPARAM const wparam, 136 | LPARAM const lparam) noexcept { 137 | if (message == WM_NCCREATE) { 138 | auto window_struct = reinterpret_cast(lparam); 139 | SetWindowLongPtr(window, GWLP_USERDATA, 140 | reinterpret_cast(window_struct->lpCreateParams)); 141 | 142 | auto that = static_cast(window_struct->lpCreateParams); 143 | EnableFullDpiSupportIfAvailable(window); 144 | that->window_handle_ = window; 145 | } else if (Win32Window* that = GetThisFromHandle(window)) { 146 | return that->MessageHandler(window, message, wparam, lparam); 147 | } 148 | 149 | return DefWindowProc(window, message, wparam, lparam); 150 | } 151 | 152 | LRESULT 153 | Win32Window::MessageHandler(HWND hwnd, 154 | UINT const message, 155 | WPARAM const wparam, 156 | LPARAM const lparam) noexcept { 157 | switch (message) { 158 | case WM_DESTROY: 159 | window_handle_ = nullptr; 160 | Destroy(); 161 | if (quit_on_close_) { 162 | PostQuitMessage(0); 163 | } 164 | return 0; 165 | 166 | case WM_DPICHANGED: { 167 | auto newRectSize = reinterpret_cast(lparam); 168 | LONG newWidth = newRectSize->right - newRectSize->left; 169 | LONG newHeight = newRectSize->bottom - newRectSize->top; 170 | 171 | SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, 172 | newHeight, SWP_NOZORDER | SWP_NOACTIVATE); 173 | 174 | return 0; 175 | } 176 | case WM_SIZE: { 177 | RECT rect = GetClientArea(); 178 | if (child_content_ != nullptr) { 179 | // Size and position the child window. 180 | MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, 181 | rect.bottom - rect.top, TRUE); 182 | } 183 | return 0; 184 | } 185 | 186 | case WM_ACTIVATE: 187 | if (child_content_ != nullptr) { 188 | SetFocus(child_content_); 189 | } 190 | return 0; 191 | } 192 | 193 | return DefWindowProc(window_handle_, message, wparam, lparam); 194 | } 195 | 196 | void Win32Window::Destroy() { 197 | OnDestroy(); 198 | 199 | if (window_handle_) { 200 | DestroyWindow(window_handle_); 201 | window_handle_ = nullptr; 202 | } 203 | if (g_active_window_count == 0) { 204 | WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); 205 | } 206 | } 207 | 208 | Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { 209 | return reinterpret_cast( 210 | GetWindowLongPtr(window, GWLP_USERDATA)); 211 | } 212 | 213 | void Win32Window::SetChildContent(HWND content) { 214 | child_content_ = content; 215 | SetParent(content, window_handle_); 216 | RECT frame = GetClientArea(); 217 | 218 | MoveWindow(content, frame.left, frame.top, frame.right - frame.left, 219 | frame.bottom - frame.top, true); 220 | 221 | SetFocus(child_content_); 222 | } 223 | 224 | RECT Win32Window::GetClientArea() { 225 | RECT frame; 226 | GetClientRect(window_handle_, &frame); 227 | return frame; 228 | } 229 | 230 | HWND Win32Window::GetHandle() { 231 | return window_handle_; 232 | } 233 | 234 | void Win32Window::SetQuitOnClose(bool quit_on_close) { 235 | quit_on_close_ = quit_on_close; 236 | } 237 | 238 | bool Win32Window::OnCreate() { 239 | // No-op; provided for subclasses. 240 | return true; 241 | } 242 | 243 | void Win32Window::OnDestroy() { 244 | // No-op; provided for subclasses. 245 | } 246 | -------------------------------------------------------------------------------- /windows/runner/win32_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_WIN32_WINDOW_H_ 2 | #define RUNNER_WIN32_WINDOW_H_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // A class abstraction for a high DPI-aware Win32 Window. Intended to be 11 | // inherited from by classes that wish to specialize with custom 12 | // rendering and input handling 13 | class Win32Window { 14 | public: 15 | struct Point { 16 | unsigned int x; 17 | unsigned int y; 18 | Point(unsigned int x, unsigned int y) : x(x), y(y) {} 19 | }; 20 | 21 | struct Size { 22 | unsigned int width; 23 | unsigned int height; 24 | Size(unsigned int width, unsigned int height) 25 | : width(width), height(height) {} 26 | }; 27 | 28 | Win32Window(); 29 | virtual ~Win32Window(); 30 | 31 | // Creates and shows a win32 window with |title| and position and size using 32 | // |origin| and |size|. New windows are created on the default monitor. Window 33 | // sizes are specified to the OS in physical pixels, hence to ensure a 34 | // consistent size to will treat the width height passed in to this function 35 | // as logical pixels and scale to appropriate for the default monitor. Returns 36 | // true if the window was created successfully. 37 | bool CreateAndShow(const std::wstring& title, 38 | const Point& origin, 39 | const Size& size); 40 | 41 | // Release OS resources associated with window. 42 | void Destroy(); 43 | 44 | // Inserts |content| into the window tree. 45 | void SetChildContent(HWND content); 46 | 47 | // Returns the backing Window handle to enable clients to set icon and other 48 | // window properties. Returns nullptr if the window has been destroyed. 49 | HWND GetHandle(); 50 | 51 | // If true, closing this window will quit the application. 52 | void SetQuitOnClose(bool quit_on_close); 53 | 54 | // Return a RECT representing the bounds of the current client area. 55 | RECT GetClientArea(); 56 | 57 | protected: 58 | // Processes and route salient window messages for mouse handling, 59 | // size change and DPI. Delegates handling of these to member overloads that 60 | // inheriting classes can handle. 61 | virtual LRESULT MessageHandler(HWND window, 62 | UINT const message, 63 | WPARAM const wparam, 64 | LPARAM const lparam) noexcept; 65 | 66 | // Called when CreateAndShow is called, allowing subclass window-related 67 | // setup. Subclasses should return false if setup fails. 68 | virtual bool OnCreate(); 69 | 70 | // Called when Destroy is called. 71 | virtual void OnDestroy(); 72 | 73 | private: 74 | friend class WindowClassRegistrar; 75 | 76 | // OS callback called by message pump. Handles the WM_NCCREATE message which 77 | // is passed when the non-client area is being created and enables automatic 78 | // non-client DPI scaling so that the non-client area automatically 79 | // responsponds to changes in DPI. All other messages are handled by 80 | // MessageHandler. 81 | static LRESULT CALLBACK WndProc(HWND const window, 82 | UINT const message, 83 | WPARAM const wparam, 84 | LPARAM const lparam) noexcept; 85 | 86 | // Retrieves a class instance pointer for |window| 87 | static Win32Window* GetThisFromHandle(HWND const window) noexcept; 88 | 89 | bool quit_on_close_ = false; 90 | 91 | // window handle for top level window. 92 | HWND window_handle_ = nullptr; 93 | 94 | // window handle for hosted content. 95 | HWND child_content_ = nullptr; 96 | }; 97 | 98 | #endif // RUNNER_WIN32_WINDOW_H_ 99 | --------------------------------------------------------------------------------