From 7a95472d9ce8e147f3724c7dc7257d0b2ca84202 Mon Sep 17 00:00:00 2001 From: Nisacarolina Date: Tue, 19 Aug 2025 14:07:00 +0700 Subject: [PATCH] first commit --- .gitignore | 45 ++ .metadata | 45 ++ FIREBASE_SETUP.md | 218 ++++++ README.md | 16 + analysis_options.yaml | 28 + android/.gitignore | 14 + android/app/build.gradle.kts | 45 ++ android/app/google-services.json | 87 +++ android/app/src/debug/AndroidManifest.xml | 7 + android/app/src/main/AndroidManifest.xml | 45 ++ .../com/example/debartis/MainActivity.kt | 5 + .../res/drawable-v21/launch_background.xml | 12 + .../main/res/drawable/launch_background.xml | 12 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../main/res/mipmap-hdpi/launcher_icon.png | Bin 0 -> 1941 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../main/res/mipmap-mdpi/launcher_icon.png | Bin 0 -> 1397 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xhdpi/launcher_icon.png | Bin 0 -> 2708 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxhdpi/launcher_icon.png | Bin 0 -> 4257 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../main/res/mipmap-xxxhdpi/launcher_icon.png | Bin 0 -> 5416 bytes .../app/src/main/res/values-night/styles.xml | 18 + android/app/src/main/res/values/styles.xml | 18 + android/app/src/profile/AndroidManifest.xml | 7 + android/build.gradle.kts | 31 + android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 5 + android/settings.gradle.kts | 25 + assets/icons/app_icon.png | Bin 0 -> 32504 bytes assets/icons/app_icon.svg | 69 ++ generate_icon.bat | 66 ++ ios/.gitignore | 34 + ios/Flutter/AppFrameworkInfo.plist | 26 + ios/Flutter/Debug.xcconfig | 1 + ios/Flutter/Release.xcconfig | 1 + ios/Runner.xcodeproj/project.pbxproj | 616 +++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 99 +++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + ios/Runner/AppDelegate.swift | 13 + .../AppIcon.appiconset/Contents.json | 122 +++ .../Icon-App-1024x1024@1x.png | Bin 0 -> 41771 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 501 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 976 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 1518 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 728 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 1524 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 2405 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 976 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 2051 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 3009 bytes .../AppIcon.appiconset/Icon-App-50x50@1x.png | Bin 0 -> 1333 bytes .../AppIcon.appiconset/Icon-App-50x50@2x.png | Bin 0 -> 2557 bytes .../AppIcon.appiconset/Icon-App-57x57@1x.png | Bin 0 -> 1469 bytes .../AppIcon.appiconset/Icon-App-57x57@2x.png | Bin 0 -> 3061 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 3009 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 5124 bytes .../AppIcon.appiconset/Icon-App-72x72@1x.png | Bin 0 -> 1741 bytes .../AppIcon.appiconset/Icon-App-72x72@2x.png | Bin 0 -> 3820 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 1928 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 4148 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 4900 bytes .../LaunchImage.imageset/Contents.json | 23 + .../LaunchImage.imageset/LaunchImage.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/README.md | 5 + ios/Runner/Base.lproj/LaunchScreen.storyboard | 37 + ios/Runner/Base.lproj/Main.storyboard | 26 + ios/Runner/Info.plist | 49 ++ ios/Runner/Runner-Bridging-Header.h | 1 + ios/RunnerTests/RunnerTests.swift | 12 + lib/firebase_options.dart | 89 +++ lib/main.dart | 26 + lib/models/fire_detection_model.dart | 119 +++ lib/models/sensor_data_model.dart | 68 ++ lib/models/user_model.dart | 77 ++ lib/screens/dashboard_screen.dart | 447 +++++++++++ lib/screens/login_screen.dart | 411 ++++++++++ lib/screens/register_screen.dart | 0 lib/services/auth_service.dart | 135 ++++ lib/services/firestore_service.dart | 209 ++++++ lib/services/realtime_database_service.dart | 116 +++ lib/utils/app_colors.dart | 49 ++ lib/utils/app_icons.dart | 171 +++++ lib/utils/app_theme.dart | 101 +++ lib/utils/icon_generator.dart | 167 +++++ lib/widgets/custom_button.dart | 98 +++ lib/widgets/custom_text_field.dart | 71 ++ lib/widgets/sensor_card.dart | 208 ++++++ linux/.gitignore | 1 + linux/CMakeLists.txt | 128 ++++ linux/flutter/CMakeLists.txt | 88 +++ linux/flutter/generated_plugin_registrant.cc | 11 + linux/flutter/generated_plugin_registrant.h | 15 + linux/flutter/generated_plugins.cmake | 23 + linux/runner/CMakeLists.txt | 26 + linux/runner/main.cc | 6 + linux/runner/my_application.cc | 130 ++++ linux/runner/my_application.h | 18 + macos/.gitignore | 7 + macos/Flutter/Flutter-Debug.xcconfig | 1 + macos/Flutter/Flutter-Release.xcconfig | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 22 + macos/Runner.xcodeproj/project.pbxproj | 705 ++++++++++++++++++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 99 +++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + macos/Runner/AppDelegate.swift | 13 + .../AppIcon.appiconset/Contents.json | 68 ++ .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 49720 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 3424 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 435 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 8126 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 847 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 20047 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 1664 bytes macos/Runner/Base.lproj/MainMenu.xib | 343 +++++++++ macos/Runner/Configs/AppInfo.xcconfig | 14 + macos/Runner/Configs/Debug.xcconfig | 2 + macos/Runner/Configs/Release.xcconfig | 2 + macos/Runner/Configs/Warnings.xcconfig | 13 + macos/Runner/DebugProfile.entitlements | 12 + macos/Runner/Info.plist | 32 + macos/Runner/MainFlutterWindow.swift | 15 + macos/Runner/Release.entitlements | 8 + macos/RunnerTests/RunnerTests.swift | 12 + pubspec.lock | 650 ++++++++++++++++ pubspec.yaml | 121 +++ scripts/generate_icon.dart | 136 ++++ test/widget_test.dart | 30 + web/favicon.png | Bin 0 -> 435 bytes web/icons/Icon-192.png | Bin 0 -> 5416 bytes web/icons/Icon-512.png | Bin 0 -> 20047 bytes web/icons/Icon-maskable-192.png | Bin 0 -> 5416 bytes web/icons/Icon-maskable-512.png | Bin 0 -> 20047 bytes web/index.html | 38 + web/manifest.json | 35 + windows/.gitignore | 17 + windows/CMakeLists.txt | 108 +++ windows/flutter/CMakeLists.txt | 109 +++ .../flutter/generated_plugin_registrant.cc | 23 + windows/flutter/generated_plugin_registrant.h | 15 + windows/flutter/generated_plugins.cmake | 27 + windows/runner/CMakeLists.txt | 40 + windows/runner/Runner.rc | 121 +++ windows/runner/flutter_window.cpp | 71 ++ windows/runner/flutter_window.h | 33 + windows/runner/main.cpp | 43 ++ windows/runner/resource.h | 16 + windows/runner/resources/app_icon.ico | Bin 0 -> 1419 bytes windows/runner/runner.exe.manifest | 14 + windows/runner/utils.cpp | 65 ++ windows/runner/utils.h | 19 + windows/runner/win32_window.cpp | 288 +++++++ windows/runner/win32_window.h | 102 +++ 163 files changed, 8453 insertions(+) create mode 100644 .gitignore create mode 100644 .metadata create mode 100644 FIREBASE_SETUP.md create mode 100644 README.md create mode 100644 analysis_options.yaml create mode 100644 android/.gitignore create mode 100644 android/app/build.gradle.kts create mode 100644 android/app/google-services.json create mode 100644 android/app/src/debug/AndroidManifest.xml create mode 100644 android/app/src/main/AndroidManifest.xml create mode 100644 android/app/src/main/kotlin/com/example/debartis/MainActivity.kt create mode 100644 android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 android/app/src/main/res/drawable/launch_background.xml create mode 100644 android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-hdpi/launcher_icon.png create mode 100644 android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-mdpi/launcher_icon.png create mode 100644 android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-xhdpi/launcher_icon.png create mode 100644 android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png create mode 100644 android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png create mode 100644 android/app/src/main/res/values-night/styles.xml create mode 100644 android/app/src/main/res/values/styles.xml create mode 100644 android/app/src/profile/AndroidManifest.xml create mode 100644 android/build.gradle.kts create mode 100644 android/gradle.properties create mode 100644 android/gradle/wrapper/gradle-wrapper.properties create mode 100644 android/settings.gradle.kts create mode 100644 assets/icons/app_icon.png create mode 100644 assets/icons/app_icon.svg create mode 100644 generate_icon.bat create mode 100644 ios/.gitignore create mode 100644 ios/Flutter/AppFrameworkInfo.plist create mode 100644 ios/Flutter/Debug.xcconfig create mode 100644 ios/Flutter/Release.xcconfig create mode 100644 ios/Runner.xcodeproj/project.pbxproj create mode 100644 ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 ios/Runner/AppDelegate.swift create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md create mode 100644 ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 ios/Runner/Base.lproj/Main.storyboard create mode 100644 ios/Runner/Info.plist create mode 100644 ios/Runner/Runner-Bridging-Header.h create mode 100644 ios/RunnerTests/RunnerTests.swift create mode 100644 lib/firebase_options.dart create mode 100644 lib/main.dart create mode 100644 lib/models/fire_detection_model.dart create mode 100644 lib/models/sensor_data_model.dart create mode 100644 lib/models/user_model.dart create mode 100644 lib/screens/dashboard_screen.dart create mode 100644 lib/screens/login_screen.dart create mode 100644 lib/screens/register_screen.dart create mode 100644 lib/services/auth_service.dart create mode 100644 lib/services/firestore_service.dart create mode 100644 lib/services/realtime_database_service.dart create mode 100644 lib/utils/app_colors.dart create mode 100644 lib/utils/app_icons.dart create mode 100644 lib/utils/app_theme.dart create mode 100644 lib/utils/icon_generator.dart create mode 100644 lib/widgets/custom_button.dart create mode 100644 lib/widgets/custom_text_field.dart create mode 100644 lib/widgets/sensor_card.dart create mode 100644 linux/.gitignore create mode 100644 linux/CMakeLists.txt create mode 100644 linux/flutter/CMakeLists.txt create mode 100644 linux/flutter/generated_plugin_registrant.cc create mode 100644 linux/flutter/generated_plugin_registrant.h create mode 100644 linux/flutter/generated_plugins.cmake create mode 100644 linux/runner/CMakeLists.txt create mode 100644 linux/runner/main.cc create mode 100644 linux/runner/my_application.cc create mode 100644 linux/runner/my_application.h create mode 100644 macos/.gitignore create mode 100644 macos/Flutter/Flutter-Debug.xcconfig create mode 100644 macos/Flutter/Flutter-Release.xcconfig create mode 100644 macos/Flutter/GeneratedPluginRegistrant.swift create mode 100644 macos/Runner.xcodeproj/project.pbxproj create mode 100644 macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 macos/Runner/AppDelegate.swift create mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 macos/Runner/Base.lproj/MainMenu.xib create mode 100644 macos/Runner/Configs/AppInfo.xcconfig create mode 100644 macos/Runner/Configs/Debug.xcconfig create mode 100644 macos/Runner/Configs/Release.xcconfig create mode 100644 macos/Runner/Configs/Warnings.xcconfig create mode 100644 macos/Runner/DebugProfile.entitlements create mode 100644 macos/Runner/Info.plist create mode 100644 macos/Runner/MainFlutterWindow.swift create mode 100644 macos/Runner/Release.entitlements create mode 100644 macos/RunnerTests/RunnerTests.swift create mode 100644 pubspec.lock create mode 100644 pubspec.yaml create mode 100644 scripts/generate_icon.dart create mode 100644 test/widget_test.dart create mode 100644 web/favicon.png create mode 100644 web/icons/Icon-192.png create mode 100644 web/icons/Icon-512.png create mode 100644 web/icons/Icon-maskable-192.png create mode 100644 web/icons/Icon-maskable-512.png create mode 100644 web/index.html create mode 100644 web/manifest.json create mode 100644 windows/.gitignore create mode 100644 windows/CMakeLists.txt create mode 100644 windows/flutter/CMakeLists.txt create mode 100644 windows/flutter/generated_plugin_registrant.cc create mode 100644 windows/flutter/generated_plugin_registrant.h create mode 100644 windows/flutter/generated_plugins.cmake create mode 100644 windows/runner/CMakeLists.txt create mode 100644 windows/runner/Runner.rc create mode 100644 windows/runner/flutter_window.cpp create mode 100644 windows/runner/flutter_window.h create mode 100644 windows/runner/main.cpp create mode 100644 windows/runner/resource.h create mode 100644 windows/runner/resources/app_icon.ico create mode 100644 windows/runner/runner.exe.manifest create mode 100644 windows/runner/utils.cpp create mode 100644 windows/runner/utils.h create mode 100644 windows/runner/win32_window.cpp create mode 100644 windows/runner/win32_window.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..79c113f --- /dev/null +++ b/.gitignore @@ -0,0 +1,45 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.build/ +.buildlog/ +.history +.svn/ +.swiftpm/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/.metadata b/.metadata new file mode 100644 index 0000000..9a674c6 --- /dev/null +++ b/.metadata @@ -0,0 +1,45 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "35c388afb57ef061d06a39b537336c87e0e3d1b1" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1 + base_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1 + - platform: android + create_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1 + base_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1 + - platform: ios + create_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1 + base_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1 + - platform: linux + create_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1 + base_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1 + - platform: macos + create_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1 + base_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1 + - platform: web + create_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1 + base_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1 + - platform: windows + create_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1 + base_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/FIREBASE_SETUP.md b/FIREBASE_SETUP.md new file mode 100644 index 0000000..307056b --- /dev/null +++ b/FIREBASE_SETUP.md @@ -0,0 +1,218 @@ +# Firebase Setup Guide for Debartis Fire Detection + +## Langkah-langkah Setup Firebase + +### 1. Buat Project Firebase +1. Buka [Firebase Console](https://console.firebase.google.com/) +2. Klik "Add project" atau "Tambah project" +3. Masukkan nama project: `debartis-fire-detection` +4. Ikuti langkah-langkah setup hingga selesai + +### 2. Enable Authentication +1. Di Firebase Console, pilih project `debartis-fire-detection` +2. Pilih "Authentication" di sidebar +3. Pilih tab "Sign-in method" +4. Enable "Email/Password" authentication +5. Klik "Save" + +### 3. Setup Firestore Database +1. Pilih "Firestore Database" di sidebar +2. Klik "Create database" +3. Pilih "Start in test mode" (untuk development) +4. Pilih lokasi server (pilih yang paling dekat dengan lokasi Anda) +5. Klik "Done" + +### 4. Setup Storage +1. Pilih "Storage" di sidebar +2. Klik "Get started" +3. Gunakan default rules untuk sekarang +4. Pilih lokasi yang sama dengan Firestore +5. Klik "Done" + +### 5. Add Flutter App ke Firebase Project +1. Di Firebase Console, klik ikon Flutter (atau Add app > Flutter) +2. Masukkan informasi berikut: + - **Apple bundle ID**: `com.example.debartis` + - **Android package name**: `com.example.debartis` + - **App nickname**: `Debartis Fire Detection` + +### 6. Download Configuration Files +1. Download `google-services.json` untuk Android +2. Download `GoogleService-Info.plist` untuk iOS +3. Tempatkan file tersebut di lokasi yang sesuai: + - Android: `android/app/google-services.json` + - iOS: `ios/Runner/GoogleService-Info.plist` + +### 7. Update firebase_options.dart +1. Buka file `lib/firebase_options.dart` +2. Ganti semua placeholder dengan konfigurasi yang benar dari Firebase Console +3. Untuk mendapatkan konfigurasi: + - Buka Project Settings di Firebase Console + - Scroll ke bawah ke "Your apps" + - Pilih platform yang ingin dikonfigurasi + - Copy konfigurasi yang diperlukan + +### 8. Konfigurasi Android (android/app/build.gradle) +Tambahkan di bagian bawah file: +```gradle +apply plugin: 'com.google.gms.google-services' +``` + +### 9. Konfigurasi Android (android/build.gradle) +Tambahkan di dependencies: +```gradle +dependencies { + classpath 'com.google.gms:google-services:4.3.15' +} +``` + +### 10. Konfigurasi iOS (ios/Runner/Info.plist) +Tambahkan konfigurasi URL scheme jika diperlukan. + +### 11. Test Setup +1. Jalankan `flutter clean` +2. Jalankan `flutter pub get` +3. Jalankan `flutter run` +4. Coba register user baru +5. Cek Firebase Console untuk memastikan user terdaftar + +## Database Structure + +### Collections yang digunakan: + +#### 1. users +- uid (document ID) +- email: string +- displayName: string +- photoURL: string +- role: string (default: "user") +- isActive: boolean +- createdAt: timestamp +- updatedAt: timestamp + +#### 2. detections +- userId: string +- timestamp: timestamp +- location: string +- confidence: number (0.0 - 1.0) +- imageUrl: string +- status: string (detected, confirmed, false_alarm, resolved) +- severity: string (low, medium, high, critical) +- description: string +- resolved: boolean +- createdAt: timestamp + +#### 3. alerts +- userId: string +- detectionId: string +- title: string +- message: string +- severity: string +- location: string +- timestamp: timestamp +- isRead: boolean +- isResolved: boolean +- createdAt: timestamp + +#### 4. devices +- userId: string +- deviceName: string +- deviceType: string +- location: string +- status: string +- lastSeen: timestamp +- createdAt: timestamp + +## Security Rules + +### Firestore Rules +```javascript +rules_version = '2'; +service cloud.firestore { + match /databases/{database}/documents { + // Users can only access their own data + match /users/{userId} { + allow read, write: if request.auth != null && request.auth.uid == userId; + } + + // Detections - users can only access their own detections + match /detections/{detectionId} { + allow read, write: if request.auth != null && request.auth.uid == resource.data.userId; + } + + // Alerts - users can only access their own alerts + match /alerts/{alertId} { + allow read, write: if request.auth != null && request.auth.uid == resource.data.userId; + } + + // Devices - users can only access their own devices + match /devices/{deviceId} { + allow read, write: if request.auth != null && request.auth.uid == resource.data.userId; + } + } +} +``` + +### Storage Rules +```javascript +rules_version = '2'; +service firebase.storage { + match /b/{bucket}/o { + match /users/{userId}/{allPaths=**} { + allow read, write: if request.auth != null && request.auth.uid == userId; + } + } +} +``` + +## Environment Variables (Opsional) +Untuk keamanan tambahan, Anda bisa menggunakan environment variables untuk API keys. + +## Troubleshooting +- Jika error "Firebase project not found", pastikan project ID benar +- Jika error authentication, cek apakah Email/Password sudah dienable +- Jika error Firestore, cek rules dan pastikan database sudah dibuat +- Jika error di build, pastikan google-services.json sudah di tempatkan dengan benar + +### Windows Build Issues +Firebase SDK untuk Windows masih dalam tahap pengembangan dan sering mengalami masalah: + +1. **Disk Space Error**: + - Pastikan ada space disk yang cukup (minimal 5GB free) + - Hapus folder `build` dan jalankan `flutter clean` + +2. **Linking Errors** (LNK2019, LNK1120): + - Firebase Windows SDK memiliki compatibility issues + - **Solusi**: Gunakan platform lain untuk development: + ```bash + # Untuk Android + flutter run -d android + + # Untuk Web + flutter run -d chrome + + # Untuk testing di emulator + flutter emulators --launch + ``` + +3. **CMake Deprecation Warnings**: + - Ini hanya warning dan tidak mempengaruhi functionality + - Akan diperbaiki di versi Firebase SDK selanjutnya + +4. **Alternative untuk Windows Development**: + - Gunakan Firebase Web SDK melalui browser + - Develop dan test di Android emulator + - Deploy ke Android device untuk testing + +### Recommended Development Flow: +1. **Primary Development**: Android (emulator atau device) +2. **Testing**: Web browser (Chrome) +3. **Production**: Android APK build +4. **Windows**: Tunggu Firebase SDK update atau gunakan alternatif + +## Next Steps +1. Buat halaman register +2. Buat halaman home/dashboard +3. Implementasi real-time fire detection +4. Implementasi push notifications +5. Implementasi file upload untuk images diff --git a/README.md b/README.md new file mode 100644 index 0000000..c362b34 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# debartis + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..0d29021 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,28 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..be3943c --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,14 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java +.cxx/ + +# Remember to never publicly share your keystore. +# See https://flutter.dev/to/reference-keystore +key.properties +**/*.keystore +**/*.jks diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts new file mode 100644 index 0000000..cdc1dec --- /dev/null +++ b/android/app/build.gradle.kts @@ -0,0 +1,45 @@ +plugins { + id("com.android.application") + id("kotlin-android") + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id("dev.flutter.flutter-gradle-plugin") + id("com.google.gms.google-services") +} + +android { + namespace = "com.example.debartis" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_11.toString() + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "com.example.debartis" + // You can update the following values to match your application needs. + // For more information, see: https://flutter.dev/to/review-gradle-config. + minSdk = 23 + targetSdk = flutter.targetSdkVersion + versionCode = flutter.versionCode + versionName = flutter.versionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig = signingConfigs.getByName("debug") + } + } +} + +flutter { + source = "../.." +} diff --git a/android/app/google-services.json b/android/app/google-services.json new file mode 100644 index 0000000..0e9798a --- /dev/null +++ b/android/app/google-services.json @@ -0,0 +1,87 @@ +{ + "project_info": { + "project_number": "844787133360", + "firebase_url": "https://deteksi-kebakaran-berbas-915bd-default-rtdb.firebaseio.com", + "project_id": "deteksi-kebakaran-berbas-915bd", + "storage_bucket": "deteksi-kebakaran-berbas-915bd.firebasestorage.app" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:844787133360:android:31f7628397719b40494475", + "android_client_info": { + "package_name": "com.example.debartis" + } + }, + "oauth_client": [], + "api_key": [ + { + "current_key": "AIzaSyBWdmHcBVtmkZ8Eeg5aaLF9nmsiL47MXaI" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:844787133360:android:2213fea564d5cbb0494475", + "android_client_info": { + "package_name": "com.example.deteksi_kebakaran_iot" + } + }, + "oauth_client": [], + "api_key": [ + { + "current_key": "AIzaSyBWdmHcBVtmkZ8Eeg5aaLF9nmsiL47MXaI" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:844787133360:android:b1b14f81067aeb08494475", + "android_client_info": { + "package_name": "com.example.deteksikebakaran" + } + }, + "oauth_client": [], + "api_key": [ + { + "current_key": "AIzaSyBWdmHcBVtmkZ8Eeg5aaLF9nmsiL47MXaI" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:844787133360:android:cc39404d02d57b15494475", + "android_client_info": { + "package_name": "deteksikebakaran.com" + } + }, + "oauth_client": [], + "api_key": [ + { + "current_key": "AIzaSyBWdmHcBVtmkZ8Eeg5aaLF9nmsiL47MXaI" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..95c1582 --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/kotlin/com/example/debartis/MainActivity.kt b/android/app/src/main/kotlin/com/example/debartis/MainActivity.kt new file mode 100644 index 0000000..23760bb --- /dev/null +++ b/android/app/src/main/kotlin/com/example/debartis/MainActivity.kt @@ -0,0 +1,5 @@ +package com.example.debartis + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity : FlutterActivity() diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-hdpi/launcher_icon.png b/android/app/src/main/res/mipmap-hdpi/launcher_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..076c1f64bfba0bdd1e5037b7201ffda9c41062cb GIT binary patch literal 1941 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 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png b/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..c006d9c9f99fb7a1f8d56dec46e245ad37235904 GIT binary patch literal 2708 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 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png b/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5e1a6882ae4283b3223117c558a6e885f8e3d23e GIT binary patch literal 4257 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 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png b/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..88e023715d427a3fc82b9fdef07109031bd6ecc4 GIT binary patch literal 5416 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 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..06952be --- /dev/null +++ b/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..cb1ef88 --- /dev/null +++ b/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/build.gradle.kts b/android/build.gradle.kts new file mode 100644 index 0000000..84e949b --- /dev/null +++ b/android/build.gradle.kts @@ -0,0 +1,31 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath("com.google.gms:google-services:4.3.15") + } +} + +val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get() +rootProject.layout.buildDirectory.value(newBuildDir) + +subprojects { + val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) + project.layout.buildDirectory.value(newSubprojectBuildDir) +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean") { + delete(rootProject.layout.buildDirectory) +} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000..f018a61 --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError +android.useAndroidX=true +android.enableJetifier=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..afa1e8e --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts new file mode 100644 index 0000000..a439442 --- /dev/null +++ b/android/settings.gradle.kts @@ -0,0 +1,25 @@ +pluginManagement { + val flutterSdkPath = run { + val properties = java.util.Properties() + file("local.properties").inputStream().use { properties.load(it) } + val flutterSdkPath = properties.getProperty("flutter.sdk") + require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } + flutterSdkPath + } + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + id("com.android.application") version "8.7.0" apply false + id("org.jetbrains.kotlin.android") version "1.8.22" apply false +} + +include(":app") diff --git a/assets/icons/app_icon.png b/assets/icons/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5567ba8affdd7f2db631f193b5c43aba59c5525b GIT binary patch literal 32504 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/generate_icon.bat b/generate_icon.bat new file mode 100644 index 0000000..54774f2 --- /dev/null +++ b/generate_icon.bat @@ -0,0 +1,66 @@ +@echo off +echo Generating app icon... + +REM Buat icon sederhana menggunakan PowerShell dan .NET +powershell -Command " +Add-Type -AssemblyName System.Drawing +$bitmap = New-Object System.Drawing.Bitmap(512, 512) +$graphics = [System.Drawing.Graphics]::FromImage($bitmap) +$graphics.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::AntiAlias + +# Background gradient +$brush = New-Object System.Drawing.Drawing2D.LinearGradientBrush( + (New-Object System.Drawing.Point(0, 0)), + (New-Object System.Drawing.Point(512, 512)), + [System.Drawing.Color]::FromArgb(25, 118, 210), + [System.Drawing.Color]::FromArgb(33, 150, 243) +) +$graphics.FillRectangle($brush, 0, 0, 512, 512) + +# White elements +$whiteBrush = New-Object System.Drawing.SolidBrush([System.Drawing.Color]::White) +$whitePen = New-Object System.Drawing.Pen([System.Drawing.Color]::White, 8) + +# Draw API nodes (circles) +$graphics.FillEllipse($whiteBrush, 40, 180, 40, 40) +$graphics.FillEllipse($whiteBrush, 40, 236, 40, 40) +$graphics.FillEllipse($whiteBrush, 40, 292, 40, 40) + +$graphics.FillEllipse($whiteBrush, 432, 180, 40, 40) +$graphics.FillEllipse($whiteBrush, 432, 236, 40, 40) +$graphics.FillEllipse($whiteBrush, 432, 292, 40, 40) + +# Draw connection lines +$graphics.DrawLine($whitePen, 80, 200, 180, 200) +$graphics.DrawLine($whitePen, 80, 256, 180, 256) +$graphics.DrawLine($whitePen, 80, 312, 180, 312) + +$graphics.DrawLine($whitePen, 332, 200, 432, 200) +$graphics.DrawLine($whitePen, 332, 256, 432, 256) +$graphics.DrawLine($whitePen, 332, 312, 432, 312) + +# Central box +$graphics.FillRectangle($whiteBrush, 220, 180, 72, 132) + +# Fire symbol (simple triangle) +$fireBrush = New-Object System.Drawing.SolidBrush([System.Drawing.Color]::FromArgb(244, 67, 54)) +$firePoints = @( + (New-Object System.Drawing.Point(256, 190)), + (New-Object System.Drawing.Point(245, 230)), + (New-Object System.Drawing.Point(267, 230)) +) +$graphics.FillPolygon($fireBrush, $firePoints) + +# Status indicator +$statusBrush = New-Object System.Drawing.SolidBrush([System.Drawing.Color]::FromArgb(76, 175, 80)) +$graphics.FillEllipse($statusBrush, 376, 88, 48, 48) + +$graphics.Dispose() +$bitmap.Save('assets\icons\app_icon.png', [System.Drawing.Imaging.ImageFormat]::Png) +$bitmap.Dispose() + +Write-Host 'Icon generated successfully!' +" + +echo Icon generated at assets\icons\app_icon.png +pause diff --git a/ios/.gitignore b/ios/.gitignore new file mode 100644 index 0000000..7a7f987 --- /dev/null +++ b/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000..7c56964 --- /dev/null +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 12.0 + + diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..592ceee --- /dev/null +++ b/ios/Flutter/Debug.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000..592ceee --- /dev/null +++ b/ios/Flutter/Release.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..98b9e1e --- /dev/null +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,616 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C8082294A63A400263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C807B294A618700263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 331C8082294A63A400263BE5 /* RunnerTests */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C8080294A63A400263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 331C807D294A63A400263BE5 /* Sources */, + 331C807F294A63A400263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C8086294A63A400263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C8080294A63A400263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + 331C8080294A63A400263BE5 /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C807F294A63A400263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C807D294A63A400263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.debartis; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 331C8088294A63A400263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.debartis.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Debug; + }; + 331C8089294A63A400263BE5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.debartis.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Release; + }; + 331C808A294A63A400263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.debartis.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.debartis; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.debartis; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C8088294A63A400263BE5 /* Debug */, + 331C8089294A63A400263BE5 /* Release */, + 331C808A294A63A400263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..15cada4 --- /dev/null +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..1d526a1 --- /dev/null +++ b/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift new file mode 100644 index 0000000..6266644 --- /dev/null +++ b/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import Flutter +import UIKit + +@main +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d36b1fa --- /dev/null +++ b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..004e0f22d77c4249f00c90418043e6c3f21ab6d5 GIT binary patch literal 41771 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

TKWJr({_oFH*yo!a>UXZAW9| zk3z!-uy-5eqUx(&Wsy42=WB+R^EydTTI@0$q?Ftx|1lnpL$_r0dDjySBm_GP(ze4S3Tv;iz3%)muVj@~!u1SPb6^#N&wLrMw=##pksN^##1@ z#nb;E)cH}Kz!N|7nSarqC)%ZLxQSuM7!+&Y#BqxwhMXgG3wO|21PUMgT?9A+2-w_Q z_GG}b*D{}aV*HwhCf^$@zds50u$e^}fJ#A@kP;^88XAkiyI}B{)H{KmuQe#5?}5de z&C2uYrhNDZ0s;P_dKLu?%wMAt$TSyMF?et;(0USnIm0zL9`}MWCNv5b!QT44J3m@G zk7Jw}jFMe{HzTO2#bk-V1j{}b(Z9tI7eWEjqWkiAWyTj3iij5-1s)3>zL(E{XVRFh ztodIq5(>`5BQo@kkfF>rzm$RVWOvpE5`*mXp9lzv#DJC(awFN;_>-9W3|(lmHD63K zkHE#b4>yStIh@0>Of|(1xKN;_eR~LuWFRgZfsN@nDLRta6joeqiXQK3M!d7Z-scm# z!C8$L^*hkda%txdAfi*6Ps(I#yOW$FQ!d*ne=-uwHYkcpwotyBd_8!>ZNO1Pm!{}v ztO~G(*o63^oy6^62Kw0hN376qvsoVonYw|1A*Dvx>AJ8U%df80#G>AAC@i0h zCL_0sa)SV9^63N|l�@k@s{_YP_P5Y6a}Ni2XfV=tDY0{n12T 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 THDq@ZJm;fYdPhH%tD9cqFC`JLKYNOyw#GHc}4 zm>AoAp@dwTHq3s8lGZf^Av+X4B>B?w>k30jQ-_*{iZ&`et}9x^Lt8hDu3|abh*IUb zNN{g?VCy7r+SMEcaYH{Xw=}!-6~xcaouQqT7hIAuC%{BG`fW0OToW zu#0ou!4U4`L>?okP~|`yghWo{InH*VS{~c+@CdCfEz|Gt7;yC^M!f=VCpE)zMLQv> zH#x(spt?iM74`t!zFBU!0WLB|wpek8WHKCBo9}s)ZOwAnZ=a^LxTG!bztR#4w3yh0 zfLYA|t24nD#krRaN{*-0=x)YA-=^&kIT_nLH+HdwMdVLzCEH{VPCF=78R r8$;roSy%SK^sb~ICNs<@^Xu>lqeG97fyYws00000NkvXXu0mjfOsMD0 literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..6dcd55a20615930c6a3d82c3c6386a2bea06f8b9 GIT binary patch literal 976 zcmV;>126oEP)cK&Yvp^f+}?nqI4|dTPwEJKZhz0JO@c~!(?1=xm!3sSFNSeK^0cQ;X036p9om_DyWKLo61p9)BxLile~4YFol<=Qth!IAlpCxA7I z>s)ieo%WJNPo$&Q?qvC-)MVV`REk8e&lWb7(=W#ST_0IlsD;DYkVha?T|_^$S{y!3 z%&_>~^I^>s-~rI{udHn@AbQV+u}JMXnopC7kA{FDQBye$03CHq6s+i6FTZ#jE0`b7 z(8Yn3^&r;i!?w9VvNz|Z&8vQ|5Q2gnqP3Rp9Sdm8TmzHr&=f0lr!94VwG{(8Kj@`N z6y~M+PnfwP{8s;SbgXi!{>+t1s@j@t=1MO$+02!8s^-j<395SL$`DmCb7hVyFRqMI y#m<#!YRuy-P-CxC7O1h#l?AF;T$!f+S$_km85Vy2j794J0000Hv#KR;W z(H_Rw*eIZBbX>Nzt11NA1gv63+mM>3T}m%&y0kROa%tiuwprrD`Rq7uT(-vkUSfYf z{`=?SCG8H%`&rz=iOQUS-BVE|w- z+>}Z?)RmQDdew=o)_*oN9Ko?90AQ`_XYbVh1hYX!xhSS95f!zrL4i2qMSSUjwf$4G zT`I~%8M^m9j!7Z{kU#Mtr75iU%{pYVu=&wo@Q-Om6LF)HIPi2eO~Ipo#x6}J zQDaG-#v$l=V?#p?1psK?XxVxE*3}uNd{5vQ+`Ntwwt1EX(FnBJh23g1u8FwX1W$K5 zomLD0uzQj`Jw8gb6Bv!w_Fx7Gs*G%-+qZ-B&8{n=69d=ebP~DH{)Ln zh5FrMO!Gz3yuyP>A7+hXxm!i&CEL*?+x0{6P|F&cf}dT9U7JfQE#rB) zp{YdMk(m|d3gpV1$IW@i_(U9DdQNdx8YCLXBLggAbas9gWAYT)4gZ>?T+t|`ldK6h+|mC&;5rbBIGGDXD`)g8xuZhx{|;$_{eh1f#Evz_*bY?*?h<6%?#S0P`7Q!Y&#Y6yW?C{sS) zRU2hEi5H8&_IEh@#fkTD`O1k&*t4ZCscZB(Il)*5VWaY*RT~n3^Y^8~X-!u1q?J z7?;^K)e&)-T~mD#m(w-X9r3bVQ#}%^cTIIltkN~rFL9}^sji6?yQX?4F7BEdfLKyOK31;tqfe=vrKde}uTQgG^7FCG;y`XB7#LGa)~ML|@G zUaH_WT#?EYLEXA_T|2kgK`o`t(xgd~R+?sQ(q>3W%;6(^yzlcp@AE$I_u<}K-w{Fp z0LB&RRxk^wuSgBhB0&qrsIk=oz4duuJg*595W-s#u>z{%{s&M5V8Yirs0rV8L0w&o zd%TY~IN$2}HGL$u$h6!2jlvrq_ss8fpi-uFeX~~npX_Oh%5#N5J*et4YnqBuUvlk6 zt5dOEjcP(r`2rl@=e$1b8K2DlSQlGCFCKJV_HTo783^KCOn5ZIwSw;M#&^#!ujYhM zOx)BLp@5$`x8FrmL^vU+IqGSEf)lY43T(?@)2B$kl@faxT!9O+Vt*COFi zZ^!uonxTo%qJ-*w8=uOqL86iyE)>yG{hY4r>B#=rb4wu6r3%j zSVhev9k=hPDI5Y|Xkx6Y!{(J%)qxrvq>p*2z>9o??~^aA0~7bdx%sqY(tX2+G#x${ z^nCjnvZO$8Uex&vAdxLi#QD{{Y;Gyk1Z{GqWcbQNQi^6|!yMl^yM4^hOvd@hO0m&n zgo3ktidEL~N_e?gBfgZaW>$3=a%#p=fqXsq!U1~vmvr?t+t7C^E*$mJ4V;6n?1TNBkZsPh?R^l8p`Pqsg%|CoO59kLfx z{jlk4YK5u6XZC_N?q9pgZ+b86WLo!`{h-G8*pBk+o7i@ywg11|z0L)X$0Wx90000< KMNUMnLSTZHXKD8U literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..268254c01c09f3b63fd86e80765ea782587976b5 GIT binary patch literal 1524 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 XgzINr$T8)y00000NkvXXu0mjf+RJ25 literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..6dcd55a20615930c6a3d82c3c6386a2bea06f8b9 GIT binary patch literal 976 zcmV;>126oEP)cK&Yvp^f+}?nqI4|dTPwEJKZhz0JO@c~!(?1=xm!3sSFNSeK^0cQ;X036p9om_DyWKLo61p9)BxLile~4YFol<=Qth!IAlpCxA7I z>s)ieo%WJNPo$&Q?qvC-)MVV`REk8e&lWb7(=W#ST_0IlsD;DYkVha?T|_^$S{y!3 z%&_>~^I^>s-~rI{udHn@AbQV+u}JMXnopC7kA{FDQBye$03CHq6s+i6FTZ#jE0`b7 z(8Yn3^&r;i!?w9VvNz|Z&8vQ|5Q2gnqP3Rp9Sdm8TmzHr&=f0lr!94VwG{(8Kj@`N z6y~M+PnfwP{8s;SbgXi!{>+t1s@j@t=1MO$+02!8s^-j<395SL$`DmCb7hVyFRqMI y#m<#!YRuy-P-CxC7O1h#l?AF;T$!f+S$_km85Vy2j794J0000kbnP)0 zTWl0n7(R2`3*FteyDhD>KoubcqlJK0t+ob5g4~tzLhwO>5aj`5NDRgYqDBcm0BTZF zFGQ(4aFGN|B#=<3+)6`AFEkWNftI!w+THD5XLl~To$hw_%$eEQ-7d^7C&0{q&iUs* z-<*HH`Oe{xh1;%S000aN{gEpwS}_37J(7NMLUBhO;f7FO5<(lkME6a-Ini;&XtD4= zp}*urq1BSFEZQ(&&5bx!Xh#8SZp0}>3owjKCMp(B=hXW~&|+Zxib1|)!cjG5N;nyX zHVjx3*_hf)j@ zqlWMFZc-UHjYw-Olk;u$Ai89mTaAVuA3aUYqm-1E#xhx`3+i=4KvE)rW5$(LS{g(P z0N{CwW%UF>L!)L`i^{lY($Xecq22C$DbLy<_V&{SZcnMoxG-sHkfkC}g=_!dN}X=l zVrmyEXnJyY??>V9Q|v%mCM~p<;&^>`&t{|XuF=rTFc!CGwtBQ$-qVVqrF{MppT8u5 zxZ2#)N?bX{G)XKaBi1O|5NTJ+0Jca>N&{w6YEOri$Fz z1^Uhhf!Yg>AupR`)ao)*b(tBCB`Vkd4eU7WND?guz`D7Us-D(6{M@fsz1IgBsV_ol z87b_@hf_-n^Z;kS}6+xMr?JKr56&3%T_T>#ofc&i5wyazeO76D@1PxGI0R`;w^GpfRzGiEH$Phzu%uM~)_>`)Uk)t* zfa|)+wzo4jiWp!~jaeTcE7d2fQV5GkS#GclPc6W8BXW^_Y zp`x|V|H~zZH^|FHifKxxn7z@#wzWk6j4|o&)=BdiK*j}9)8S|KT=0BSVG`;dc-S3h zTvA^IY4HGjaIkO59PuForP#llpS|pa9`{Rlo#t< zH22pH`hk~2C@rKX!-3=N?j$vhoGB53RzJ_R#YKgL~>{; z9Q?61ccx81M0~x73UJ_1n?E*bRhO9sv_c_pZ2KbHm3y$KWTfM054E^^ovgSG&0W%G zL_cbCxye8xPah$yTmv3PJ61N#pLD1_`V))X(=r?2Z~fF9ip1rIEO`+pJH22WaDr$@ zm6pz=L#K(@60Q^hhl%!;C#^HLd|O_R>bd)z{YZ-&@|2qBOjd^$`Q!~p?Fy|>+`KIO z{Kh=JXn|kf9gucL>}fUL^Y1z{ytOiyA~1&Ie{UN3v-L5cjh8PTdijq}ICm|!2x_$9 zZs3~+kJJ~vr?vN-y{>6wM!xR$Q1GFLm4=BEZCtn$K`UHOcLiRp9eQpu<)pcrL-3MT zD6;3YhP>?1J-@iC#?cDJ$AG53pn|XLIW3bOFR@a0?LikUt1D&nP^^6^<&uPk0~ki( zV5W_5`1w8;LRZQvt+n&aUoFWqQaH_U-_<*6E;?0@Db~J}a!F20y-+~btguWmhrX%D zI{crWbPJ0Bac6|mnp3FXxip^~WqCb`fBu%WYbbcC(+hd3%H$NFl~X3!dTjr@+1Bv- zdDU#geHXXuychCCD6OS)vdHLf+82wg8_O+-FfwJwDaZA}pct(;$d7M~p|Fn7d~`*~ z@u$@}CLJz9sr2$l9J5C67OX7QhX6;vDTLDU1*2mF(C1_>_s~iqqhG{GfP?0CU!!|p zqg!EqkY>~Upn*d0fvD&Bt<{JxD2m#v2?3Gh1!8%4q{X%6wVW*7*xTZG{@XM5=DtAU zES2brucpzX-d9Ipl_k28oN-aSzu7PKMUYni2)p_4;Lc@vQ!M0wi}~`@7+RSjK8oFQ zHD-VV?;myUeaAevkfIsB_NuS0m6mo!pVO-E^1ZyL$85k|G{-~J7Z*zVh>&E&{!T<5gX9&KFbv@!v$`g2-o%u z32+nF8OPshS1ZYDOTIAm#hDO}w%ljPaMVnofkILk(li65BqgPxlYx*n9s_@yz`~-DUk4{S zhlIv~icfGb6P0W-r=zkA$}vkvX0k{X z$^gTNp^i8mm1R>NIHNO>MQTyzEFDRYI31M}q|61KAuLjnG6v0~wU0O*l@(DQJfky& zMe0(HTcb6PI31N$po{>Wek@X*ax{^e&}Yqz7dQ>0L^*NE^!VGO4N& zr=zm^lp$qORU_6ZM~z5B7U@S>W*(gqERsYS!vX+cENcX6P==*Oh$ z_uppILjZu!H^T0i*I{2WKwLr0((>q(V4*o>qp@q;sJG3g2aySrsiU~)j5Yg4WAjTi zlcgkPd1cZJrVIcmFL^JPa>S^t#!;n*8k@H&iCIROG_xt2O|98k_3)zrV9l=0vNXB9 zV-t>9YMC^%DTh&_juW<=?Ai$E1Y(v{Ce3)t0ANOA5BxxbW+Zx)#4MiP4F}2qfI!d+ zKh~fHLk$7@`C4{a#V zE+ZF@GZ_t7eOK^oo!1>;g_Xshg8z6~;nszD*x=xIOBaE;aOl!&*0B^0yTRPqxJ2)<%c>i1ZtB=|rIvJF*&{J@m*|76#h4Dux z32e)PyqDgOJ}^!yWsirxdWBfHSilEpLm2=ttBmAP4qI^!{_Q>+OQ!!5>5?wBOy$p0*;EZGH>zKM*tk;8?EiiH9 zim~Dk?YM#K3OS5O%yY`f{olxix?Fz{i1&|}P&a9-BY6B;@3&{!;wS(3A4j{B7SKfw z%FUPKjH&-02n3kg8~F6uP<51Yr<+m!S@Pj+@9oaughGB2;R~|+F1rLoMDpmL{=H*L zv1wWVh*5*Wyjg}}ni>EA^eq%<3Cap$rdja4)7^hsmtSU!u0jEt z-CpUqYo`U2Me=BGfPMB*%a+IUD;}e)Cd2Ix|B))kCwJUXXIT+L2f5P_XZDLm;Pv#M z*TT!Ru(Gn48@uThhr50_k6JR$WW+&z7k%K0>+1c0u(EhJsngHyyzft4>v7y{9Yfs`=WFAMW=M-^ywxI8C_F7@XFc8G}PmdP82=dP{s^N z@6v%XZ^dA4-GGBmk(l}Eodj1S4yKiuZ5G4rc7JnEaL6ckwzD14oU$MK>a~iL6~s&# zvFRm7636Q8!DdHXat%rE#B?{!vAwz^-)0$D4X?F$fA&%5wHBXR5sQmN%;z(1cp>VZ zm(O=d3T4s&UYmy=kW|WyvY%E>=`{p~H z0iLtmh~-fRz))IFZg^4DP3v>{H_NS0Pv+b!Egow+v7UJ9h~4RDQOZK;9f@PRmO(1Q zCIZ_u$F{4oQ(?q{&H{TQikPO9A1T1sOo9}ZM(5(+nUVFoEAIFy0MDaibmxpR=Vih3 zr;5j@5*y-bkVEKTRo}mwybuKqDDQnLH`k1;-XM<1AEk^KhIpTze1wF9hUC%QcP|GW zRyJvND0eV= zUSM(Kb~nxUMyxLK3>L}iBb|8sI0icqcQE$V{BrWm|~S~^N01mU5gp$5_I&sM?hJmn{?Y>9v*wXWI_=c zJACT%zN1%r6-FHIihPDG%QVjMoqqPk(~kWstwk}bP>^ANeaUsPNphJqUiV=iz4~Cw zmIb*L6Q~?>sHpT=?(F#UcB)dDPNzBoTTFexS;I^?nfQ95L~3 z(oj*Ud+&wrWMy&~?{`I}P0YPMcF!f(o=YweZwTcf?7rgVKI_z`k`?c? z^6!y`4doYRT3%no=~Y`Z$((C8eD|-t49jWL zh_$4QGW95zTMe%*gw!4uk0qb~s`+q@7o{vzCY@Pk9+e00l36*KDx@MAJL)8Y565~O z|F5&E#SfA~`6o+qtgt&e30Y5cG}nxgi0-XtCYky{Bi=uuGHDD-zU5l*byIUtv(oMI zJzqa)7-hSM-F~)P+WR%&($p3^53J1Lt<(pvc`n`y2q=r}P1@`Tu0PVYXGP)cQ6>Y1^?2y_ zs-1f;^+a;UVMRGSe!juK{)FS_OS8W+k_fNb`>Q-}U-b$ri|O0Tr_aL5Quiix`B|4==sOzme?Lu@Le}`D^OP#W6%G?h_&P$!Ln#u&kS*NUlGQoG&DQlz5Yd6p-Yo#1pCeXfxrCV);kDawO)hUpV=nko}I<>V+2xBuyW$dvyNWU%fY7Or}O00000NkvXXu0mjf D>445d literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..2ccc0fe3a6306d21a077d6eba7e6555a92fbf1b7 GIT binary patch literal 1333 zcmV-51t!8S?TSVPKgx|=k+$!4>=vokw$bapngXJ%(+CpF3LA)7gK z=FI>6&i8%i%=wtjAO3|90)&7phAr|xqE)HddC`^>g7m$7VN@nWj*(qal+2!LD=U{$ zn9wp6VhWH+ww9?7S^g)Jm6QP!AjDYAY1NoarHq-<)-oMz-F>G@F)I;TC#IAWvlsBH zFj-0EU`jc0z2gboZZA!7QSK>>9fnL`;KVidUCrj6>dHR>fJHYlVaABN%=+NO*y3)l)pXGLW)`Ic(Ti+mC|ew z2%+a2+=2u*yqrxtHC8&F-QpyN20*@Wa%7sf$@Fw3Q_C{f9wZWCW_p^H`0>H&xAu7e z;Cz4RvtNUD48FgqsPnZ70KgbSGKm}x0JuCB9hnZRE=O9(=K;I4^-5r;vy~|jc!0Mgg{`e zp?j3e-VHQPY_4&29;gyTeELf8-YlP+iD;yoTd2mZ5RXAovCLv`fAYAg)BDTnJ#^Pl z?3ce2gcFIu&b4pczDZu~Ums=8{vIL|H&W5Rn7rlZbz9Dcj+0vdrI{K$^tNgT5m=gD zN1kyVXmSC-=WS&JUBL*Sju4F#OPrK)SLUSZJdSJ?4w*wU0yy(q=**$&|AMr$cdqwt408AmY1WsXE8Tv3CV6`%X(n}bivQs- zyQP%+W;hm%t4jei>FedurJwIaF$NLqr)A1yYA>yViPS_;eChj;g9cvIbkn4-2*KW3 zmmuLA-h@tl1$N42vXX+v3c9wOzUCFuAD`@%_-R{p$DT3(_--)#$(2xUCX3M0n#YW( zeTVDZ!BPsLn-7H}7sJ?aCBDA1Sg9W0RoYtTG9C}0Z<0IP7gpeI1%z*hBI+ZhjLizF z=HZ>Lle>$x0-3U@gr-s)Ej5lO%4uKfATuiEG z=}TjYq-2u5${(MBu#wQpnatyftYCodu|&3%o%Hqb&%TeHCFD$B^T3odea#P(b^5v{ zF>Z&+B7M!9od_ou7?WZ8nt!J3^fjOHL`snxL_yd|m%bJdle!WMi%D@}p~Mp@#T1&I rl&kuwE+D3CCl&;gYOR8ZC$jP{+=FV2p1q-800000NkvXXu0mjf>2{Ft literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..f0d7875123a82c63a4abab841053aae8f067c4c1 GIT binary patch literal 2557 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 T`FAie00000NkvXXu0mjf%}4cS literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..4e8e785ec698fe6f2341ff5a6872b1b190cc2560 GIT binary patch literal 1469 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 D>445d literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..dc1d98292c57b0d49fa09b1416b7bf395b49bea0 GIT binary patch literal 5124 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 literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..00983906970f4ab2d3dfbf151cb899f012df656b GIT binary patch literal 1741 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 PUtAWZ*4J^@?!^8NG05E~ literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 0000000..0bedcf2 --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000..89c2725 --- /dev/null +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..f2e259c --- /dev/null +++ b/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/Base.lproj/Main.storyboard b/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f3c2851 --- /dev/null +++ b/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist new file mode 100644 index 0000000..43cfbcc --- /dev/null +++ b/ios/Runner/Info.plist @@ -0,0 +1,49 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Debartis + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + debartis + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + + + diff --git a/ios/Runner/Runner-Bridging-Header.h b/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..308a2a5 --- /dev/null +++ b/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/ios/RunnerTests/RunnerTests.swift b/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000..86a7c3b --- /dev/null +++ b/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Flutter +import UIKit +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/lib/firebase_options.dart b/lib/firebase_options.dart new file mode 100644 index 0000000..6cc2d90 --- /dev/null +++ b/lib/firebase_options.dart @@ -0,0 +1,89 @@ +// File generated by FlutterFire CLI. +// ignore_for_file: lines_longer_than_80_chars, avoid_classes_with_only_static_members +import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; +import 'package:flutter/foundation.dart' + show defaultTargetPlatform, kIsWeb, TargetPlatform; + +/// Default [FirebaseOptions] for use with your Firebase apps. +/// +/// Example: +/// ```dart +/// import 'firebase_options.dart'; +/// // ... +/// await Firebase.initializeApp( +/// options: DefaultFirebaseOptions.currentPlatform, +/// ); +/// ``` +class DefaultFirebaseOptions { + static FirebaseOptions get currentPlatform { + if (kIsWeb) { + return web; + } + switch (defaultTargetPlatform) { + case TargetPlatform.android: + return android; + case TargetPlatform.iOS: + return ios; + case TargetPlatform.macOS: + return macos; + case TargetPlatform.windows: + return windows; + case TargetPlatform.linux: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for linux - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + default: + throw UnsupportedError( + 'DefaultFirebaseOptions are not supported for this platform.', + ); + } + } + + static const FirebaseOptions web = FirebaseOptions( + apiKey: 'AIzaSyBWdmHcBVtmkZ8Eeg5aaLF9nmsiL47MXaI', + appId: '1:844787133360:web:your-web-app-id', + messagingSenderId: '844787133360', + projectId: 'deteksi-kebakaran-berbas-915bd', + authDomain: 'deteksi-kebakaran-berbas-915bd.firebaseapp.com', + storageBucket: 'deteksi-kebakaran-berbas-915bd.firebasestorage.app', + databaseURL: + 'https://deteksi-kebakaran-berbas-915bd-default-rtdb.firebaseio.com', + ); + + static const FirebaseOptions android = FirebaseOptions( + apiKey: 'AIzaSyBWdmHcBVtmkZ8Eeg5aaLF9nmsiL47MXaI', + appId: '1:844787133360:android:31f7628397719b40494475', + messagingSenderId: '844787133360', + projectId: 'deteksi-kebakaran-berbas-915bd', + storageBucket: 'deteksi-kebakaran-berbas-915bd.firebasestorage.app', + databaseURL: + 'https://deteksi-kebakaran-berbas-915bd-default-rtdb.firebaseio.com', + ); + + static const FirebaseOptions ios = FirebaseOptions( + apiKey: 'AIzaSyBWdmHcBVtmkZ8Eeg5aaLF9nmsiL47MXaI', + appId: '1:844787133360:ios:your-ios-app-id', + messagingSenderId: '844787133360', + projectId: 'deteksi-kebakaran-berbas-915bd', + storageBucket: 'deteksi-kebakaran-berbas-915bd.firebasestorage.app', + iosBundleId: 'com.example.debartis', + ); + + static const FirebaseOptions macos = FirebaseOptions( + apiKey: 'AIzaSyBWdmHcBVtmkZ8Eeg5aaLF9nmsiL47MXaI', + appId: '1:844787133360:macos:your-macos-app-id', + messagingSenderId: '844787133360', + projectId: 'deteksi-kebakaran-berbas-915bd', + storageBucket: 'deteksi-kebakaran-berbas-915bd.firebasestorage.app', + iosBundleId: 'com.example.debartis', + ); + + static const FirebaseOptions windows = FirebaseOptions( + apiKey: 'AIzaSyBWdmHcBVtmkZ8Eeg5aaLF9nmsiL47MXaI', + appId: '1:844787133360:windows:your-windows-app-id', + messagingSenderId: '844787133360', + projectId: 'deteksi-kebakaran-berbas-915bd', + storageBucket: 'deteksi-kebakaran-berbas-915bd.firebasestorage.app', + ); +} diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..b0a38d5 --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'firebase_options.dart'; +import 'screens/login_screen.dart'; +import 'utils/app_theme.dart'; +import 'utils/app_icons.dart'; + +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Fire Detection System', + theme: AppTheme.lightTheme, + home: const LoginScreen(), + debugShowCheckedModeBanner: false, + ); + } +} diff --git a/lib/models/fire_detection_model.dart b/lib/models/fire_detection_model.dart new file mode 100644 index 0000000..d3b8621 --- /dev/null +++ b/lib/models/fire_detection_model.dart @@ -0,0 +1,119 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; + +class FireDetectionModel { + final String id; + final String userId; + final DateTime timestamp; + final String location; + final double confidence; + final String imageUrl; + final String status; + final String severity; + final String description; + final bool resolved; + final DateTime createdAt; + + FireDetectionModel({ + required this.id, + required this.userId, + required this.timestamp, + required this.location, + required this.confidence, + required this.imageUrl, + required this.status, + required this.severity, + required this.description, + required this.resolved, + required this.createdAt, + }); + + factory FireDetectionModel.fromFirestore(DocumentSnapshot doc) { + Map data = doc.data() as Map; + return FireDetectionModel( + id: doc.id, + userId: data['userId'] ?? '', + timestamp: data['timestamp']?.toDate() ?? DateTime.now(), + location: data['location'] ?? '', + confidence: (data['confidence'] ?? 0.0).toDouble(), + imageUrl: data['imageUrl'] ?? '', + status: data['status'] ?? 'detected', + severity: data['severity'] ?? 'medium', + description: data['description'] ?? '', + resolved: data['resolved'] ?? false, + createdAt: data['createdAt']?.toDate() ?? DateTime.now(), + ); + } + + Map toFirestore() { + return { + 'userId': userId, + 'timestamp': Timestamp.fromDate(timestamp), + 'location': location, + 'confidence': confidence, + 'imageUrl': imageUrl, + 'status': status, + 'severity': severity, + 'description': description, + 'resolved': resolved, + 'createdAt': Timestamp.fromDate(createdAt), + }; + } + + FireDetectionModel copyWith({ + String? id, + String? userId, + DateTime? timestamp, + String? location, + double? confidence, + String? imageUrl, + String? status, + String? severity, + String? description, + bool? resolved, + DateTime? createdAt, + }) { + return FireDetectionModel( + id: id ?? this.id, + userId: userId ?? this.userId, + timestamp: timestamp ?? this.timestamp, + location: location ?? this.location, + confidence: confidence ?? this.confidence, + imageUrl: imageUrl ?? this.imageUrl, + status: status ?? this.status, + severity: severity ?? this.severity, + description: description ?? this.description, + resolved: resolved ?? this.resolved, + createdAt: createdAt ?? this.createdAt, + ); + } + + String get severityText { + switch (severity) { + case 'low': + return 'Rendah'; + case 'medium': + return 'Sedang'; + case 'high': + return 'Tinggi'; + case 'critical': + return 'Kritis'; + default: + return 'Tidak diketahui'; + } + } + + String get statusText { + switch (status) { + case 'detected': + return 'Terdeteksi'; + case 'confirmed': + return 'Dikonfirmasi'; + case 'false_alarm': + return 'Alarm Palsu'; + case 'resolved': + return 'Diselesaikan'; + default: + return 'Tidak diketahui'; + } + } +} diff --git a/lib/models/sensor_data_model.dart b/lib/models/sensor_data_model.dart new file mode 100644 index 0000000..8d0cc1d --- /dev/null +++ b/lib/models/sensor_data_model.dart @@ -0,0 +1,68 @@ +class SensorData { + final bool apiTerdeteksi; + final bool buzzerActive; + final bool exhaustActive; + final int gasValue; + final int kelembapan; + final bool pumpActive; + final double suhu; + + SensorData({ + required this.apiTerdeteksi, + required this.buzzerActive, + required this.exhaustActive, + required this.gasValue, + required this.kelembapan, + required this.pumpActive, + required this.suhu, + }); + + factory SensorData.fromJson(Map json) { + return SensorData( + apiTerdeteksi: json['api_terdeteksi'] ?? false, + buzzerActive: json['buzzer_active'] ?? false, + exhaustActive: json['exhaust_active'] ?? false, + gasValue: json['gas_value'] ?? 0, + kelembapan: json['kelembapan'] ?? 0, + pumpActive: json['pump_active'] ?? false, + suhu: (json['suhu'] ?? 0).toDouble(), + ); + } + + Map toJson() { + return { + 'api_terdeteksi': apiTerdeteksi, + 'buzzer_active': buzzerActive, + 'exhaust_active': exhaustActive, + 'gas_value': gasValue, + 'kelembapan': kelembapan, + 'pump_active': pumpActive, + 'suhu': suhu, + }; + } + + // Helper methods untuk status + String get statusApi => apiTerdeteksi ? 'Terdeteksi' : 'Aman'; + String get statusSuhu => + suhu > 35 + ? 'Panas' + : suhu > 25 + ? 'Normal' + : 'Dingin'; + String get statusKelembapan => + kelembapan > 70 + ? 'Tinggi' + : kelembapan > 40 + ? 'Normal' + : 'Rendah'; + String get statusGas => + gasValue > 300 + ? 'Bahaya' + : gasValue > 150 + ? 'Waspada' + : 'Aman'; + + // Helper untuk warna status + bool get isFireDetected => apiTerdeteksi || gasValue > 300 || suhu > 40; + bool get isWarning => gasValue > 150 || suhu > 35 || kelembapan > 70; +} diff --git a/lib/models/user_model.dart b/lib/models/user_model.dart new file mode 100644 index 0000000..bf8f7e3 --- /dev/null +++ b/lib/models/user_model.dart @@ -0,0 +1,77 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; + +class UserModel { + final String uid; + final String email; + final String displayName; + final String photoURL; + final String role; + final bool isActive; + final DateTime? createdAt; + final DateTime? updatedAt; + + UserModel({ + required this.uid, + required this.email, + required this.displayName, + required this.photoURL, + required this.role, + required this.isActive, + this.createdAt, + this.updatedAt, + }); + + factory UserModel.fromFirestore(DocumentSnapshot doc) { + Map data = doc.data() as Map; + return UserModel( + uid: doc.id, + email: data['email'] ?? '', + displayName: data['displayName'] ?? '', + photoURL: data['photoURL'] ?? '', + role: data['role'] ?? 'user', + isActive: data['isActive'] ?? true, + createdAt: data['createdAt']?.toDate(), + updatedAt: data['updatedAt']?.toDate(), + ); + } + + Map toFirestore() { + return { + 'email': email, + 'displayName': displayName, + 'photoURL': photoURL, + 'role': role, + 'isActive': isActive, + 'createdAt': + createdAt != null + ? Timestamp.fromDate(createdAt!) + : FieldValue.serverTimestamp(), + 'updatedAt': + updatedAt != null + ? Timestamp.fromDate(updatedAt!) + : FieldValue.serverTimestamp(), + }; + } + + UserModel copyWith({ + String? uid, + String? email, + String? displayName, + String? photoURL, + String? role, + bool? isActive, + DateTime? createdAt, + DateTime? updatedAt, + }) { + return UserModel( + uid: uid ?? this.uid, + email: email ?? this.email, + displayName: displayName ?? this.displayName, + photoURL: photoURL ?? this.photoURL, + role: role ?? this.role, + isActive: isActive ?? this.isActive, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ); + } +} diff --git a/lib/screens/dashboard_screen.dart b/lib/screens/dashboard_screen.dart new file mode 100644 index 0000000..d9ef8fe --- /dev/null +++ b/lib/screens/dashboard_screen.dart @@ -0,0 +1,447 @@ +import 'package:flutter/material.dart'; +import '../models/sensor_data_model.dart'; +import '../services/realtime_database_service.dart'; +import '../services/auth_service.dart'; +import '../utils/app_colors.dart'; +import '../widgets/sensor_card.dart'; +import '../widgets/custom_button.dart'; +import 'login_screen.dart'; + +class DashboardScreen extends StatefulWidget { + const DashboardScreen({Key? key}) : super(key: key); + + @override + State createState() => _DashboardScreenState(); +} + +class _DashboardScreenState extends State { + final RealtimeDatabaseService _dbService = RealtimeDatabaseService(); + final AuthService _authService = AuthService(); + bool _isLoading = false; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Dashboard Deteksi Kebakaran'), + backgroundColor: AppColors.primaryRed, + foregroundColor: AppColors.textLight, + elevation: 0, + actions: [ + IconButton( + icon: const Icon(Icons.refresh), + onPressed: () { + setState(() {}); + }, + ), + PopupMenuButton( + onSelected: (value) { + if (value == 'logout') { + _handleLogout(); + } + }, + itemBuilder: + (BuildContext context) => [ + PopupMenuItem( + value: 'logout', + child: Row( + children: [ + Icon(Icons.logout, color: AppColors.textError), + const SizedBox(width: 8), + Text('Logout'), + ], + ), + ), + ], + ), + ], + ), + body: StreamBuilder( + stream: _dbService.sensorDataStream, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation(AppColors.primaryRed), + ), + ); + } + + if (snapshot.hasError) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.error_outline, + size: 64, + color: AppColors.textError, + ), + const SizedBox(height: 16), + Text( + 'Gagal memuat data sensor', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + color: AppColors.textError, + ), + ), + const SizedBox(height: 8), + Text( + 'Periksa koneksi internet Anda', + style: TextStyle( + fontSize: 14, + color: AppColors.textSecondary, + ), + ), + const SizedBox(height: 16), + ElevatedButton( + onPressed: () { + setState(() {}); + }, + child: const Text('Coba Lagi'), + ), + ], + ), + ); + } + + final sensorData = snapshot.data!; + return RefreshIndicator( + onRefresh: () async { + setState(() {}); + }, + child: SingleChildScrollView( + physics: const AlwaysScrollableScrollPhysics(), + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Status Overview + _buildStatusOverview(sensorData), + + const SizedBox(height: 24), + + // Sensor Data Section + _buildSectionTitle('Data Sensor'), + _buildSensorCards(sensorData), + + const SizedBox(height: 24), + + // Control Section + // _buildSectionTitle('Kontrol Sistem'), + // _buildControlCards(sensorData), + const SizedBox(height: 24), + + // Emergency Actions + // _buildEmergencyActions(), + ], + ), + ), + ); + }, + ), + ); + } + + Widget _buildStatusOverview(SensorData sensorData) { + Color statusColor; + String statusText; + IconData statusIcon; + + if (sensorData.isFireDetected) { + statusColor = AppColors.textError; + statusText = 'BAHAYA - API TERDETEKSI'; + statusIcon = Icons.local_fire_department; + } else if (sensorData.isWarning) { + statusColor = AppColors.primaryOrange; + statusText = 'WASPADA - KONDISI ABNORMAL'; + statusIcon = Icons.warning; + } else { + statusColor = AppColors.accentGreen; + statusText = 'AMAN - KONDISI NORMAL'; + statusIcon = Icons.check_circle; + } + + return Container( + width: double.infinity, + padding: const EdgeInsets.all(20), + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [statusColor, statusColor.withOpacity(0.8)], + ), + borderRadius: BorderRadius.circular(16), + boxShadow: [ + BoxShadow( + color: statusColor.withOpacity(0.3), + blurRadius: 20, + offset: const Offset(0, 10), + ), + ], + ), + child: Column( + children: [ + Icon(statusIcon, size: 48, color: AppColors.textLight), + const SizedBox(height: 12), + Text( + statusText, + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: AppColors.textLight, + ), + textAlign: TextAlign.center, + ), + const SizedBox(height: 8), + Text( + 'Terakhir Update: ${DateTime.now().toString().substring(0, 19)}', + style: TextStyle( + fontSize: 12, + color: AppColors.textLight.withOpacity(0.8), + ), + ), + ], + ), + ); + } + + Widget _buildSectionTitle(String title) { + return Padding( + padding: const EdgeInsets.only(bottom: 16), + child: Text( + title, + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: AppColors.textPrimary, + ), + ), + ); + } + + Widget _buildSensorCards(SensorData sensorData) { + return Column( + children: [ + SensorCard( + title: 'Suhu', + value: sensorData.suhu.toStringAsFixed(1), + unit: '°C', + icon: Icons.thermostat, + statusColor: + sensorData.suhu > 35 + ? AppColors.textError + : sensorData.suhu > 25 + ? AppColors.accentGreen + : AppColors.primaryBlue, + status: sensorData.statusSuhu, + ), + SensorCard( + title: 'Kelembapan', + value: sensorData.kelembapan.toString(), + unit: '%', + icon: Icons.water_drop, + statusColor: + sensorData.kelembapan > 70 + ? AppColors.primaryBlue + : sensorData.kelembapan > 40 + ? AppColors.accentGreen + : AppColors.primaryOrange, + status: sensorData.statusKelembapan, + ), + SensorCard( + title: 'Gas', + value: sensorData.gasValue.toString(), + unit: 'ppm', + icon: Icons.air, + statusColor: + sensorData.gasValue > 300 + ? AppColors.textError + : sensorData.gasValue > 150 + ? AppColors.primaryOrange + : AppColors.accentGreen, + status: sensorData.statusGas, + ), + SensorCard( + title: 'Deteksi Api', + value: sensorData.apiTerdeteksi ? 'TERDETEKSI' : 'AMAN', + unit: '', + icon: Icons.local_fire_department, + statusColor: + sensorData.apiTerdeteksi + ? AppColors.textError + : AppColors.accentGreen, + status: sensorData.statusApi, + ), + ], + ); + } + + Widget _buildControlCards(SensorData sensorData) { + return Column( + children: [ + ControlCard( + title: 'Buzzer', + description: 'Alarm suara peringatan', + icon: Icons.volume_up, + isActive: sensorData.buzzerActive, + activeColor: AppColors.primaryOrange, + onToggle: () => _toggleControl('buzzer', !sensorData.buzzerActive), + ), + ControlCard( + title: 'Exhaust Fan', + description: 'Kipas pembuangan asap', + icon: Icons.air, + isActive: sensorData.exhaustActive, + activeColor: AppColors.primaryBlue, + onToggle: () => _toggleControl('exhaust', !sensorData.exhaustActive), + ), + ControlCard( + title: 'Water Pump', + description: 'Pompa air pemadam', + icon: Icons.water, + isActive: sensorData.pumpActive, + activeColor: AppColors.accentGreen, + onToggle: () => _toggleControl('pump', !sensorData.pumpActive), + ), + ], + ); + } + + Widget _buildEmergencyActions() { + return Column( + children: [ + const SizedBox(height: 16), + // Mode Darurat Button + CustomButton( + text: 'Mode Darurat', + onPressed: _activateEmergencyMode, + backgroundColor: AppColors.textError, + icon: Icons.emergency, + isLoading: _isLoading, + width: double.infinity, + ), + const SizedBox(height: 8), + // Reset Semua Button + CustomButton( + text: 'Reset Semua', + onPressed: _resetAllControls, + backgroundColor: AppColors.secondaryGray, + icon: Icons.refresh, + isLoading: _isLoading, + width: double.infinity, + ), + ], + ); + } + + Future _toggleControl(String controlType, bool value) async { + setState(() { + _isLoading = true; + }); + + try { + switch (controlType) { + case 'buzzer': + await _dbService.updateControl(buzzerActive: value); + break; + case 'exhaust': + await _dbService.updateControl(exhaustActive: value); + break; + case 'pump': + await _dbService.updateControl(pumpActive: value); + break; + } + + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Kontrol $controlType berhasil diupdate'), + backgroundColor: AppColors.accentGreen, + ), + ); + } catch (e) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(e.toString()), + backgroundColor: AppColors.textError, + ), + ); + } finally { + setState(() { + _isLoading = false; + }); + } + } + + Future _activateEmergencyMode() async { + setState(() { + _isLoading = true; + }); + + try { + await _dbService.activateEmergencyMode(); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Mode darurat diaktifkan!'), + backgroundColor: AppColors.textError, + ), + ); + } catch (e) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(e.toString()), + backgroundColor: AppColors.textError, + ), + ); + } finally { + setState(() { + _isLoading = false; + }); + } + } + + Future _resetAllControls() async { + setState(() { + _isLoading = true; + }); + + try { + await _dbService.resetAllControls(); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Semua kontrol berhasil direset'), + backgroundColor: AppColors.accentGreen, + ), + ); + } catch (e) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(e.toString()), + backgroundColor: AppColors.textError, + ), + ); + } finally { + setState(() { + _isLoading = false; + }); + } + } + + Future _handleLogout() async { + try { + await _authService.signOut(); + Navigator.pushReplacement( + context, + MaterialPageRoute(builder: (context) => const LoginScreen()), + ); + } catch (e) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(e.toString()), + backgroundColor: AppColors.textError, + ), + ); + } + } +} diff --git a/lib/screens/login_screen.dart b/lib/screens/login_screen.dart new file mode 100644 index 0000000..7b97886 --- /dev/null +++ b/lib/screens/login_screen.dart @@ -0,0 +1,411 @@ +import 'package:flutter/material.dart'; +import '../utils/app_colors.dart'; +import '../widgets/custom_text_field.dart'; +import '../widgets/custom_button.dart'; +import '../services/auth_service.dart'; +import '../services/firestore_service.dart'; +import 'dashboard_screen.dart'; +import 'package:firebase_auth/firebase_auth.dart'; + +class LoginScreen extends StatefulWidget { + const LoginScreen({Key? key}) : super(key: key); + + @override + State createState() => _LoginScreenState(); +} + +class _LoginScreenState extends State + with TickerProviderStateMixin { + final _formKey = GlobalKey(); + final _emailController = TextEditingController(); + final _passwordController = TextEditingController(); + final _authService = AuthService(); + final _firestoreService = FirestoreService(); + bool _isLoading = false; + late AnimationController _animationController; + late AnimationController _pulseAnimationController; + late Animation _fadeAnimation; + late Animation _slideAnimation; + late Animation _scaleAnimation; + late Animation _pulseAnimation; + + @override + void initState() { + super.initState(); + + // Initialize animation controllers + _animationController = AnimationController( + duration: const Duration(milliseconds: 1500), + vsync: this, + ); + + _pulseAnimationController = AnimationController( + duration: const Duration(milliseconds: 2000), + vsync: this, + ); + + // Initialize animations + _fadeAnimation = Tween(begin: 0.0, end: 1.0).animate( + CurvedAnimation(parent: _animationController, curve: Curves.easeInOut), + ); + + _slideAnimation = Tween( + begin: const Offset(0, 0.5), + end: Offset.zero, + ).animate( + CurvedAnimation(parent: _animationController, curve: Curves.elasticOut), + ); + + _scaleAnimation = Tween(begin: 0.8, end: 1.0).animate( + CurvedAnimation(parent: _animationController, curve: Curves.elasticOut), + ); + + _pulseAnimation = Tween(begin: 1.0, end: 1.1).animate( + CurvedAnimation( + parent: _pulseAnimationController, + curve: Curves.easeInOut, + ), + ); + + // Start animations + _animationController.forward(); + _pulseAnimationController.repeat(reverse: true); + } + + @override + void dispose() { + _animationController.dispose(); + _pulseAnimationController.dispose(); + _emailController.dispose(); + _passwordController.dispose(); + super.dispose(); + } + + String? _validateEmail(String? value) { + if (value == null || value.isEmpty) { + return 'Email tidak boleh kosong'; + } + if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(value)) { + return 'Format email tidak valid'; + } + return null; + } + + String? _validatePassword(String? value) { + if (value == null || value.isEmpty) { + return 'Password tidak boleh kosong'; + } + if (value.length < 6) { + return 'Password minimal 6 karakter'; + } + return null; + } + + void _handleLogin() async { + if (_formKey.currentState!.validate()) { + setState(() { + _isLoading = true; + }); + + try { + // Login dengan Firebase + UserCredential? result = await _authService.signInWithEmailAndPassword( + email: _emailController.text.trim(), + password: _passwordController.text.trim(), + ); + + if (result?.user != null) { + // Cek apakah user profile sudah ada di Firestore + var userProfile = await _firestoreService.getUserProfile( + result!.user!.uid, + ); + + if (userProfile == null) { + // Buat profile baru jika belum ada + await _firestoreService.createUserProfile( + uid: result.user!.uid, + email: result.user!.email!, + displayName: result.user!.displayName, + photoURL: result.user!.photoURL, + ); + } + + // Tampilkan pesan sukses + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Login berhasil!'), + backgroundColor: AppColors.accentGreen, + ), + ); + + // TODO: Navigate to home screen + Navigator.pushReplacement( + context, + MaterialPageRoute(builder: (context) => const DashboardScreen()), + ); + } + } catch (e) { + // Tampilkan error message + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(e.toString()), + backgroundColor: AppColors.textError, + ), + ); + } finally { + setState(() { + _isLoading = false; + }); + } + } + } + + @override + Widget build(BuildContext context) { + final screenHeight = MediaQuery.of(context).size.height; + final screenWidth = MediaQuery.of(context).size.width; + + return Scaffold( + body: Container( + width: screenWidth, + height: screenHeight, + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [ + AppColors.backgroundLight, + AppColors.backgroundWhite, + Color(0xFFFFF8F0), + ], + ), + ), + child: SafeArea( + child: FadeTransition( + opacity: _fadeAnimation, + child: SlideTransition( + position: _slideAnimation, + child: ScaleTransition( + scale: _scaleAnimation, + child: SingleChildScrollView( + padding: EdgeInsets.symmetric( + horizontal: screenWidth * 0.08, + vertical: 16, + ), + child: Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox(height: screenHeight * 0.05), + + // Logo and App Title + _buildHeader(), + + SizedBox(height: screenHeight * 0.06), + + // Login Form + _buildLoginForm(), + + SizedBox(height: screenHeight * 0.04), + + // Login Button + CustomButton( + text: 'Masuk', + onPressed: _handleLogin, + isLoading: _isLoading, + width: double.infinity, + height: 54, + icon: Icons.login, + ), + + SizedBox(height: screenHeight * 0.04), + + // Footer + _buildFooter(), + ], + ), + ), + ), + ), + ), + ), + ), + ), + ); + } + + Widget _buildHeader() { + return Column( + children: [ + // Fire Detection Logo with animated pulse + AnimatedBuilder( + animation: _pulseAnimation, + builder: (context, child) { + return Transform.scale( + scale: _pulseAnimation.value, + child: Container( + width: 110, + height: 110, + decoration: BoxDecoration( + shape: BoxShape.circle, + gradient: AppColors.primaryGradient, + boxShadow: [ + BoxShadow( + color: AppColors.primaryRed.withOpacity(0.4), + blurRadius: 25, + offset: const Offset(0, 10), + ), + BoxShadow( + color: AppColors.primaryOrange.withOpacity(0.3), + blurRadius: 40, + offset: const Offset(0, 15), + ), + ], + ), + child: const Icon( + Icons.local_fire_department, + size: 55, + color: AppColors.textLight, + ), + ), + ); + }, + ), + + const SizedBox(height: 20), + + // App Title + const Text( + 'Fire Detection', + style: TextStyle( + fontSize: 28, + fontWeight: FontWeight.bold, + color: AppColors.textPrimary, + letterSpacing: 1.2, + ), + ), + + const SizedBox(height: 8), + + // Subtitle + const Text( + 'Sistem Deteksi Kebakaran Otomatis', + style: TextStyle( + fontSize: 14, + color: AppColors.textSecondary, + fontWeight: FontWeight.w400, + ), + ), + ], + ); + } + + Widget _buildLoginForm() { + return Container( + padding: const EdgeInsets.all(20), + decoration: BoxDecoration( + color: AppColors.backgroundWhite, + borderRadius: BorderRadius.circular(20), + boxShadow: [ + BoxShadow( + color: AppColors.secondaryGray.withOpacity(0.08), + blurRadius: 25, + offset: const Offset(0, 10), + ), + BoxShadow( + color: AppColors.primaryRed.withOpacity(0.05), + blurRadius: 30, + offset: const Offset(0, 20), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Masuk ke Akun Anda', + style: TextStyle( + fontSize: 22, + fontWeight: FontWeight.w600, + color: AppColors.textPrimary, + ), + ), + + const SizedBox(height: 6), + + const Text( + 'Silakan masukkan email dan password Anda', + style: TextStyle(fontSize: 13, color: AppColors.textSecondary), + ), + + const SizedBox(height: 24), + + // Email Field + CustomTextField( + hintText: 'Masukkan email Anda', + labelText: 'Email', + prefixIcon: Icons.email_outlined, + controller: _emailController, + validator: _validateEmail, + keyboardType: TextInputType.emailAddress, + ), + + // Password Field + CustomTextField( + hintText: 'Masukkan password Anda', + labelText: 'Password', + prefixIcon: Icons.lock_outline, + controller: _passwordController, + validator: _validatePassword, + obscureText: true, + ), + ], + ), + ); + } + + Widget _buildFooter() { + return Column( + children: [ + // Row( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // const Text( + // 'Belum punya akun? ', + // style: TextStyle(color: AppColors.textSecondary, fontSize: 14), + // ), + // TextButton( + // onPressed: () { + // // Navigate to register page + // }, + // child: const Text( + // 'Daftar Sekarang', + // style: TextStyle( + // color: AppColors.primaryRed, + // fontSize: 14, + // fontWeight: FontWeight.w600, + // ), + // ), + // ), + // ], + // ), + const SizedBox(height: 16), + + // Version Info + const Text( + 'Version 1.0.0', + style: TextStyle(color: AppColors.textSecondary, fontSize: 12), + ), + + const SizedBox(height: 4), + + // Copyright + const Text( + '© 2025 Fire Detection System', + style: TextStyle(color: AppColors.textSecondary, fontSize: 12), + ), + ], + ); + } +} diff --git a/lib/screens/register_screen.dart b/lib/screens/register_screen.dart new file mode 100644 index 0000000..e69de29 diff --git a/lib/services/auth_service.dart b/lib/services/auth_service.dart new file mode 100644 index 0000000..a3a4d94 --- /dev/null +++ b/lib/services/auth_service.dart @@ -0,0 +1,135 @@ +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:flutter/foundation.dart'; + +class AuthService { + final FirebaseAuth _auth = FirebaseAuth.instance; + + // Get current user + User? get currentUser => _auth.currentUser; + + // Auth state changes stream + Stream get authStateChanges => _auth.authStateChanges(); + + // Sign in with email and password + Future signInWithEmailAndPassword({ + required String email, + required String password, + }) async { + try { + UserCredential result = await _auth.signInWithEmailAndPassword( + email: email, + password: password, + ); + return result; + } on FirebaseAuthException catch (e) { + if (kDebugMode) { + print('Firebase Auth Error: ${e.message}'); + } + throw _handleAuthException(e); + } catch (e) { + if (kDebugMode) { + print('General Auth Error: $e'); + } + throw 'Terjadi kesalahan saat login'; + } + } + + // Register with email and password + Future registerWithEmailAndPassword({ + required String email, + required String password, + }) async { + try { + UserCredential result = await _auth.createUserWithEmailAndPassword( + email: email, + password: password, + ); + return result; + } on FirebaseAuthException catch (e) { + if (kDebugMode) { + print('Firebase Auth Error: ${e.message}'); + } + throw _handleAuthException(e); + } catch (e) { + if (kDebugMode) { + print('General Auth Error: $e'); + } + throw 'Terjadi kesalahan saat mendaftar'; + } + } + + // Sign out + Future signOut() async { + try { + await _auth.signOut(); + } catch (e) { + if (kDebugMode) { + print('Sign out error: $e'); + } + throw 'Terjadi kesalahan saat logout'; + } + } + + // Reset password + Future sendPasswordResetEmail({required String email}) async { + try { + await _auth.sendPasswordResetEmail(email: email); + } on FirebaseAuthException catch (e) { + if (kDebugMode) { + print('Password reset error: ${e.message}'); + } + throw _handleAuthException(e); + } catch (e) { + if (kDebugMode) { + print('General password reset error: $e'); + } + throw 'Terjadi kesalahan saat mengirim email reset password'; + } + } + + // Update user profile + Future updateUserProfile({ + String? displayName, + String? photoURL, + }) async { + try { + await currentUser?.updateDisplayName(displayName); + if (photoURL != null) { + await currentUser?.updatePhotoURL(photoURL); + } + } catch (e) { + if (kDebugMode) { + print('Update profile error: $e'); + } + throw 'Terjadi kesalahan saat update profil'; + } + } + + // Handle Firebase Auth exceptions + String _handleAuthException(FirebaseAuthException e) { + switch (e.code) { + case 'weak-password': + return 'Password terlalu lemah'; + case 'email-already-in-use': + return 'Email sudah digunakan'; + case 'invalid-email': + return 'Email tidak valid'; + case 'user-not-found': + return 'User tidak ditemukan'; + case 'wrong-password': + return 'Password salah'; + case 'user-disabled': + return 'Akun telah dinonaktifkan'; + case 'too-many-requests': + return 'Terlalu banyak percobaan login. Coba lagi nanti'; + case 'operation-not-allowed': + return 'Operasi tidak diizinkan'; + case 'invalid-credential': + return 'Kredensial tidak valid'; + case 'network-request-failed': + return 'Koneksi internet bermasalah'; + default: + return e.message ?? 'Terjadi kesalahan yang tidak diketahui'; + } + } +} diff --git a/lib/services/firestore_service.dart b/lib/services/firestore_service.dart new file mode 100644 index 0000000..eafe937 --- /dev/null +++ b/lib/services/firestore_service.dart @@ -0,0 +1,209 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:flutter/foundation.dart'; + +class FirestoreService { + final FirebaseFirestore _firestore = FirebaseFirestore.instance; + + // Collections + static const String usersCollection = 'users'; + static const String detectionsCollection = 'detections'; + static const String alertsCollection = 'alerts'; + static const String devicesCollection = 'devices'; + + // User operations + Future createUserProfile({ + required String uid, + required String email, + String? displayName, + String? photoURL, + }) async { + try { + await _firestore.collection(usersCollection).doc(uid).set({ + 'email': email, + 'displayName': displayName ?? '', + 'photoURL': photoURL ?? '', + 'createdAt': FieldValue.serverTimestamp(), + 'updatedAt': FieldValue.serverTimestamp(), + 'role': 'user', + 'isActive': true, + }); + } catch (e) { + if (kDebugMode) { + print('Create user profile error: $e'); + } + throw 'Gagal membuat profil pengguna'; + } + } + + Future getUserProfile(String uid) async { + try { + DocumentSnapshot doc = + await _firestore.collection(usersCollection).doc(uid).get(); + return doc.exists ? doc : null; + } catch (e) { + if (kDebugMode) { + print('Get user profile error: $e'); + } + throw 'Gagal mengambil profil pengguna'; + } + } + + Future updateUserProfile({ + required String uid, + Map? data, + }) async { + try { + if (data != null) { + data['updatedAt'] = FieldValue.serverTimestamp(); + await _firestore.collection(usersCollection).doc(uid).update(data); + } + } catch (e) { + if (kDebugMode) { + print('Update user profile error: $e'); + } + throw 'Gagal mengupdate profil pengguna'; + } + } + + // Fire detection operations + Future saveDetectionData({ + required String uid, + required Map detectionData, + }) async { + try { + await _firestore.collection(detectionsCollection).add({ + 'userId': uid, + 'timestamp': FieldValue.serverTimestamp(), + 'location': detectionData['location'] ?? '', + 'confidence': detectionData['confidence'] ?? 0.0, + 'imageUrl': detectionData['imageUrl'] ?? '', + 'status': detectionData['status'] ?? 'detected', + 'severity': detectionData['severity'] ?? 'medium', + 'description': detectionData['description'] ?? '', + 'resolved': false, + 'createdAt': FieldValue.serverTimestamp(), + }); + } catch (e) { + if (kDebugMode) { + print('Save detection error: $e'); + } + throw 'Gagal menyimpan data deteksi'; + } + } + + Stream getDetectionHistory(String uid) { + return _firestore + .collection(detectionsCollection) + .where('userId', isEqualTo: uid) + .orderBy('timestamp', descending: true) + .snapshots(); + } + + // Alert operations + Future createAlert({ + required String uid, + required String detectionId, + required Map alertData, + }) async { + try { + await _firestore.collection(alertsCollection).add({ + 'userId': uid, + 'detectionId': detectionId, + 'title': alertData['title'] ?? 'Fire Detected', + 'message': alertData['message'] ?? 'Kebakaran terdeteksi', + 'severity': alertData['severity'] ?? 'high', + 'location': alertData['location'] ?? '', + 'timestamp': FieldValue.serverTimestamp(), + 'isRead': false, + 'isResolved': false, + 'createdAt': FieldValue.serverTimestamp(), + }); + } catch (e) { + if (kDebugMode) { + print('Create alert error: $e'); + } + throw 'Gagal membuat alert'; + } + } + + Stream getAlerts(String uid) { + return _firestore + .collection(alertsCollection) + .where('userId', isEqualTo: uid) + .orderBy('timestamp', descending: true) + .snapshots(); + } + + Future markAlertAsRead(String alertId) async { + try { + await _firestore.collection(alertsCollection).doc(alertId).update({ + 'isRead': true, + }); + } catch (e) { + if (kDebugMode) { + print('Mark alert as read error: $e'); + } + throw 'Gagal menandai alert sebagai dibaca'; + } + } + + // Device operations + Future registerDevice({ + required String uid, + required Map deviceData, + }) async { + try { + await _firestore.collection(devicesCollection).add({ + 'userId': uid, + 'deviceName': deviceData['deviceName'] ?? '', + 'deviceType': deviceData['deviceType'] ?? 'camera', + 'location': deviceData['location'] ?? '', + 'status': 'active', + 'lastSeen': FieldValue.serverTimestamp(), + 'createdAt': FieldValue.serverTimestamp(), + }); + } catch (e) { + if (kDebugMode) { + print('Register device error: $e'); + } + throw 'Gagal mendaftarkan perangkat'; + } + } + + Stream getUserDevices(String uid) { + return _firestore + .collection(devicesCollection) + .where('userId', isEqualTo: uid) + .snapshots(); + } + + // Generic operations + Future deleteDocument(String collection, String docId) async { + try { + await _firestore.collection(collection).doc(docId).delete(); + } catch (e) { + if (kDebugMode) { + print('Delete document error: $e'); + } + throw 'Gagal menghapus dokumen'; + } + } + + Future> getDocuments({ + required String collection, + Query? query, + }) async { + try { + QuerySnapshot snapshot = + query != null + ? await query.get() + : await _firestore.collection(collection).get(); + return snapshot.docs; + } catch (e) { + if (kDebugMode) { + print('Get documents error: $e'); + } + throw 'Gagal mengambil dokumen'; + } + } +} diff --git a/lib/services/realtime_database_service.dart b/lib/services/realtime_database_service.dart new file mode 100644 index 0000000..dab9a5c --- /dev/null +++ b/lib/services/realtime_database_service.dart @@ -0,0 +1,116 @@ +import 'package:firebase_database/firebase_database.dart'; +import 'package:flutter/foundation.dart'; +import '../models/sensor_data_model.dart'; + +class RealtimeDatabaseService { + final FirebaseDatabase _database = FirebaseDatabase.instance; + + // Get database reference + DatabaseReference get _sensorRef => _database.ref().child('sensor_data'); + + // Stream untuk mendapatkan data sensor secara real-time + Stream get sensorDataStream { + return _sensorRef.onValue.map((event) { + if (event.snapshot.value != null) { + final data = Map.from(event.snapshot.value as Map); + return SensorData.fromJson(data); + } + return SensorData( + apiTerdeteksi: false, + buzzerActive: false, + exhaustActive: false, + gasValue: 0, + kelembapan: 0, + pumpActive: false, + suhu: 0.0, + ); + }); + } + + // Get data sensor satu kali + Future getSensorData() async { + try { + final snapshot = await _sensorRef.get(); + if (snapshot.value != null) { + final data = Map.from(snapshot.value as Map); + return SensorData.fromJson(data); + } + return SensorData( + apiTerdeteksi: false, + buzzerActive: false, + exhaustActive: false, + gasValue: 0, + kelembapan: 0, + pumpActive: false, + suhu: 0.0, + ); + } catch (e) { + if (kDebugMode) { + print('Error getting sensor data: $e'); + } + throw 'Gagal mengambil data sensor'; + } + } + + // Update kontrol sistem (buzzer, exhaust, pump) + Future updateControl({ + bool? buzzerActive, + bool? exhaustActive, + bool? pumpActive, + }) async { + try { + Map updates = {}; + + if (buzzerActive != null) { + updates['buzzer_active'] = buzzerActive; + } + if (exhaustActive != null) { + updates['exhaust_active'] = exhaustActive; + } + if (pumpActive != null) { + updates['pump_active'] = pumpActive; + } + + if (updates.isNotEmpty) { + await _sensorRef.update(updates); + } + } catch (e) { + if (kDebugMode) { + print('Error updating control: $e'); + } + throw 'Gagal mengupdate kontrol sistem'; + } + } + + // Reset all controls + Future resetAllControls() async { + try { + await _sensorRef.update({ + 'buzzer_active': false, + 'exhaust_active': false, + 'pump_active': false, + }); + } catch (e) { + if (kDebugMode) { + print('Error resetting controls: $e'); + } + throw 'Gagal mereset kontrol sistem'; + } + } + + // Activate emergency mode + Future activateEmergencyMode() async { + try { + await _sensorRef.update({ + 'buzzer_active': true, + 'exhaust_active': true, + 'pump_active': true, + }); + } catch (e) { + if (kDebugMode) { + print('Error activating emergency mode: $e'); + } + throw 'Gagal mengaktifkan mode darurat'; + } + } +} diff --git a/lib/utils/app_colors.dart b/lib/utils/app_colors.dart new file mode 100644 index 0000000..266c4af --- /dev/null +++ b/lib/utils/app_colors.dart @@ -0,0 +1,49 @@ +import 'package:flutter/material.dart'; + +class AppColors { + // Primary colors - Fire theme dengan orange-red gradasi + static const Color primaryRed = Color(0xFFFF4444); + static const Color primaryOrange = Color(0xFFFF6B35); + static const Color primaryDeepOrange = Color(0xFFE63946); + + // Secondary colors - Smoke and safety theme + static const Color secondaryGray = Color(0xFF6C757D); + static const Color secondaryLightGray = Color(0xFFADB5BD); + static const Color secondaryDarkGray = Color(0xFF495057); + + // Background colors + static const Color backgroundLight = Color(0xFFF8F9FA); + static const Color backgroundWhite = Color(0xFFFFFFFF); + static const Color backgroundDark = Color(0xFF212529); + + // Accent colors + static const Color accentBlue = Color(0xFF0077BE); + static const Color primaryBlue = Color(0xFF007BFF); + static const Color accentGreen = Color(0xFF28A745); + static const Color accentYellow = Color(0xFFFFD700); + + // Text colors + static const Color textPrimary = Color(0xFF212529); + static const Color textSecondary = Color(0xFF6C757D); + static const Color textLight = Color(0xFFFFFFFF); + static const Color textError = Color(0xFFDC3545); + + // Gradients + static const LinearGradient primaryGradient = LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [primaryOrange, primaryRed], + ); + + static const LinearGradient backgroundGradient = LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [backgroundLight, backgroundWhite], + ); + + static const LinearGradient smokeGradient = LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [secondaryLightGray, secondaryGray], + ); +} diff --git a/lib/utils/app_icons.dart b/lib/utils/app_icons.dart new file mode 100644 index 0000000..1eef81e --- /dev/null +++ b/lib/utils/app_icons.dart @@ -0,0 +1,171 @@ +import 'package:flutter/material.dart'; + +class AppIcons { + // Icon API sederhana menggunakan Flutter Icons + static const IconData apiIcon = Icons.api; + static const IconData fireIcon = Icons.local_fire_department; + static const IconData sensorIcon = Icons.sensors; + static const IconData dashboardIcon = Icons.dashboard; + static const IconData settingsIcon = Icons.settings; + static const IconData networkIcon = Icons.wifi; + static const IconData databaseIcon = Icons.storage; + + // Custom API Icon Widget + static Widget buildApiIcon({ + double size = 24, + Color? color, + bool showBadge = false, + }) { + return Stack( + alignment: Alignment.center, + children: [ + Icon(Icons.api, size: size, color: color ?? Colors.blue), + if (showBadge) + Positioned( + top: 0, + right: 0, + child: Container( + width: size * 0.3, + height: size * 0.3, + decoration: const BoxDecoration( + color: Colors.green, + shape: BoxShape.circle, + ), + ), + ), + ], + ); + } + + // Custom Fire Detection Icon + static Widget buildFireDetectionIcon({ + double size = 24, + Color? color, + bool isActive = false, + }) { + return Stack( + alignment: Alignment.center, + children: [ + Icon( + Icons.local_fire_department, + size: size, + color: isActive ? Colors.red : (color ?? Colors.grey), + ), + if (isActive) + Positioned( + top: 0, + right: 0, + child: Container( + width: size * 0.25, + height: size * 0.25, + decoration: const BoxDecoration( + color: Colors.red, + shape: BoxShape.circle, + ), + child: const Icon(Icons.warning, color: Colors.white, size: 8), + ), + ), + ], + ); + } + + // Custom Sensor Icon + static Widget buildSensorIcon({ + double size = 24, + Color? color, + bool isOnline = true, + }) { + return Stack( + alignment: Alignment.center, + children: [ + Icon(Icons.sensors, size: size, color: color ?? Colors.blue), + Positioned( + bottom: 0, + right: 0, + child: Container( + width: size * 0.25, + height: size * 0.25, + decoration: BoxDecoration( + color: isOnline ? Colors.green : Colors.red, + shape: BoxShape.circle, + ), + ), + ), + ], + ); + } + + // Logo/Brand Icon untuk App + static Widget buildAppLogo({ + double size = 48, + Color? primaryColor, + Color? secondaryColor, + }) { + return Container( + width: size, + height: size, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + primaryColor ?? Colors.blue, + secondaryColor ?? Colors.blueAccent, + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + shape: BoxShape.circle, + boxShadow: [ + BoxShadow( + color: (primaryColor ?? Colors.blue).withOpacity(0.3), + blurRadius: 8, + offset: const Offset(0, 4), + ), + ], + ), + child: Icon(Icons.api, size: size * 0.6, color: Colors.white), + ); + } + + // Icon untuk berbagai status + static IconData getStatusIcon(String status) { + switch (status.toLowerCase()) { + case 'online': + case 'active': + return Icons.check_circle; + case 'offline': + case 'inactive': + return Icons.offline_bolt; + case 'warning': + return Icons.warning; + case 'error': + case 'danger': + return Icons.error; + case 'loading': + return Icons.refresh; + default: + return Icons.help; + } + } + + // Color untuk berbagai status + static Color getStatusColor(String status) { + switch (status.toLowerCase()) { + case 'online': + case 'active': + case 'normal': + return Colors.green; + case 'offline': + case 'inactive': + return Colors.grey; + case 'warning': + return Colors.orange; + case 'error': + case 'danger': + return Colors.red; + case 'loading': + return Colors.blue; + default: + return Colors.grey; + } + } +} diff --git a/lib/utils/app_theme.dart b/lib/utils/app_theme.dart new file mode 100644 index 0000000..12123f0 --- /dev/null +++ b/lib/utils/app_theme.dart @@ -0,0 +1,101 @@ +import 'package:flutter/material.dart'; +import 'app_colors.dart'; + +class AppTheme { + static ThemeData get lightTheme { + return ThemeData( + primarySwatch: Colors.deepOrange, + primaryColor: AppColors.primaryRed, + scaffoldBackgroundColor: AppColors.backgroundLight, + fontFamily: 'Roboto', + + // AppBar theme + appBarTheme: const AppBarTheme( + backgroundColor: AppColors.primaryRed, + foregroundColor: AppColors.textLight, + elevation: 0, + centerTitle: true, + titleTextStyle: TextStyle( + fontSize: 20, + fontWeight: FontWeight.w600, + color: AppColors.textLight, + ), + ), + + // Elevated button theme + elevatedButtonTheme: ElevatedButtonThemeData( + style: ElevatedButton.styleFrom( + backgroundColor: AppColors.primaryRed, + foregroundColor: AppColors.textLight, + elevation: 2, + padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 32), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + textStyle: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600), + ), + ), + + // Input decoration theme + inputDecorationTheme: InputDecorationTheme( + filled: true, + fillColor: AppColors.backgroundWhite, + contentPadding: const EdgeInsets.symmetric( + vertical: 16, + horizontal: 20, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: AppColors.secondaryLightGray), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: AppColors.secondaryLightGray), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: AppColors.primaryRed, width: 2), + ), + errorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: AppColors.textError), + ), + hintStyle: TextStyle(color: AppColors.secondaryGray, fontSize: 16), + labelStyle: TextStyle(color: AppColors.textSecondary, fontSize: 16), + ), + + // Text theme + textTheme: const TextTheme( + headlineLarge: TextStyle( + fontSize: 32, + fontWeight: FontWeight.bold, + color: AppColors.textPrimary, + ), + headlineMedium: TextStyle( + fontSize: 28, + fontWeight: FontWeight.w600, + color: AppColors.textPrimary, + ), + headlineSmall: TextStyle( + fontSize: 24, + fontWeight: FontWeight.w600, + color: AppColors.textPrimary, + ), + bodyLarge: TextStyle(fontSize: 16, color: AppColors.textPrimary), + bodyMedium: TextStyle(fontSize: 14, color: AppColors.textSecondary), + bodySmall: TextStyle(fontSize: 12, color: AppColors.textSecondary), + ), + + // Card theme + cardTheme: CardTheme( + color: AppColors.backgroundWhite, + elevation: 4, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), + shadowColor: AppColors.secondaryGray.withOpacity(0.2), + ), + + // Icon theme + iconTheme: const IconThemeData(color: AppColors.primaryRed, size: 24), + ); + } +} diff --git a/lib/utils/icon_generator.dart b/lib/utils/icon_generator.dart new file mode 100644 index 0000000..04dfc96 --- /dev/null +++ b/lib/utils/icon_generator.dart @@ -0,0 +1,167 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'dart:ui' as ui; +import 'dart:io'; + +class IconGenerator { + static Future generateAppIcons() async { + // Sizes untuk berbagai platform + final sizes = [ + 16, 32, 48, 64, 128, 256, 512, 1024, // Berbagai ukuran + ]; + + for (final size in sizes) { + await _generateIcon(size); + } + } + + static Future _generateIcon(int size) async { + // Create a recorder to record drawing commands + final recorder = ui.PictureRecorder(); + final canvas = Canvas(recorder); + + // Background + final backgroundPaint = + Paint() + ..color = Colors.blue + ..style = PaintingStyle.fill; + + canvas.drawRRect( + RRect.fromLTRBR( + 0, + 0, + size.toDouble(), + size.toDouble(), + Radius.circular(size * 0.1), + ), + backgroundPaint, + ); + + // API Icon shape + final iconPaint = + Paint() + ..color = Colors.white + ..style = PaintingStyle.fill; + + final iconSize = size * 0.6; + final iconOffset = size * 0.2; + + // Draw simple API icon (circles and lines) + // Center circle + canvas.drawCircle( + Offset(size * 0.5, size * 0.5), + iconSize * 0.15, + iconPaint, + ); + + // Side circles + canvas.drawCircle( + Offset(iconOffset + iconSize * 0.2, size * 0.5), + iconSize * 0.1, + iconPaint, + ); + + canvas.drawCircle( + Offset(iconOffset + iconSize * 0.8, size * 0.5), + iconSize * 0.1, + iconPaint, + ); + + // Connecting lines + final linePaint = + Paint() + ..color = Colors.white + ..strokeWidth = iconSize * 0.05 + ..style = PaintingStyle.stroke; + + canvas.drawLine( + Offset(iconOffset + iconSize * 0.3, size * 0.5), + Offset(iconOffset + iconSize * 0.35, size * 0.5), + linePaint, + ); + + canvas.drawLine( + Offset(iconOffset + iconSize * 0.65, size * 0.5), + Offset(iconOffset + iconSize * 0.7, size * 0.5), + linePaint, + ); + + // End recording + final picture = recorder.endRecording(); + final image = await picture.toImage(size, size); + final byteData = await image.toByteData(format: ui.ImageByteFormat.png); + + if (byteData != null) { + final file = File('assets/icons/app_icon_${size}x$size.png'); + await file.writeAsBytes(byteData.buffer.asUint8List()); + print('Generated icon: ${file.path}'); + } + } + + // Simple icon untuk development + static Widget buildSimpleApiIcon({double size = 24, Color? color}) { + return Container( + width: size, + height: size, + decoration: BoxDecoration( + color: color ?? Colors.blue, + borderRadius: BorderRadius.circular(size * 0.1), + ), + child: Stack( + alignment: Alignment.center, + children: [ + // Center dot + Container( + width: size * 0.15, + height: size * 0.15, + decoration: const BoxDecoration( + color: Colors.white, + shape: BoxShape.circle, + ), + ), + // Left dot + Positioned( + left: size * 0.2, + child: Container( + width: size * 0.1, + height: size * 0.1, + decoration: const BoxDecoration( + color: Colors.white, + shape: BoxShape.circle, + ), + ), + ), + // Right dot + Positioned( + right: size * 0.2, + child: Container( + width: size * 0.1, + height: size * 0.1, + decoration: const BoxDecoration( + color: Colors.white, + shape: BoxShape.circle, + ), + ), + ), + // Connecting lines + Positioned( + left: size * 0.3, + child: Container( + width: size * 0.05, + height: size * 0.02, + color: Colors.white, + ), + ), + Positioned( + right: size * 0.3, + child: Container( + width: size * 0.05, + height: size * 0.02, + color: Colors.white, + ), + ), + ], + ), + ); + } +} diff --git a/lib/widgets/custom_button.dart b/lib/widgets/custom_button.dart new file mode 100644 index 0000000..ae13f97 --- /dev/null +++ b/lib/widgets/custom_button.dart @@ -0,0 +1,98 @@ +import 'package:flutter/material.dart'; +import '../utils/app_colors.dart'; + +class CustomButton extends StatelessWidget { + final String text; + final VoidCallback? onPressed; + final bool isLoading; + final bool isOutlined; + final IconData? icon; + final double? width; + final double height; + final Color? backgroundColor; + + const CustomButton({ + Key? key, + required this.text, + this.onPressed, + this.isLoading = false, + this.isOutlined = false, + this.icon, + this.width, + this.height = 56, + this.backgroundColor, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + width: width, + height: height, + margin: const EdgeInsets.only(bottom: 16), + child: + isOutlined + ? OutlinedButton( + onPressed: isLoading ? null : onPressed, + style: OutlinedButton.styleFrom( + side: const BorderSide(color: AppColors.primaryRed, width: 2), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + child: _buildButtonContent(), + ) + : ElevatedButton( + onPressed: isLoading ? null : onPressed, + style: ElevatedButton.styleFrom( + backgroundColor: backgroundColor ?? AppColors.primaryRed, + foregroundColor: AppColors.textLight, + elevation: 2, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + child: _buildButtonContent(), + ), + ); + } + + Widget _buildButtonContent() { + if (isLoading) { + return const SizedBox( + width: 20, + height: 20, + child: CircularProgressIndicator( + strokeWidth: 2, + valueColor: AlwaysStoppedAnimation(AppColors.textLight), + ), + ); + } + + if (icon != null) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(icon, size: 20), + const SizedBox(width: 8), + Text( + text, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: isOutlined ? AppColors.primaryRed : AppColors.textLight, + ), + ), + ], + ); + } + + return Text( + text, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: isOutlined ? AppColors.primaryRed : AppColors.textLight, + ), + ); + } +} diff --git a/lib/widgets/custom_text_field.dart b/lib/widgets/custom_text_field.dart new file mode 100644 index 0000000..24f6619 --- /dev/null +++ b/lib/widgets/custom_text_field.dart @@ -0,0 +1,71 @@ +import 'package:flutter/material.dart'; +import '../utils/app_colors.dart'; + +class CustomTextField extends StatefulWidget { + final String hintText; + final String? labelText; + final IconData? prefixIcon; + final bool obscureText; + final TextEditingController? controller; + final String? Function(String?)? validator; + final TextInputType? keyboardType; + final bool enabled; + final VoidCallback? onSuffixIconPressed; + + const CustomTextField({ + Key? key, + required this.hintText, + this.labelText, + this.prefixIcon, + this.obscureText = false, + this.controller, + this.validator, + this.keyboardType, + this.enabled = true, + this.onSuffixIconPressed, + }) : super(key: key); + + @override + State createState() => _CustomTextFieldState(); +} + +class _CustomTextFieldState extends State { + bool _isObscured = true; + + @override + Widget build(BuildContext context) { + return Container( + margin: const EdgeInsets.only(bottom: 16), + child: TextFormField( + controller: widget.controller, + obscureText: widget.obscureText ? _isObscured : false, + validator: widget.validator, + keyboardType: widget.keyboardType, + enabled: widget.enabled, + style: const TextStyle(fontSize: 16, color: AppColors.textPrimary), + decoration: InputDecoration( + hintText: widget.hintText, + labelText: widget.labelText, + prefixIcon: + widget.prefixIcon != null + ? Icon(widget.prefixIcon, color: AppColors.primaryRed) + : null, + suffixIcon: + widget.obscureText + ? IconButton( + icon: Icon( + _isObscured ? Icons.visibility_off : Icons.visibility, + color: AppColors.secondaryGray, + ), + onPressed: () { + setState(() { + _isObscured = !_isObscured; + }); + }, + ) + : null, + ), + ), + ); + } +} diff --git a/lib/widgets/sensor_card.dart b/lib/widgets/sensor_card.dart new file mode 100644 index 0000000..dd5cd78 --- /dev/null +++ b/lib/widgets/sensor_card.dart @@ -0,0 +1,208 @@ +import 'package:flutter/material.dart'; +import '../utils/app_colors.dart'; + +class SensorCard extends StatelessWidget { + final String title; + final String value; + final String unit; + final IconData icon; + final Color statusColor; + final String status; + final VoidCallback? onTap; + + const SensorCard({ + Key? key, + required this.title, + required this.value, + required this.unit, + required this.icon, + required this.statusColor, + required this.status, + this.onTap, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Card( + elevation: 4, + margin: const EdgeInsets.symmetric(vertical: 8), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + child: InkWell( + borderRadius: BorderRadius.circular(12), + onTap: onTap, + child: Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [AppColors.backgroundWhite, AppColors.backgroundLight], + ), + ), + child: Column( + children: [ + Row( + children: [ + Container( + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: statusColor.withOpacity(0.1), + shape: BoxShape.circle, + ), + child: Icon(icon, color: statusColor, size: 24), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: AppColors.textSecondary, + ), + ), + const SizedBox(height: 4), + Row( + children: [ + Text( + value, + style: const TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + color: AppColors.textPrimary, + ), + ), + const SizedBox(width: 4), + Text( + unit, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: AppColors.textSecondary, + ), + ), + ], + ), + ], + ), + ), + ], + ), + const SizedBox(height: 12), + Container( + width: double.infinity, + padding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 6, + ), + decoration: BoxDecoration( + color: statusColor.withOpacity(0.1), + borderRadius: BorderRadius.circular(20), + ), + child: Text( + status, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: statusColor, + ), + ), + ), + ], + ), + ), + ), + ); + } +} + +class ControlCard extends StatelessWidget { + final String title; + final String description; + final IconData icon; + final bool isActive; + final VoidCallback onToggle; + final Color activeColor; + + const ControlCard({ + Key? key, + required this.title, + required this.description, + required this.icon, + required this.isActive, + required this.onToggle, + required this.activeColor, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Card( + elevation: 4, + margin: const EdgeInsets.symmetric(vertical: 8), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + child: Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [AppColors.backgroundWhite, AppColors.backgroundLight], + ), + ), + child: Row( + children: [ + Container( + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: (isActive ? activeColor : AppColors.secondaryGray) + .withOpacity(0.1), + shape: BoxShape.circle, + ), + child: Icon( + icon, + color: isActive ? activeColor : AppColors.secondaryGray, + size: 24, + ), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: AppColors.textPrimary, + ), + ), + const SizedBox(height: 4), + Text( + description, + style: const TextStyle( + fontSize: 13, + color: AppColors.textSecondary, + ), + ), + ], + ), + ), + Switch( + value: isActive, + onChanged: (_) => onToggle(), + activeColor: activeColor, + inactiveTrackColor: AppColors.secondaryLightGray, + ), + ], + ), + ), + ); + } +} diff --git a/linux/.gitignore b/linux/.gitignore new file mode 100644 index 0000000..d3896c9 --- /dev/null +++ b/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt new file mode 100644 index 0000000..eea0849 --- /dev/null +++ b/linux/CMakeLists.txt @@ -0,0 +1,128 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.13) +project(runner LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "debartis") +# The unique GTK application identifier for this application. See: +# https://wiki.gnome.org/HowDoI/ChooseApplicationID +set(APPLICATION_ID "com.example.debartis") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Load bundled libraries from the lib/ directory relative to the binary. +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Root filesystem for cross-building. +if(FLUTTER_TARGET_PLATFORM_SYSROOT) + set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +endif() + +# Define build configuration options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) + install(FILES "${bundled_library}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endforeach(bundled_library) + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/linux/flutter/CMakeLists.txt b/linux/flutter/CMakeLists.txt new file mode 100644 index 0000000..d5bd016 --- /dev/null +++ b/linux/flutter/CMakeLists.txt @@ -0,0 +1,88 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 0000000..e71a16d --- /dev/null +++ b/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,11 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + + +void fl_register_plugins(FlPluginRegistry* registry) { +} diff --git a/linux/flutter/generated_plugin_registrant.h b/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 0000000..e0f0a47 --- /dev/null +++ b/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake new file mode 100644 index 0000000..2e1de87 --- /dev/null +++ b/linux/flutter/generated_plugins.cmake @@ -0,0 +1,23 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/linux/runner/CMakeLists.txt b/linux/runner/CMakeLists.txt new file mode 100644 index 0000000..e97dabc --- /dev/null +++ b/linux/runner/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.13) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the application ID. +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Add dependency libraries. Add any application-specific dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) + +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") diff --git a/linux/runner/main.cc b/linux/runner/main.cc new file mode 100644 index 0000000..e7c5c54 --- /dev/null +++ b/linux/runner/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/linux/runner/my_application.cc b/linux/runner/my_application.cc new file mode 100644 index 0000000..c7beb7e --- /dev/null +++ b/linux/runner/my_application.cc @@ -0,0 +1,130 @@ +#include "my_application.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen* screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "debartis"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } else { + gtk_window_set_title(window, "debartis"); + } + + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GApplication::startup. +static void my_application_startup(GApplication* application) { + //MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application startup. + + G_APPLICATION_CLASS(my_application_parent_class)->startup(application); +} + +// Implements GApplication::shutdown. +static void my_application_shutdown(GApplication* application) { + //MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application shutdown. + + G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application); +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject* object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; + G_APPLICATION_CLASS(klass)->startup = my_application_startup; + G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + // Set the program name to the application ID, which helps various systems + // like GTK and desktop environments map this running application to its + // corresponding .desktop file. This ensures better integration by allowing + // the application to be recognized beyond its binary name. + g_set_prgname(APPLICATION_ID); + + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, + "flags", G_APPLICATION_NON_UNIQUE, + nullptr)); +} diff --git a/linux/runner/my_application.h b/linux/runner/my_application.h new file mode 100644 index 0000000..72271d5 --- /dev/null +++ b/linux/runner/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/macos/.gitignore b/macos/.gitignore new file mode 100644 index 0000000..746adbb --- /dev/null +++ b/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/macos/Flutter/Flutter-Debug.xcconfig b/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 0000000..c2efd0b --- /dev/null +++ b/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/Flutter-Release.xcconfig b/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 0000000..c2efd0b --- /dev/null +++ b/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 0000000..f07ffd2 --- /dev/null +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,22 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + +import cloud_firestore +import firebase_auth +import firebase_core +import firebase_database +import firebase_storage +import shared_preferences_foundation + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + FLTFirebaseFirestorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseFirestorePlugin")) + FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin")) + FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) + FLTFirebaseDatabasePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseDatabasePlugin")) + FLTFirebaseStoragePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseStoragePlugin")) + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) +} diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..c2081f7 --- /dev/null +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,705 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC10EC2044A3C60003C045; + remoteInfo = Runner; + }; + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* debartis.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "debartis.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 331C80D2294CF70F00263BE5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C80D6294CF71000263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C80D7294CF71000263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 331C80D6294CF71000263BE5 /* RunnerTests */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* debartis.app */, + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C80D4294CF70F00263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 331C80D1294CF70F00263BE5 /* Sources */, + 331C80D2294CF70F00263BE5 /* Frameworks */, + 331C80D3294CF70F00263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C80DA294CF71000263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* debartis.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C80D4294CF70F00263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 33CC10EC2044A3C60003C045; + }; + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 331C80D4294CF70F00263BE5 /* RunnerTests */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C80D3294CF70F00263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C80D1294CF70F00263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC10EC2044A3C60003C045 /* Runner */; + targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */; + }; + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 331C80DB294CF71000263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.debartis.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/debartis.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/debartis"; + }; + name = Debug; + }; + 331C80DC294CF71000263BE5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.debartis.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/debartis.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/debartis"; + }; + name = Release; + }; + 331C80DD294CF71000263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.debartis.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/debartis.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/debartis"; + }; + name = Profile; + }; + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C80DB294CF71000263BE5 /* Debug */, + 331C80DC294CF71000263BE5 /* Release */, + 331C80DD294CF71000263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..b712dd2 --- /dev/null +++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/Runner.xcworkspace/contents.xcworkspacedata b/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..1d526a1 --- /dev/null +++ b/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/macos/Runner/AppDelegate.swift b/macos/Runner/AppDelegate.swift new file mode 100644 index 0000000..b3c1761 --- /dev/null +++ b/macos/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import Cocoa +import FlutterMacOS + +@main +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } +} diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..96d3fee --- /dev/null +++ b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "info": { + "version": 1, + "author": "xcode" + }, + "images": [ + { + "size": "16x16", + "idiom": "mac", + "filename": "app_icon_16.png", + "scale": "1x" + }, + { + "size": "16x16", + "idiom": "mac", + "filename": "app_icon_32.png", + "scale": "2x" + }, + { + "size": "32x32", + "idiom": "mac", + "filename": "app_icon_32.png", + "scale": "1x" + }, + { + "size": "32x32", + "idiom": "mac", + "filename": "app_icon_64.png", + "scale": "2x" + }, + { + "size": "128x128", + "idiom": "mac", + "filename": "app_icon_128.png", + "scale": "1x" + }, + { + "size": "128x128", + "idiom": "mac", + "filename": "app_icon_256.png", + "scale": "2x" + }, + { + "size": "256x256", + "idiom": "mac", + "filename": "app_icon_256.png", + "scale": "1x" + }, + { + "size": "256x256", + "idiom": "mac", + "filename": "app_icon_512.png", + "scale": "2x" + }, + { + "size": "512x512", + "idiom": "mac", + "filename": "app_icon_512.png", + "scale": "1x" + }, + { + "size": "512x512", + "idiom": "mac", + "filename": "app_icon_1024.png", + "scale": "2x" + } + ] +} \ No newline at end of file diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000000000000000000000000000000000000..37492e5d18b4c121d16198c10dc7039f5e71047a GIT binary patch literal 49720 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 *F!5GEsI)Qm7PHw)I&%<;owQT$T|0@ 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 Axc~qF literal 0 HcmV?d00001 diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000000000000000000000000000000000000..e178c8868d034cbffdbccf386cbb6c312e6f5a12 GIT binary patch literal 3424 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 LJ!?~b%qRB$WIWgA literal 0 HcmV?d00001 diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 0000000000000000000000000000000000000000..f605f97f910fb2c891b5d163c3382fbfa058afba GIT binary patch literal 435 zcmV;k0ZjghP)&Yf6M`2B*0 zolS;|yM9#Q@k6K%>RnGII3AGHC?EvjdkC+pGYDXNP&{Kpv~4+_g3c+71fVCVvfv{k zX|V|Pkrp^7F>fR&bUrc0-K+*v1TcjhS&-;B5)W@8v<=|{8G~Xw#Z-av7DqnK#szMk zEp%*&_b-3y_D`-mo^@K^72pvjARAS^8DU2iwZB!B-M~9;pt5hVJ9?3N} zQKVcsMy@$>#x}z?cKp6W&*S-veLtV~=ly=a-q+{ze#ag^W+yMBDFXt5d;F=Y=Kiyf$rTR5keJem>Xh-DE|Qu9}G(!% zs(-dDjKtVTN2((+>FET`riBn@YdWlcEeMWf?Anbq%AA`V%-)0iLl5N6qj{dkUNW1c zesZ{s4OFqL8+}6JykBNS=;JAIc5j4ROl}#;{!A_7W(={90&G^6`?XoO`c*-2iyg}+ zBEIryDIg5fm;0KThf*hBABm|4#B}(&EM$Wr@}WM+Jp#~7#15U$^|Syp23uzwht809 zVhbA-JzqWaa?Z`G)eCB{rDBg0lu7qU1h82 z;UCd{t5~}~?5|sO07;G<^qaRM9tkBx&v&Ffdp&M+(QiFCa&O%zeiMH3_LtesyUbe+ zg5N`9JUbS&PANt%TjM1+%`h`c*4bQPgFk$~Stjd-n{l^=x)UP2yQ~oi!jdbm^&@w7- z%}liIdVJ>?TjySuoK9>+gbwC=7^rP<$-8kJb8>K3(r%%cqn|<;RiLH4_npk2dwNrD z>DXx)?wzMup{$%0DdNnBqL4)mNA*t@Tb^gZeIuC1vGNo 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 literal 0 HcmV?d00001 diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 0000000000000000000000000000000000000000..65388208240346ca5c590c8fe6988bb110d6670c GIT binary patch literal 1664 zcmV-`27md9P)% z+iw(A9LK+Bc6PTdE8BKUsnQl6T8&f_K@9a0DKBVRd{Q*V;NPGS9|^ux-%%1oYIrb& z2Swi7#;6f!g|^b$szF-Xg)X;lx3Js2&6qR8&d%k`?o4Nqy_{s*p3853zt5T3^ZlL4 zDc|zhpL2u|KL~+op?wzm=?La1i}sJlKam>^_VQ@{5qv$=#`?h)>!oi`RKMiE`SJ%(gc)6@J{X7@U)+|>+Jsz-l@GgJhD$f=?3rAK0iEBCZHsPcWPf49)<}hnc$t; zTZ3QM8c<2_P-L)Vf_G|f4<70~;K>K?)ZRWktgZp23lB6k{)|AwMG%rkB)SvfJG~II zSQh*jTmwoMo=EUpTFxs?o0|sVa>Wr^`oOaPym>$=!_yLIxBz`=^j|_Kd;oyUl7G`Y zpw!{%2#%x~;b%aI0EjM2{+Me(Il$8rS>1u=z_4aW^uddO=(g<7um+S1JpHwFrw=YJ zjt2jTbk`~T;;oNt`P1Y9ejgyBtvjDBW%NEk3$N~{vPb#XZ!bnLcRz*hZM6u7llbxKYSH0o$;?hc3@w`UI$Lm0U5;Tc zBA~m?jo_U$&dsc`^8mAp4gMXIK95W4L?HtqI0M9E5boUt7icOPe6~JxErH={iM-=Y z^MKAKH+HqlPr$^@1k<=s-86VQ0dv2AdfY+^)5Ensfy<52;dsQQ zPOix^sIzCj4>)yc1@n(&aF)v8`n6kx*OcpR)gT}q`Vs}_LjC#DZ-YzgUaAD zZdnGy8Kylm59r(O!`JUN=VWkr87D5T*m4f_h$jHw+v&lvzKuB<{4Iu)SKy$)LGoZAU1YF~EO6{t!P*#&QTmXGefUMpQM)1AHEWR=>*#Z*;2^rVMiOc2ND~ zFxP<7e?)O~UqGp!8DB;J@UqsA-N&8~NrqBcK*MUUujd*h84RT@`j<;+Rlt!wKBazk zDk1sT+cVz>oEnK@emRZqtu?X?PCaxCZzp)ic?KaE7+X`xUPV`GCrUhxPLTl?e}P=L0Gi9)-^bR5m=x|NjD&4-fObKy|=B$^(dQaP`1Lmciz22>|Jw0i+m9XxV*0ICum@&u5og-31z$}{+4*#8GLK(?jkx2Agl0000< KMNUMnLSTX + + + + + + + + + + + + + + + + + + + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/Runner/Configs/AppInfo.xcconfig b/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 0000000..b1564aa --- /dev/null +++ b/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = debartis + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.example.debartis + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2025 com.example. All rights reserved. diff --git a/macos/Runner/Configs/Debug.xcconfig b/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 0000000..36b0fd9 --- /dev/null +++ b/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/macos/Runner/Configs/Release.xcconfig b/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 0000000..dff4f49 --- /dev/null +++ b/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/macos/Runner/Configs/Warnings.xcconfig b/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 0000000..42bcbf4 --- /dev/null +++ b/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements new file mode 100644 index 0000000..dddb8a3 --- /dev/null +++ b/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + + diff --git a/macos/Runner/Info.plist b/macos/Runner/Info.plist new file mode 100644 index 0000000..4789daa --- /dev/null +++ b/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/macos/Runner/MainFlutterWindow.swift b/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 0000000..3cc05eb --- /dev/null +++ b/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements new file mode 100644 index 0000000..852fa1a --- /dev/null +++ b/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/macos/RunnerTests/RunnerTests.swift b/macos/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000..61f3bd1 --- /dev/null +++ b/macos/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Cocoa +import FlutterMacOS +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/pubspec.lock b/pubspec.lock new file mode 100644 index 0000000..dfc3514 --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,650 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _flutterfire_internals: + dependency: transitive + description: + name: _flutterfire_internals + sha256: a5788040810bd84400bc209913fbc40f388cded7cdf95ee2f5d2bff7e38d5241 + url: "https://pub.dev" + source: hosted + version: "1.3.58" + archive: + dependency: transitive + description: + name: archive + sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" + url: "https://pub.dev" + source: hosted + version: "4.0.7" + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" + async: + dependency: transitive + description: + name: async + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + url: "https://pub.dev" + source: hosted + version: "2.13.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + characters: + dependency: transitive + description: + name: characters + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff + url: "https://pub.dev" + source: hosted + version: "2.0.3" + cli_util: + dependency: transitive + description: + name: cli_util + sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c + url: "https://pub.dev" + source: hosted + version: "0.4.2" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" + cloud_firestore: + dependency: "direct main" + description: + name: cloud_firestore + sha256: "39be8bf17e55d1211d8e2142ba1551bbcf30e272fe90adb36d54a9b1ae97bd30" + url: "https://pub.dev" + source: hosted + version: "5.6.11" + cloud_firestore_platform_interface: + dependency: transitive + description: + name: cloud_firestore_platform_interface + sha256: a8a1ce4f8da07225b8fe37ee3eeff3bde019e0607bab93329091f5491ee2f62f + url: "https://pub.dev" + source: hosted + version: "6.6.11" + cloud_firestore_web: + dependency: transitive + description: + name: cloud_firestore_web + sha256: a3c0c5913860abfa0c9af68e245feb24d51ffebf07910780efc0d01ac463dc92 + url: "https://pub.dev" + source: hosted + version: "4.4.11" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + crypto: + dependency: transitive + description: + name: crypto + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + url: "https://pub.dev" + source: hosted + version: "3.0.6" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 + url: "https://pub.dev" + source: hosted + version: "1.0.8" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.dev" + source: hosted + version: "1.3.3" + ffi: + dependency: transitive + description: + name: ffi + sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" + firebase_auth: + dependency: "direct main" + description: + name: firebase_auth + sha256: f5b640f664aae71774b398ed765740c1b5d34a339f4c4975d4dde61d59a623f6 + url: "https://pub.dev" + source: hosted + version: "5.6.2" + firebase_auth_platform_interface: + dependency: transitive + description: + name: firebase_auth_platform_interface + sha256: "62199aeda6a688cbdefbcbbac53ede71be3ac8807cec00a8066d444797a08806" + url: "https://pub.dev" + source: hosted + version: "7.7.2" + firebase_auth_web: + dependency: transitive + description: + name: firebase_auth_web + sha256: caaf29b7eb9d212dcec36d2eaa66504c5bd523fe844302833680c9df8460fbc0 + url: "https://pub.dev" + source: hosted + version: "5.15.2" + firebase_core: + dependency: "direct main" + description: + name: firebase_core + sha256: c6e8a6bf883d8ddd0dec39be90872daca65beaa6f4cff0051ed3b16c56b82e9f + url: "https://pub.dev" + source: hosted + version: "3.15.1" + firebase_core_platform_interface: + dependency: transitive + description: + name: firebase_core_platform_interface + sha256: "5dbc900677dcbe5873d22ad7fbd64b047750124f1f9b7ebe2a33b9ddccc838eb" + url: "https://pub.dev" + source: hosted + version: "6.0.0" + firebase_core_web: + dependency: transitive + description: + name: firebase_core_web + sha256: "0ed0dc292e8f9ac50992e2394e9d336a0275b6ae400d64163fdf0a8a8b556c37" + url: "https://pub.dev" + source: hosted + version: "2.24.1" + firebase_database: + dependency: "direct main" + description: + name: firebase_database + sha256: "8f3b44a1217ca4553c62eaaae4269fccb7d3e25116c8f8cf86ddac98213df6ca" + url: "https://pub.dev" + source: hosted + version: "11.3.9" + firebase_database_platform_interface: + dependency: transitive + description: + name: firebase_database_platform_interface + sha256: "633ac51a06b1d6a55d7dcc1048bc817fb770e6830d62c8518919cd4fa1ed299a" + url: "https://pub.dev" + source: hosted + version: "0.2.6+9" + firebase_database_web: + dependency: transitive + description: + name: firebase_database_web + sha256: "900122529a269a6a9eb9be3af68bc1cae2196c219b026dfa378638182873bdda" + url: "https://pub.dev" + source: hosted + version: "0.2.6+15" + firebase_storage: + dependency: "direct main" + description: + name: firebase_storage + sha256: "172b53e91be47e7e17760b574ef0dce03e071f669eebc8348358ac540b53afa4" + url: "https://pub.dev" + source: hosted + version: "12.4.9" + firebase_storage_platform_interface: + dependency: transitive + description: + name: firebase_storage_platform_interface + sha256: "87353c1c312b5a7a0cce0ff4a747fde5fa95f52879286c1a26c8013ac3625412" + url: "https://pub.dev" + source: hosted + version: "5.2.9" + firebase_storage_web: + dependency: transitive + description: + name: firebase_storage_web + sha256: e1ea59820fd7ac38189f4eeb6e5e81c15b40f3f6edb64ce724222c2a671b67c9 + url: "https://pub.dev" + source: hosted + version: "3.10.16" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_launcher_icons: + dependency: "direct dev" + description: + name: flutter_launcher_icons + sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea" + url: "https://pub.dev" + source: hosted + version: "0.13.1" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" + url: "https://pub.dev" + source: hosted + version: "5.0.0" + flutter_svg: + dependency: "direct dev" + description: + name: flutter_svg + sha256: cd57f7969b4679317c17af6fd16ee233c1e60a82ed209d8a475c54fd6fd6f845 + url: "https://pub.dev" + source: hosted + version: "2.2.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + http: + dependency: transitive + description: + name: http + sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" + url: "https://pub.dev" + source: hosted + version: "1.4.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" + image: + dependency: transitive + description: + name: image + sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928" + url: "https://pub.dev" + source: hosted + version: "4.5.4" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + url: "https://pub.dev" + source: hosted + version: "10.0.9" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + url: "https://pub.dev" + source: hosted + version: "3.0.9" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + lints: + dependency: transitive + description: + name: lints + sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 + url: "https://pub.dev" + source: hosted + version: "5.1.1" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + url: "https://pub.dev" + source: hosted + version: "0.12.17" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" + source: hosted + version: "0.11.1" + meta: + dependency: transitive + description: + name: meta + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + url: "https://pub.dev" + source: hosted + version: "1.16.0" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + path_parsing: + dependency: transitive + description: + name: path_parsing + sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.dev" + source: hosted + version: "2.3.0" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646" + url: "https://pub.dev" + source: hosted + version: "6.1.0" + platform: + dependency: transitive + description: + name: platform + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + url: "https://pub.dev" + source: hosted + version: "3.1.6" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + posix: + dependency: transitive + description: + name: posix + sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61" + url: "https://pub.dev" + source: hosted + version: "6.0.3" + provider: + dependency: "direct main" + description: + name: provider + sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84" + url: "https://pub.dev" + source: hosted + version: "6.1.5" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" + url: "https://pub.dev" + source: hosted + version: "2.5.3" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac" + url: "https://pub.dev" + source: hosted + version: "2.4.10" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03" + url: "https://pub.dev" + source: hosted + version: "2.5.4" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 + url: "https://pub.dev" + source: hosted + version: "2.4.3" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + source_span: + dependency: transitive + description: + name: source_span + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + url: "https://pub.dev" + source: hosted + version: "1.10.1" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + test_api: + dependency: transitive + description: + name: test_api + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + url: "https://pub.dev" + source: hosted + version: "0.7.4" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + vector_graphics: + dependency: transitive + description: + name: vector_graphics + sha256: a4f059dc26fc8295b5921376600a194c4ec7d55e72f2fe4c7d2831e103d461e6 + url: "https://pub.dev" + source: hosted + version: "1.1.19" + vector_graphics_codec: + dependency: transitive + description: + name: vector_graphics_codec + sha256: "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146" + url: "https://pub.dev" + source: hosted + version: "1.1.13" + vector_graphics_compiler: + dependency: transitive + description: + name: vector_graphics_compiler + sha256: "557a315b7d2a6dbb0aaaff84d857967ce6bdc96a63dc6ee2a57ce5a6ee5d3331" + url: "https://pub.dev" + source: hosted + version: "1.1.17" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + url: "https://pub.dev" + source: hosted + version: "15.0.0" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + xml: + dependency: transitive + description: + name: xml + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 + url: "https://pub.dev" + source: hosted + version: "6.5.0" + yaml: + dependency: transitive + description: + name: yaml + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce + url: "https://pub.dev" + source: hosted + version: "3.1.3" +sdks: + dart: ">=3.7.0 <4.0.0" + flutter: ">=3.27.0" diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..d50bbbe --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,121 @@ +name: debartis +description: "A new Flutter project." +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +# In Windows, build-name is used as the major, minor, and patch parts +# of the product and file versions while build-number is used as the build suffix. +version: 1.0.0+1 + +environment: + sdk: ^3.7.0 + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.8 + + # Firebase dependencies + firebase_core: ^3.15.1 + firebase_auth: ^5.6.2 + cloud_firestore: ^5.6.11 + firebase_storage: ^12.4.9 + firebase_database: ^11.1.6 + + # Additional useful packages + provider: ^6.1.1 + shared_preferences: ^2.2.2 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^5.0.0 + flutter_launcher_icons: ^0.13.1 + flutter_svg: ^2.0.9 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + assets: + - assets/icons/ + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/to/resolution-aware-images + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/to/asset-from-package + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/to/font-from-package + +# Flutter Launcher Icons configuration +flutter_launcher_icons: + android: "launcher_icon" + ios: true + image_path: "assets/icons/app_icon.png" + min_sdk_android: 21 # android min sdk min:16, default 21 + remove_alpha_ios: true # Remove alpha channel for iOS App Store + web: + generate: true + image_path: "assets/icons/app_icon.png" + background_color: "#1976D2" + theme_color: "#2196F3" + windows: + generate: true + image_path: "assets/icons/app_icon.png" + icon_size: 48 # min:48, max:256, default: 48 + macos: + generate: true + image_path: "assets/icons/app_icon.png" diff --git a/scripts/generate_icon.dart b/scripts/generate_icon.dart new file mode 100644 index 0000000..693780e --- /dev/null +++ b/scripts/generate_icon.dart @@ -0,0 +1,136 @@ +import 'dart:io'; +import 'dart:ui' as ui; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +// Script untuk generate icon PNG dari Flutter widget +Future generateIcon() async { + WidgetsFlutterBinding.ensureInitialized(); + + const size = 512; + final recorder = ui.PictureRecorder(); + final canvas = Canvas(recorder); + + // Background gradient + final backgroundPaint = + Paint() + ..shader = const LinearGradient( + colors: [Color(0xFF1976D2), Color(0xFF2196F3)], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ).createShader(Rect.fromLTWH(0, 0, size.toDouble(), size.toDouble())); + + // Draw rounded background + canvas.drawRRect( + RRect.fromLTRBR( + 0, + 0, + size.toDouble(), + size.toDouble(), + const Radius.circular(80), + ), + backgroundPaint, + ); + + // White paint for elements + final whitePaint = + Paint() + ..color = Colors.white + ..style = PaintingStyle.fill; + + final linePaint = + Paint() + ..color = Colors.white + ..strokeWidth = 8 + ..strokeCap = StrokeCap.round; + + // Draw API connection lines + // Left side + canvas.drawLine(const Offset(80, 200), const Offset(180, 200), linePaint); + canvas.drawLine(const Offset(80, 256), const Offset(180, 256), linePaint); + canvas.drawLine(const Offset(80, 312), const Offset(180, 312), linePaint); + + // Right side + canvas.drawLine(const Offset(332, 200), const Offset(432, 200), linePaint); + canvas.drawLine(const Offset(332, 256), const Offset(432, 256), linePaint); + canvas.drawLine(const Offset(332, 312), const Offset(432, 312), linePaint); + + // Central connections + canvas.drawLine(const Offset(200, 200), const Offset(220, 200), linePaint); + canvas.drawLine(const Offset(200, 256), const Offset(220, 256), linePaint); + canvas.drawLine(const Offset(200, 312), const Offset(220, 312), linePaint); + + canvas.drawLine(const Offset(292, 200), const Offset(312, 200), linePaint); + canvas.drawLine(const Offset(292, 256), const Offset(312, 256), linePaint); + canvas.drawLine(const Offset(292, 312), const Offset(312, 312), linePaint); + + // Draw API nodes (circles) + canvas.drawCircle(const Offset(60, 200), 20, whitePaint); + canvas.drawCircle(const Offset(60, 256), 20, whitePaint); + canvas.drawCircle(const Offset(60, 312), 20, whitePaint); + + canvas.drawCircle(const Offset(452, 200), 20, whitePaint); + canvas.drawCircle(const Offset(452, 256), 20, whitePaint); + canvas.drawCircle(const Offset(452, 312), 20, whitePaint); + + // Central processing unit + canvas.drawRRect( + RRect.fromLTRBR(220, 180, 292, 312, const Radius.circular(12)), + whitePaint, + ); + + // Fire detection symbol + final firePaint = + Paint() + ..shader = const LinearGradient( + colors: [Color(0xFFFF5722), Color(0xFFF44336)], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ).createShader(const Rect.fromLTWH(240, 190, 32, 60)); + + // Simple fire shape + final firePath = + Path() + ..moveTo(256, 190) + ..quadraticBezierTo(240, 200, 245, 220) + ..quadraticBezierTo(250, 235, 256, 240) + ..quadraticBezierTo(262, 235, 267, 220) + ..quadraticBezierTo(272, 200, 256, 190); + + canvas.drawPath(firePath, firePaint); + + // Status indicator + final statusPaint = Paint()..color = const Color(0xFF4CAF50); + canvas.drawCircle(const Offset(400, 112), 24, statusPaint); + + // Check mark + final checkPaint = + Paint() + ..color = Colors.white + ..strokeWidth = 4 + ..strokeCap = StrokeCap.round + ..style = PaintingStyle.stroke; + + final checkPath = + Path() + ..moveTo(388, 112) + ..lineTo(396, 120) + ..lineTo(412, 104); + + canvas.drawPath(checkPath, checkPaint); + + // Create image + final picture = recorder.endRecording(); + final image = await picture.toImage(size, size); + final byteData = await image.toByteData(format: ui.ImageByteFormat.png); + + if (byteData != null) { + final file = File('assets/icons/app_icon.png'); + await file.writeAsBytes(byteData.buffer.asUint8List()); + print('Icon generated successfully: ${file.path}'); + } +} + +void main() async { + await generateIcon(); +} diff --git a/test/widget_test.dart b/test/widget_test.dart new file mode 100644 index 0000000..b148e3c --- /dev/null +++ b/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:debartis/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} diff --git a/web/favicon.png b/web/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..f605f97f910fb2c891b5d163c3382fbfa058afba GIT binary patch literal 435 zcmV;k0ZjghP)&Yf6M`2B*0 zolS;|yM9#Q@k6K%>RnGII3AGHC?EvjdkC+pGYDXNP&{Kpv~4+_g3c+71fVCVvfv{k zX|V|Pkrp^7F>fR&bUrc0-K+*v1TcjhS&-;B5)W@8v<=|{8G~Xw#Z-av7DqnK#szMk zEp%*&_b-3y_D`-Bs+pXYUU_C6=x>Z*x|po}0750A)YQzL8O z*}QY@6#(wm|N89V;gMXqY@~lZ_|4Ky?^MZ=5GeF~!{?P+mk={k2flPMzJ0r`jXV2y z;cbge1MvGU;i^tp7n{}Xh0zqQ$=N2zcEEh8Oba#CAQzeBpIQWpTe42V5nuY#KSZD`JA_{IL zSU~UQvg7x`OD#`@IxznsMH1aE;g*ToB8|>^^Cm8s4&f^JZey`GOJ`f}^Ytw#WLUCd zYM>raeu*f7TW9jt6(osn!-i7N@(Kx4uR*@1bWhCluj# z_UuqIyi7E#fybuiqXDc4KBLf$W1L2|H3sT2Y8lljx+5@-_E%uuJM7X~!(u0SC+H0A zLjv#~?<8*~TgNW;2>UKCWVFiLUj_9rE$1&7vN;oZdQ9%T&nExvgSc-W+Fo?$eftY? z)o6sCdF;K^GdoLQA)th9poMTD7PjQ&)_k<64pjBlv+&55wr?MXo{3A2e6s6hrqc z>nr9M=?2D-wVxc4t(TDTF{2vFT2{erWD?`uw96YjFFIuhK0wqp1YO*$Lp^Tz>-`Gf z-s#qidTc`d808drksADU-?~WnVNMJJ6AxMrJ~zU5ZK`voy9%17Q+F;)#`(*7MyBR$ z)8S90VcXgdox|IYW8VPGAv4Y1VF|q>_OzeAr1sy3kup#G{Q8(jrAK7aOb)6K{c#MK zNO$=9DdR$(?plhM-@h@eu@o!Yemg6LS6A)b{DbxC!|d~sE-&&?+sxd1f8x!y5*pY! z^R538C#8RgbLw`b*op4)#Gu%5Q;V3VUo66z6iX>Dvt}5P^3(^z*Q)SVC|Wv(qEwt}^dLn+e_tb2zKVTc8ytB&vdrOSi^6fcB&LHlhT?ouoU!O{=W!0q_v))t)Gqoetp~^j#^QIv*pJseV=HOqcQ?q- z0kBvYvk1Ax$KM04iJk=vaT!SIlj=f)4hKz6r?tAAmL5F$${@tK)z`w^M=3zDN$->I z_{SqZFOAS4c3!6LVy0)9^u&rx0o@2owP7`6^OWO3(>B__6J%lf+avAh@sWV_$K4lX z#BN2bPe-mGJu30DZN;U9!9HuoKVJ+~K|>eVgzAi*=9|v z6^oU#uzKZap3)xPBIwZlWKlra2R34?3 zAm$FcQV*?`?HRqGH3Sg>fAVT;Nj4uY7n-}4jf{4_5Yvy~g1(KENhcq4C++!jr+V|BfKSBRe8Gcm4d7I@E7AKOB~E^?k@Q1)tkG6-Wv_cohA<02R&_#0o0HgO zlV?@hlIO;kDk-34Sv+AG@=K#CLeK~HR(wE#Z8#daT%Qcm8(+wR`71nE+ahef0&8TZ z=>-)_)J5}pX{a_Rs@(gwwlYv)xdA0&?(FFm)RRJ%XX&H z?AjY(a(GJ8NHT4q_mIxoJT-p1YbF)S+&8reZASNsh&ggPR(~k9I%ugyZ)`S@ZVn1j zu$;wPZ&uEaZ%_A8%+>n3n-&eiHcxTyJ`y_ZGxnSKDdqOkk+2_2RB&PTYL3n+gE!3Sxv&He+jyD zI9tWdbg^zQ4?jVdoZxyLGA!gyzw}p2J6wHDT1eLOzef~Fbqc}m?Ls^pEoUg+NtQjO{kkETw+^f4^+@+g0EF8} zEmpq8cP=hLDa!BpIgp&u?V-YRCoCLy*Mkp~>ESy!HjaC{1r4k;b!hT>vFkQ=SAh?R zG(r=>O&1rz{#mViXyqYaMU$E;awoH=RNQPzlqXcGJ{(1D9F>J*)^u-w{uQfyIRs*N z&(s|v%z8N6Axxt4pUa+-9=bhVRgXh6UI^3kjF_HgFiNZ-n5f~;5X+jsIw+i%C1nGMssq(tdqPQRm!AoWsA(2S24mH#vcD_WUusb4Bz&(;xy|g&Ce%Lv{{22A@u;g~l zCDklPXSt$Tg2Y-{@bBdN;jAGh9J5C~dkR!+Ts9%uWO-k$uiZq3kUeFual4m5jzIB z=S7G3T?hF%UP1eBMSR=JI1R)}RCjsHH~pHEO_0jF+Xt}QN)ubh*0n#6Bs;KWzVnPY z$=4DZHZmt95y=}W<7KJ+lH1Q!v`C9BF{eMX8VGWZwb5pt8=zL5yyQc`NlnaRFMk5& z4KT_)J;1Cf>f|YDzOGj6yMEA6d)Gs{XGN`>BoYMLhf&TAqGN(SP{%i~SVqQ=F4#y$ zyKmSBV2uLVYZg=zvBzOf(MjmKSa0FS_7)T!(v~(Z0Jv_}!OPYAd#FKY8D8eT4xEIK|R}it8$c(=xPhk=D8$zX!Hn z=Q&-Ed9*p=O8tH(z=oZh3q-!V9q1ad(3F-jx`2e>!KPCLe5g%$!53w2jH_(JcdNeH zd{Rb4^uo)t?3FRQO*A8@+-;5b>GIJk=#NvCZ5<9a>^vZi3TL1hH)BTcw-uBP$H=W+ zLu5~F{E`AFSVCrVrh>XqZ~=ncL01SK<_>(k0B2Krk7hyLf=bzq!GjU`Nu@Fs;h!GR zaw@6hr{Ol?aouwIcfw@z)2A1jT#Yqe_t3c2)gmgV0S~>E3hXRIy^DxmSHi<;zi%n{ z(mr?xyM{nT1Dz`s96_P-)- zv)0AOq)j7*#lfe2%xBu(C#hYSL?-NC?oq?QCd%I{&2Z^BFns`Z@==uET5R^zE*TUs4VEj3+!NQL z3FJ7JE%8J^VJ%pvJ>~m&?z8m001uc(gtc-3DcG9rOphp7ubO`V4KIZm<%zvUCV=ou zJwAB|;#CpjZNHS`@f1zOYi_Ib2AUxZd=C&7mOKUXBzsFpacG7g2F8zDAqd~gMvgg+(>h1!i@_88K!*f0zPf&MR!Ko_FGG& zi|g|V$rV&kzgNPxZ=}ABZVC!bS~Mjb28LcKJTotzr3x|ARvO+ z9%vo!Ol(D$)< zpd&3m9w>iwydHhB%-ySGAQ4obsDxj%pn`yl>)c)I8QU_7K9jVvToJ+&8ebwOfFKB4 z&eB)MaAZnxpPD=ev&Ba+D2`SlwstFVLX5sB=lbl|m&Nt&rI2^XDRCsi-vR${dIY(; z@Bzg4luZ8PM8j2Spt4jAuY%qO`Y74p>q65{fMNw`MChz3xI}l-4?K?+w@?Z6)by}i;U0D+y}44(ON$N1RUe}wPFAEw;ZEN@`*&Y3h+BvV{< zXHeVaf73a$VgGxLp~T(UAvqZ9{U0nyl>}tXKXPEQs3@stfDa-ZuJZh~=%QM7%m3N4 zb#}{VXJx??>OS}%!QEsfpUtO=L9m+T*&V8mX%VNg{-XZvW&T`sPiD;Oa zZ3^I1cJQxp_KnuYhG<>Em>h(D3?ImggPR;8$Qi7=ZtFx*T&tj`=Ur#gxcjghzE&M7 zFPE+v?^nrNmtH%(<2!@9?^c6&R8p6^Kz3Jl>^IgVFyt=!^xxu_^_Dro;@ul7{-g&B zH;xPtDARj42cV&Xxc@{%>)xEuQ2*0?+x+3VbuTD*R|1|6QdCAPL`a9l3jj|-3 z*qPzOfBS(2MgA46c`GF27&t>DQMxP8^9{(TzPzvJ! literal 0 HcmV?d00001 diff --git a/web/icons/Icon-512.png b/web/icons/Icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..92f606c5ca6077ea286c578fa8c44b646311c1e6 GIT binary patch literal 20047 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 literal 0 HcmV?d00001 diff --git a/web/icons/Icon-maskable-192.png b/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000000000000000000000000000000000000..88e023715d427a3fc82b9fdef07109031bd6ecc4 GIT binary patch literal 5416 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 literal 0 HcmV?d00001 diff --git a/web/icons/Icon-maskable-512.png b/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000000000000000000000000000000000000..92f606c5ca6077ea286c578fa8c44b646311c1e6 GIT binary patch literal 20047 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 literal 0 HcmV?d00001 diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..b9193e0 --- /dev/null +++ b/web/index.html @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + debartis + + + + + + diff --git a/web/manifest.json b/web/manifest.json new file mode 100644 index 0000000..2d9702b --- /dev/null +++ b/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "debartis", + "short_name": "debartis", + "start_url": ".", + "display": "standalone", + "background_color": "#1976D2", + "theme_color": "#2196F3", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} \ No newline at end of file diff --git a/windows/.gitignore b/windows/.gitignore new file mode 100644 index 0000000..d492d0d --- /dev/null +++ b/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt new file mode 100644 index 0000000..dedac25 --- /dev/null +++ b/windows/CMakeLists.txt @@ -0,0 +1,108 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(debartis LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "debartis") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(VERSION 3.14...3.25) + +# Define build configuration option. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() +# Define settings for the Profile build mode. +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/windows/flutter/CMakeLists.txt b/windows/flutter/CMakeLists.txt new file mode 100644 index 0000000..903f489 --- /dev/null +++ b/windows/flutter/CMakeLists.txt @@ -0,0 +1,109 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + ${FLUTTER_TARGET_PLATFORM} $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 0000000..ff5147a --- /dev/null +++ b/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,23 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include +#include +#include +#include + +void RegisterPlugins(flutter::PluginRegistry* registry) { + CloudFirestorePluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("CloudFirestorePluginCApi")); + FirebaseAuthPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FirebaseAuthPluginCApi")); + FirebaseCorePluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FirebaseCorePluginCApi")); + FirebaseStoragePluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FirebaseStoragePluginCApi")); +} diff --git a/windows/flutter/generated_plugin_registrant.h b/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 0000000..dc139d8 --- /dev/null +++ b/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake new file mode 100644 index 0000000..fb0d4f3 --- /dev/null +++ b/windows/flutter/generated_plugins.cmake @@ -0,0 +1,27 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + cloud_firestore + firebase_auth + firebase_core + firebase_storage +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/windows/runner/CMakeLists.txt b/windows/runner/CMakeLists.txt new file mode 100644 index 0000000..394917c --- /dev/null +++ b/windows/runner/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/windows/runner/Runner.rc b/windows/runner/Runner.rc new file mode 100644 index 0000000..0eb202b --- /dev/null +++ b/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD +#else +#define VERSION_AS_NUMBER 1,0,0,0 +#endif + +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.example" "\0" + VALUE "FileDescription", "debartis" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "debartis" "\0" + VALUE "LegalCopyright", "Copyright (C) 2025 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "debartis.exe" "\0" + VALUE "ProductName", "debartis" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/windows/runner/flutter_window.cpp b/windows/runner/flutter_window.cpp new file mode 100644 index 0000000..955ee30 --- /dev/null +++ b/windows/runner/flutter_window.cpp @@ -0,0 +1,71 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + + flutter_controller_->engine()->SetNextFrameCallback([&]() { + this->Show(); + }); + + // Flutter can complete the first frame before the "show window" callback is + // registered. The following call ensures a frame is pending to ensure the + // window is shown. It is a no-op if the first frame hasn't completed yet. + flutter_controller_->ForceRedraw(); + + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/windows/runner/flutter_window.h b/windows/runner/flutter_window.h new file mode 100644 index 0000000..6da0652 --- /dev/null +++ b/windows/runner/flutter_window.h @@ -0,0 +1,33 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/windows/runner/main.cpp b/windows/runner/main.cpp new file mode 100644 index 0000000..5cc5154 --- /dev/null +++ b/windows/runner/main.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.Create(L"debartis", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/windows/runner/resource.h b/windows/runner/resource.h new file mode 100644 index 0000000..66a65d1 --- /dev/null +++ b/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/windows/runner/resources/app_icon.ico b/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..d100ab4fe76deab2b7c01f0d51732898d07a6502 GIT binary patch literal 1419 zcmV;61$6oV0096205C8B0000W0Cfcb02TlM0EtjeM-2)Z3IG5A4M|8uQUCw|FaQ7m zFbD3iU3SYZG=)$Bqa~2|;Db#SX|*OL zeethR^u>fm6CxU8G%?}b7wwBr#>B>lMyn=GOo}!Utv`uB(Q0F9Y0C=LSYTm)*age7 z!|d#gnOWGmcV~8YX4%4W!-j7zXU;uy=iKjpdl6dS9FGt}{2&CTruCKRC)&pWe)If9 z|1t3S-@>~1K%t8!tZ`gBezHLZp<43o!yDqYCO9Q0HZZpbPYU{tnB*VecG{-4F)JP$ zZ@?}|z1W7v3ubWr+lIyqrc_pUF)O|pBUB>UYTJZ@za!8P#;VwkMCuU$d|7-+3vGvO zH1QpKPv>L&0YOM(A^vz-{<_LS0skcEw#z?(g~StO@3qWA+m&rHVmm^O7Yp4Pv9lsS zW1;QNHu2c2Hfvd=5?m{H`OvTBmqbvk{!Oq8@#C+l@qpQNbS}|uEOvkmum)@l8 z@C(~*n{u0!9e&F!#1TMItFr`80FR5Kv>AVw6hKx$;aTzJ_&JW`H5K)nve3wp-FT@l zgd2+zj-QTTW=VGRTKl^jaB^Tzp<7i|Y}oXz^C_IXaLbWxyaK_8PlxbOhZ|o{uVHv} z89CK>ekKbw`*^%^D5MYAy~l%hkF{bvBGS`k3bQ%jo$phyYU=V5l-L6Jt{&<}L;Ugk z{kSv8n@OqGwu?t!r$>*EcHM#AjtqYOS1M>`ve2rmVntFB@Z_&A&58JGGR+P^k3+0q z>qg6kuR)!gH6DPlw)u5t4gM%sq@8^O`TF_Hz(5ghDvV{B>-*W)sSG1WpB zZi=`t6RkemP)!M6g>qwBo4QS9vrjH(@ag4@CB~G69_?ztiw`zpDxSrO?~)K@)$+#D zv2o3H^?AL2`1=o^iS2b zO$GhsIIZ}l9PsiN|CO$Vip8`w3K+OAfXSGIvGA(%+GdmDt@!uvb>UE_8{hmbVIr1e z@JtpGc!D#7Z3W-IaW;-~*VAQZU0vTl3_u0p)cqcpRy!Fxv$4k+7;Pi_Y#^v>8`|%~ zky9zm-BPTxP?L{Zv;c;B8`0nGqv*`##G0())w9XMz<)-v82WtC5+9Ds)HT6ty?)%& zOiM7&K$Dl##cR#$BIw#JU~VN_Kr>k=wvxkGIE|y-4Mt-#%Q-zOl@m}bzM4&{Qk)W1 z^q*SNtrT9AAOCBB|*L-z}%W`3u{#v7nI>)keV Z{6AVlTAAap$l(A0002ovPDHLkV1fa!vwQ#m literal 0 HcmV?d00001 diff --git a/windows/runner/runner.exe.manifest b/windows/runner/runner.exe.manifest new file mode 100644 index 0000000..153653e --- /dev/null +++ b/windows/runner/runner.exe.manifest @@ -0,0 +1,14 @@ + + + + + PerMonitorV2 + + + + + + + + + diff --git a/windows/runner/utils.cpp b/windows/runner/utils.cpp new file mode 100644 index 0000000..3a0b465 --- /dev/null +++ b/windows/runner/utils.cpp @@ -0,0 +1,65 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + unsigned int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr) + -1; // remove the trailing null character + int input_length = (int)wcslen(utf16_string); + std::string utf8_string; + if (target_length == 0 || target_length > utf8_string.max_size()) { + return utf8_string; + } + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + input_length, utf8_string.data(), target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/windows/runner/utils.h b/windows/runner/utils.h new file mode 100644 index 0000000..3879d54 --- /dev/null +++ b/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/windows/runner/win32_window.cpp b/windows/runner/win32_window.cpp new file mode 100644 index 0000000..60608d0 --- /dev/null +++ b/windows/runner/win32_window.cpp @@ -0,0 +1,288 @@ +#include "win32_window.h" + +#include +#include + +#include "resource.h" + +namespace { + +/// Window attribute that enables dark mode window decorations. +/// +/// Redefined in case the developer's machine has a Windows SDK older than +/// version 10.0.22000.0. +/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +/// Registry key for app theme preference. +/// +/// A value of 0 indicates apps should use dark mode. A non-zero or missing +/// value indicates apps should use light mode. +constexpr const wchar_t kGetPreferredBrightnessRegKey[] = + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; +constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + } + FreeLibrary(user32_module); +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registrar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::Create(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + UpdateTheme(window); + + return OnCreate(); +} + +bool Win32Window::Show() { + return ShowWindow(window_handle_, SW_SHOWNORMAL); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + + case WM_DWMCOLORIZATIONCOLORCHANGED: + UpdateTheme(hwnd); + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} + +void Win32Window::UpdateTheme(HWND const window) { + DWORD light_mode; + DWORD light_mode_size = sizeof(light_mode); + LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, + kGetPreferredBrightnessRegValue, + RRF_RT_REG_DWORD, nullptr, &light_mode, + &light_mode_size); + + if (result == ERROR_SUCCESS) { + BOOL enable_dark_mode = light_mode == 0; + DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, + &enable_dark_mode, sizeof(enable_dark_mode)); + } +} diff --git a/windows/runner/win32_window.h b/windows/runner/win32_window.h new file mode 100644 index 0000000..e901dde --- /dev/null +++ b/windows/runner/win32_window.h @@ -0,0 +1,102 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates a win32 window with |title| that is positioned and sized using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size this function will scale the inputted width and height as + // as appropriate for the default monitor. The window is invisible until + // |Show| is called. Returns true if the window was created successfully. + bool Create(const std::wstring& title, const Point& origin, const Size& size); + + // Show the current window. Returns true if the window was successfully shown. + bool Show(); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + // Update the window frame's theme to match the system theme. + static void UpdateTheme(HWND const window); + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_