From 4634d3ae8654cc1951ce5d88a29343b7fe6bc8a6 Mon Sep 17 00:00:00 2001 From: Josh Ashton <55456786+quaxlyqueen@users.noreply.github.com> Date: Sat, 8 Jun 2024 17:07:07 -0600 Subject: [PATCH 01/24] Update README.md --- README.md | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5214483..0f19215 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,20 @@ -Dependencies: +

One AI

+ +

Description:

+End-to-end solution for self-deploying a secure and private AI. Ideally, always using the latest and greatest open-source technologies available and optimizing the connection between them. + +

Dependencies:

+ +``` pip install scipy pydub torch torchaudio wavio silero sounddevice; +``` -Create output.txt file, TODO: Implement error handling. -touch output.txt; +

Usage:

-Currently, you have 5 seconds to record a prompt. It will automatically place "in 50 words or less" at the start of your prompt for faster testing. The Text To Speech (TTS) engine is not great quality, at least at first. Just getting the basic structure together, more to come... +

Currently, you have 5 seconds to record a prompt. It will automatically place "in 50 words or less" at the start of your prompt for faster testing. The Text To Speech (TTS) engine is not great quality, at least at first. Just getting the basic structure together, more to come...

+ +

TODO:

+ +- [ ] Installation script, preferably need some kind of pipeline to auto-inject dependencies into the install script. +- [ ] Send and receive text files over USB +- [ ] GUI in flutter From 3c0e777fd602d5aaaf704e75e37e07f876cb3251 Mon Sep 17 00:00:00 2001 From: Ashton Date: Fri, 5 Jul 2024 21:10:43 -0600 Subject: [PATCH 02/24] created and imported basic GUI from flutter flow --- mobile/app/.gitignore | 43 + mobile/app/.metadata | 45 + mobile/app/README.md | 16 + mobile/app/analysis_options.yaml | 28 + mobile/app/android/.gitignore | 13 + mobile/app/android/app/build.gradle | 58 + .../android/app/src/debug/AndroidManifest.xml | 7 + .../android/app/src/main/AndroidManifest.xml | 45 + .../kotlin/com/example/app/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 .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values-night/styles.xml | 18 + .../app/src/main/res/values/styles.xml | 18 + .../app/src/profile/AndroidManifest.xml | 7 + mobile/app/android/build.gradle | 18 + mobile/app/android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 5 + mobile/app/android/settings.gradle | 25 + mobile/app/ios/.gitignore | 34 + mobile/app/ios/Flutter/AppFrameworkInfo.plist | 26 + mobile/app/ios/Flutter/Debug.xcconfig | 1 + mobile/app/ios/Flutter/Release.xcconfig | 1 + .../app/ios/Runner.xcodeproj/project.pbxproj | 616 ++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 98 + .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + mobile/app/ios/Runner/AppDelegate.swift | 13 + .../AppIcon.appiconset/Contents.json | 122 ++ .../Icon-App-1024x1024@1x.png | Bin 0 -> 10932 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 295 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 450 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 282 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 462 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 704 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 586 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 1674 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 762 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 1226 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 1418 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 + .../Runner/Base.lproj/LaunchScreen.storyboard | 37 + .../app/ios/Runner/Base.lproj/Main.storyboard | 26 + mobile/app/ios/Runner/Info.plist | 49 + .../app/ios/Runner/Runner-Bridging-Header.h | 1 + mobile/app/ios/RunnerTests/RunnerTests.swift | 12 + .../components/ConversationBubblesModel.dart | 0 .../components/ConversationBubblesWidget.dart | 0 .../ConversationDetailsOverlayModel.dart | 30 + .../ConversationDetailsOverlayWidget.dart | 225 ++ .../components/ConversationOptionsModel.dart | 15 + .../components/ConversationOptionsWidget.dart | 254 +++ .../components/ConversationThreadModel.dart | 36 + .../components/ConversationThreadWidget.dart | 191 ++ mobile/app/lib/components/ModelItemModel.dart | 19 + .../app/lib/components/ModelItemWidget.dart | 122 ++ mobile/app/lib/components/NoChatsModel.dart | 14 + mobile/app/lib/components/NoChatsWidget.dart | 85 + .../lib/components/OptionsDialogModel.dart | 21 + .../lib/components/OptionsDialogWidget.dart | 210 ++ mobile/app/lib/components/PopupModel.dart | 16 + mobile/app/lib/components/PopupWidget.dart | 210 ++ mobile/app/lib/components/PromptBoxModel.dart | 26 + .../app/lib/components/PromptBoxWidget.dart | 244 +++ mobile/app/lib/main.dart | 125 ++ mobile/app/lib/pages/ConversationModel.dart | 35 + mobile/app/lib/pages/ConversationWidget.dart | 131 ++ .../app/lib/pages/ConversationsListModel.dart | 22 + .../lib/pages/ConversationsListWidget.dart | 252 +++ mobile/app/lib/pages/ImageDetailsModel.dart | 28 + mobile/app/lib/pages/ImageDetailsWidget.dart | 264 +++ mobile/app/lib/pages/SettingsModel.dart | 21 + mobile/app/lib/pages/SettingsWidget.dart | 1809 +++++++++++++++++ mobile/app/lib/pages/SetupModel.dart | 25 + mobile/app/lib/pages/SetupWidget.dart | 325 +++ mobile/app/lib/pages/SupportFormModel.dart | 46 + mobile/app/lib/pages/SupportFormWidget.dart | 576 ++++++ mobile/app/linux/.gitignore | 1 + mobile/app/linux/CMakeLists.txt | 145 ++ mobile/app/linux/flutter/CMakeLists.txt | 88 + .../flutter/generated_plugin_registrant.cc | 11 + .../flutter/generated_plugin_registrant.h | 15 + .../app/linux/flutter/generated_plugins.cmake | 23 + mobile/app/linux/main.cc | 6 + mobile/app/linux/my_application.cc | 124 ++ mobile/app/linux/my_application.h | 18 + mobile/app/macos/.gitignore | 7 + .../app/macos/Flutter/Flutter-Debug.xcconfig | 1 + .../macos/Flutter/Flutter-Release.xcconfig | 1 + .../Flutter/GeneratedPluginRegistrant.swift | 10 + .../macos/Runner.xcodeproj/project.pbxproj | 705 +++++++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 98 + .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + mobile/app/macos/Runner/AppDelegate.swift | 9 + .../AppIcon.appiconset/Contents.json | 68 + .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 102994 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 5680 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 520 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 14142 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1066 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 36406 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 2218 bytes .../app/macos/Runner/Base.lproj/MainMenu.xib | 343 ++++ .../app/macos/Runner/Configs/AppInfo.xcconfig | 14 + .../app/macos/Runner/Configs/Debug.xcconfig | 2 + .../app/macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 12 + mobile/app/macos/Runner/Info.plist | 32 + .../app/macos/Runner/MainFlutterWindow.swift | 15 + mobile/app/macos/Runner/Release.entitlements | 8 + .../app/macos/RunnerTests/RunnerTests.swift | 12 + mobile/app/pubspec.lock | 213 ++ mobile/app/pubspec.yaml | 90 + mobile/app/test/widget_test.dart | 30 + mobile/app/web/favicon.png | Bin 0 -> 917 bytes mobile/app/web/icons/Icon-192.png | Bin 0 -> 5292 bytes mobile/app/web/icons/Icon-512.png | Bin 0 -> 8252 bytes mobile/app/web/icons/Icon-maskable-192.png | Bin 0 -> 5594 bytes mobile/app/web/icons/Icon-maskable-512.png | Bin 0 -> 20998 bytes mobile/app/web/index.html | 38 + mobile/app/web/manifest.json | 35 + mobile/app/windows/.gitignore | 17 + mobile/app/windows/CMakeLists.txt | 108 + mobile/app/windows/flutter/CMakeLists.txt | 109 + .../flutter/generated_plugin_registrant.cc | 11 + .../flutter/generated_plugin_registrant.h | 15 + .../windows/flutter/generated_plugins.cmake | 23 + mobile/app/windows/runner/CMakeLists.txt | 40 + mobile/app/windows/runner/Runner.rc | 121 ++ mobile/app/windows/runner/flutter_window.cpp | 71 + mobile/app/windows/runner/flutter_window.h | 33 + mobile/app/windows/runner/main.cpp | 43 + mobile/app/windows/runner/resource.h | 16 + .../app/windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes mobile/app/windows/runner/runner.exe.manifest | 20 + mobile/app/windows/runner/utils.cpp | 65 + mobile/app/windows/runner/utils.h | 19 + mobile/app/windows/runner/win32_window.cpp | 288 +++ mobile/app/windows/runner/win32_window.h | 102 + requirements.txt => server/requirements.txt | 0 stt.py => server/stt.py | 0 160 files changed, 10178 insertions(+) create mode 100644 mobile/app/.gitignore create mode 100644 mobile/app/.metadata create mode 100644 mobile/app/README.md create mode 100644 mobile/app/analysis_options.yaml create mode 100644 mobile/app/android/.gitignore create mode 100644 mobile/app/android/app/build.gradle create mode 100644 mobile/app/android/app/src/debug/AndroidManifest.xml create mode 100644 mobile/app/android/app/src/main/AndroidManifest.xml create mode 100644 mobile/app/android/app/src/main/kotlin/com/example/app/MainActivity.kt create mode 100644 mobile/app/android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 mobile/app/android/app/src/main/res/drawable/launch_background.xml create mode 100644 mobile/app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 mobile/app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 mobile/app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 mobile/app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 mobile/app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 mobile/app/android/app/src/main/res/values-night/styles.xml create mode 100644 mobile/app/android/app/src/main/res/values/styles.xml create mode 100644 mobile/app/android/app/src/profile/AndroidManifest.xml create mode 100644 mobile/app/android/build.gradle create mode 100644 mobile/app/android/gradle.properties create mode 100644 mobile/app/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 mobile/app/android/settings.gradle create mode 100644 mobile/app/ios/.gitignore create mode 100644 mobile/app/ios/Flutter/AppFrameworkInfo.plist create mode 100644 mobile/app/ios/Flutter/Debug.xcconfig create mode 100644 mobile/app/ios/Flutter/Release.xcconfig create mode 100644 mobile/app/ios/Runner.xcodeproj/project.pbxproj create mode 100644 mobile/app/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 mobile/app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 mobile/app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 mobile/app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 mobile/app/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 mobile/app/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 mobile/app/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 mobile/app/ios/Runner/AppDelegate.swift create mode 100644 mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png create mode 100644 mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 mobile/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 mobile/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 mobile/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 mobile/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 mobile/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md create mode 100644 mobile/app/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 mobile/app/ios/Runner/Base.lproj/Main.storyboard create mode 100644 mobile/app/ios/Runner/Info.plist create mode 100644 mobile/app/ios/Runner/Runner-Bridging-Header.h create mode 100644 mobile/app/ios/RunnerTests/RunnerTests.swift create mode 100644 mobile/app/lib/components/ConversationBubblesModel.dart create mode 100644 mobile/app/lib/components/ConversationBubblesWidget.dart create mode 100644 mobile/app/lib/components/ConversationDetailsOverlayModel.dart create mode 100644 mobile/app/lib/components/ConversationDetailsOverlayWidget.dart create mode 100644 mobile/app/lib/components/ConversationOptionsModel.dart create mode 100644 mobile/app/lib/components/ConversationOptionsWidget.dart create mode 100644 mobile/app/lib/components/ConversationThreadModel.dart create mode 100644 mobile/app/lib/components/ConversationThreadWidget.dart create mode 100644 mobile/app/lib/components/ModelItemModel.dart create mode 100644 mobile/app/lib/components/ModelItemWidget.dart create mode 100644 mobile/app/lib/components/NoChatsModel.dart create mode 100644 mobile/app/lib/components/NoChatsWidget.dart create mode 100644 mobile/app/lib/components/OptionsDialogModel.dart create mode 100644 mobile/app/lib/components/OptionsDialogWidget.dart create mode 100644 mobile/app/lib/components/PopupModel.dart create mode 100644 mobile/app/lib/components/PopupWidget.dart create mode 100644 mobile/app/lib/components/PromptBoxModel.dart create mode 100644 mobile/app/lib/components/PromptBoxWidget.dart create mode 100644 mobile/app/lib/main.dart create mode 100644 mobile/app/lib/pages/ConversationModel.dart create mode 100644 mobile/app/lib/pages/ConversationWidget.dart create mode 100644 mobile/app/lib/pages/ConversationsListModel.dart create mode 100644 mobile/app/lib/pages/ConversationsListWidget.dart create mode 100644 mobile/app/lib/pages/ImageDetailsModel.dart create mode 100644 mobile/app/lib/pages/ImageDetailsWidget.dart create mode 100644 mobile/app/lib/pages/SettingsModel.dart create mode 100644 mobile/app/lib/pages/SettingsWidget.dart create mode 100644 mobile/app/lib/pages/SetupModel.dart create mode 100644 mobile/app/lib/pages/SetupWidget.dart create mode 100644 mobile/app/lib/pages/SupportFormModel.dart create mode 100644 mobile/app/lib/pages/SupportFormWidget.dart create mode 100644 mobile/app/linux/.gitignore create mode 100644 mobile/app/linux/CMakeLists.txt create mode 100644 mobile/app/linux/flutter/CMakeLists.txt create mode 100644 mobile/app/linux/flutter/generated_plugin_registrant.cc create mode 100644 mobile/app/linux/flutter/generated_plugin_registrant.h create mode 100644 mobile/app/linux/flutter/generated_plugins.cmake create mode 100644 mobile/app/linux/main.cc create mode 100644 mobile/app/linux/my_application.cc create mode 100644 mobile/app/linux/my_application.h create mode 100644 mobile/app/macos/.gitignore create mode 100644 mobile/app/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 mobile/app/macos/Flutter/Flutter-Release.xcconfig create mode 100644 mobile/app/macos/Flutter/GeneratedPluginRegistrant.swift create mode 100644 mobile/app/macos/Runner.xcodeproj/project.pbxproj create mode 100644 mobile/app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 mobile/app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 mobile/app/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 mobile/app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 mobile/app/macos/Runner/AppDelegate.swift create mode 100644 mobile/app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 mobile/app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 mobile/app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 mobile/app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 mobile/app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 mobile/app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 mobile/app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 mobile/app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 mobile/app/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 mobile/app/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 mobile/app/macos/Runner/Configs/Debug.xcconfig create mode 100644 mobile/app/macos/Runner/Configs/Release.xcconfig create mode 100644 mobile/app/macos/Runner/Configs/Warnings.xcconfig create mode 100644 mobile/app/macos/Runner/DebugProfile.entitlements create mode 100644 mobile/app/macos/Runner/Info.plist create mode 100644 mobile/app/macos/Runner/MainFlutterWindow.swift create mode 100644 mobile/app/macos/Runner/Release.entitlements create mode 100644 mobile/app/macos/RunnerTests/RunnerTests.swift create mode 100644 mobile/app/pubspec.lock create mode 100644 mobile/app/pubspec.yaml create mode 100644 mobile/app/test/widget_test.dart create mode 100644 mobile/app/web/favicon.png create mode 100644 mobile/app/web/icons/Icon-192.png create mode 100644 mobile/app/web/icons/Icon-512.png create mode 100644 mobile/app/web/icons/Icon-maskable-192.png create mode 100644 mobile/app/web/icons/Icon-maskable-512.png create mode 100644 mobile/app/web/index.html create mode 100644 mobile/app/web/manifest.json create mode 100644 mobile/app/windows/.gitignore create mode 100644 mobile/app/windows/CMakeLists.txt create mode 100644 mobile/app/windows/flutter/CMakeLists.txt create mode 100644 mobile/app/windows/flutter/generated_plugin_registrant.cc create mode 100644 mobile/app/windows/flutter/generated_plugin_registrant.h create mode 100644 mobile/app/windows/flutter/generated_plugins.cmake create mode 100644 mobile/app/windows/runner/CMakeLists.txt create mode 100644 mobile/app/windows/runner/Runner.rc create mode 100644 mobile/app/windows/runner/flutter_window.cpp create mode 100644 mobile/app/windows/runner/flutter_window.h create mode 100644 mobile/app/windows/runner/main.cpp create mode 100644 mobile/app/windows/runner/resource.h create mode 100644 mobile/app/windows/runner/resources/app_icon.ico create mode 100644 mobile/app/windows/runner/runner.exe.manifest create mode 100644 mobile/app/windows/runner/utils.cpp create mode 100644 mobile/app/windows/runner/utils.h create mode 100644 mobile/app/windows/runner/win32_window.cpp create mode 100644 mobile/app/windows/runner/win32_window.h rename requirements.txt => server/requirements.txt (100%) rename stt.py => server/stt.py (100%) diff --git a/mobile/app/.gitignore b/mobile/app/.gitignore new file mode 100644 index 0000000..29a3a50 --- /dev/null +++ b/mobile/app/.gitignore @@ -0,0 +1,43 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +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/mobile/app/.metadata b/mobile/app/.metadata new file mode 100644 index 0000000..6eb54a1 --- /dev/null +++ b/mobile/app/.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: "761747bfc538b5af34aa0d3fac380f1bc331ec49" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: android + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: ios + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: linux + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: macos + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: web + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: windows + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + + # 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/mobile/app/README.md b/mobile/app/README.md new file mode 100644 index 0000000..b73cfb0 --- /dev/null +++ b/mobile/app/README.md @@ -0,0 +1,16 @@ +# app + +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/mobile/app/analysis_options.yaml b/mobile/app/analysis_options.yaml new file mode 100644 index 0000000..0d29021 --- /dev/null +++ b/mobile/app/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/mobile/app/android/.gitignore b/mobile/app/android/.gitignore new file mode 100644 index 0000000..6f56801 --- /dev/null +++ b/mobile/app/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/mobile/app/android/app/build.gradle b/mobile/app/android/app/build.gradle new file mode 100644 index 0000000..bf2cdd3 --- /dev/null +++ b/mobile/app/android/app/build.gradle @@ -0,0 +1,58 @@ +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" +} + +def localProperties = new Properties() +def localPropertiesFile = rootProject.file("local.properties") +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader("UTF-8") { reader -> + localProperties.load(reader) + } +} + +def flutterVersionCode = localProperties.getProperty("flutter.versionCode") +if (flutterVersionCode == null) { + flutterVersionCode = "1" +} + +def flutterVersionName = localProperties.getProperty("flutter.versionName") +if (flutterVersionName == null) { + flutterVersionName = "1.0" +} + +android { + namespace = "com.example.app" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "com.example.app" + // You can update the following values to match your application needs. + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. + minSdk = flutter.minSdkVersion + targetSdk = flutter.targetSdkVersion + versionCode = flutterVersionCode.toInteger() + versionName = flutterVersionName + } + + 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.debug + } + } +} + +flutter { + source = "../.." +} diff --git a/mobile/app/android/app/src/debug/AndroidManifest.xml b/mobile/app/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/mobile/app/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/mobile/app/android/app/src/main/AndroidManifest.xml b/mobile/app/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..6ee3df0 --- /dev/null +++ b/mobile/app/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/app/android/app/src/main/kotlin/com/example/app/MainActivity.kt b/mobile/app/android/app/src/main/kotlin/com/example/app/MainActivity.kt new file mode 100644 index 0000000..026d9a9 --- /dev/null +++ b/mobile/app/android/app/src/main/kotlin/com/example/app/MainActivity.kt @@ -0,0 +1,5 @@ +package com.example.app + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() diff --git a/mobile/app/android/app/src/main/res/drawable-v21/launch_background.xml b/mobile/app/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/mobile/app/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/mobile/app/android/app/src/main/res/drawable/launch_background.xml b/mobile/app/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/mobile/app/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/mobile/app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/mobile/app/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/mobile/app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/mobile/app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 diff --git a/mobile/app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/mobile/app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 diff --git a/mobile/app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/mobile/app/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/mobile/app/android/app/src/main/res/values-night/styles.xml b/mobile/app/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..06952be --- /dev/null +++ b/mobile/app/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/mobile/app/android/app/src/main/res/values/styles.xml b/mobile/app/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..cb1ef88 --- /dev/null +++ b/mobile/app/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/mobile/app/android/app/src/profile/AndroidManifest.xml b/mobile/app/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/mobile/app/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/mobile/app/android/build.gradle b/mobile/app/android/build.gradle new file mode 100644 index 0000000..d2ffbff --- /dev/null +++ b/mobile/app/android/build.gradle @@ -0,0 +1,18 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = "../build" +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean", Delete) { + delete rootProject.buildDir +} diff --git a/mobile/app/android/gradle.properties b/mobile/app/android/gradle.properties new file mode 100644 index 0000000..3b5b324 --- /dev/null +++ b/mobile/app/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError +android.useAndroidX=true +android.enableJetifier=true diff --git a/mobile/app/android/gradle/wrapper/gradle-wrapper.properties b/mobile/app/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..e1ca574 --- /dev/null +++ b/mobile/app/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-7.6.3-all.zip diff --git a/mobile/app/android/settings.gradle b/mobile/app/android/settings.gradle new file mode 100644 index 0000000..536165d --- /dev/null +++ b/mobile/app/android/settings.gradle @@ -0,0 +1,25 @@ +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() + + 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 "7.3.0" apply false + id "org.jetbrains.kotlin.android" version "1.7.10" apply false +} + +include ":app" diff --git a/mobile/app/ios/.gitignore b/mobile/app/ios/.gitignore new file mode 100644 index 0000000..7a7f987 --- /dev/null +++ b/mobile/app/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/mobile/app/ios/Flutter/AppFrameworkInfo.plist b/mobile/app/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000..7c56964 --- /dev/null +++ b/mobile/app/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/mobile/app/ios/Flutter/Debug.xcconfig b/mobile/app/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..592ceee --- /dev/null +++ b/mobile/app/ios/Flutter/Debug.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/mobile/app/ios/Flutter/Release.xcconfig b/mobile/app/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000..592ceee --- /dev/null +++ b/mobile/app/ios/Flutter/Release.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/mobile/app/ios/Runner.xcodeproj/project.pbxproj b/mobile/app/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..8aaafb2 --- /dev/null +++ b/mobile/app/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.app; + 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.app.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.app.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.app.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 = 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; + 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 = 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; + 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.app; + 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.app; + 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/mobile/app/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/mobile/app/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/mobile/app/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/mobile/app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/mobile/app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/mobile/app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/mobile/app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/mobile/app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/mobile/app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/mobile/app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/mobile/app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..8e3ca5d --- /dev/null +++ b/mobile/app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/app/ios/Runner.xcworkspace/contents.xcworkspacedata b/mobile/app/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..1d526a1 --- /dev/null +++ b/mobile/app/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/mobile/app/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/mobile/app/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/mobile/app/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/mobile/app/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/mobile/app/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/mobile/app/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/mobile/app/ios/Runner/AppDelegate.swift b/mobile/app/ios/Runner/AppDelegate.swift new file mode 100644 index 0000000..9074fee --- /dev/null +++ b/mobile/app/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import Flutter +import UIKit + +@UIApplicationMain +@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/mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d36b1fa --- /dev/null +++ b/mobile/app/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/mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..dc9ada4725e9b0ddb1deab583e5b5102493aa332 GIT binary patch literal 10932 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 literal 0 HcmV?d00001 diff --git a/mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..797d452e458972bab9d994556c8305db4c827017 GIT binary patch literal 406 zcmV;H0crk;P))>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c literal 0 HcmV?d00001 diff --git a/mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..6ed2d933e1120817fe9182483a228007b18ab6ae GIT binary patch literal 450 zcmV;z0X_bSP)iGWQ_5NJQ_~rNh*z)}eT%KUb z`7gNk0#AwF^#0T0?hIa^`~Ck;!}#m+_uT050aTR(J!bU#|IzRL%^UsMS#KsYnTF*!YeDOytlP4VhV?b} z%rz_<=#CPc)tU1MZTq~*2=8~iZ!lSa<{9b@2Jl;?IEV8)=fG217*|@)CCYgFze-x? zIFODUIA>nWKpE+bn~n7;-89sa>#DR>TSlqWk*!2hSN6D~Qb#VqbP~4Fk&m`@1$JGr zXPIdeRE&b2Thd#{MtDK$px*d3-Wx``>!oimf%|A-&-q*6KAH)e$3|6JV%HX{Hig)k suLT-RhftRq8b9;(V=235Wa|I=027H2wCDra;{X5v07*qoM6N<$f;9x^2LJ#7 literal 0 HcmV?d00001 diff --git a/mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..4cd7b0099ca80c806f8fe495613e8d6c69460d76 GIT binary patch literal 282 zcmV+#0p(^bcu7P-R4C8Q z&e;xxFbF_Vrezo%_kH*OKhshZ6BFpG-Y1e10`QXJKbND7AMQ&cMj60B5TNObaZxYybcN07*qoM6N<$g3m;S%K!iX literal 0 HcmV?d00001 diff --git a/mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..fe730945a01f64a61e2235dbe3f45b08f7729182 GIT binary patch literal 462 zcmV;<0WtoGP)-}iV`2<;=$?g5M=KQbZ{F&YRNy7Nn@%_*5{gvDM0aKI4?ESmw z{NnZg)A0R`+4?NF_RZexyVB&^^ZvN!{I28tr{Vje;QNTz`dG&Jz0~Ek&f2;*Z7>B|cg}xYpxEFY+0YrKLF;^Q+-HreN0P{&i zK~zY`?b7ECf-n?@;d<&orQ*Q7KoR%4|C>{W^h6@&01>0SKS`dn{Q}GT%Qj_{PLZ_& zs`MFI#j-(>?bvdZ!8^xTwlY{qA)T4QLbY@j(!YJ7aXJervHy6HaG_2SB`6CC{He}f zHVw(fJWApwPq!6VY7r1w-Fs)@ox~N+q|w~e;JI~C4Vf^@d>Wvj=fl`^u9x9wd9 zR%3*Q+)t%S!MU_`id^@&Y{y7-r98lZX0?YrHlfmwb?#}^1b{8g&KzmkE(L>Z&)179 zp<)v6Y}pRl100G2FL_t(o!|l{-Q-VMg#&MKg7c{O0 z2wJImOS3Gy*Z2Qifdv~JYOp;v+U)a|nLoc7hNH;I$;lzDt$}rkaFw1mYK5_0Q(Sut zvbEloxON7$+HSOgC9Z8ltuC&0OSF!-mXv5caV>#bc3@hBPX@I$58-z}(ZZE!t-aOG zpjNkbau@>yEzH(5Yj4kZiMH32XI!4~gVXNnjAvRx;Sdg^`>2DpUEwoMhTs_st8pKG z(%SHyHdU&v%f36~uERh!bd`!T2dw;z6PrOTQ7Vt*#9F2uHlUVnb#ev_o^fh}Dzmq} zWtlk35}k=?xj28uO|5>>$yXadTUE@@IPpgH`gJ~Ro4>jd1IF|(+IX>8M4Ps{PNvmI zNj4D+XgN83gPt_Gm}`Ybv{;+&yu-C(Grdiahmo~BjG-l&mWM+{e5M1sm&=xduwgM9 z`8OEh`=F3r`^E{n_;%9weN{cf2%7=VzC@cYj+lg>+3|D|_1C@{hcU(DyQG_BvBWe? zvTv``=%b1zrol#=R`JB)>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c literal 0 HcmV?d00001 diff --git a/mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..502f463a9bc882b461c96aadf492d1729e49e725 GIT binary patch literal 586 zcmV-Q0=4~#P)+}#`wDE{8-2Mebf5<{{PqV{TgVcv*r8?UZ3{-|G?_}T*&y;@cqf{ z{Q*~+qr%%p!1pS*_Uicl#q9lc(D`!D`LN62sNwq{oYw(Wmhk)k<@f$!$@ng~_5)Ru z0Z)trIA5^j{DIW^c+vT2%lW+2<(RtE2wR;4O@)Tm`Xr*?A(qYoM}7i5Yxw>D(&6ou zxz!_Xr~yNF+waPe00049Nkl*;a!v6h%{rlvIH#gW3s8p;bFr=l}mRqpW2h zw=OA%hdyL~z+UHOzl0eKhEr$YYOL-c-%Y<)=j?(bzDweB7{b+%_ypvm_cG{SvM=DK zhv{K@m>#Bw>2W$eUI#iU)Wdgs8Y3U+A$Gd&{+j)d)BmGKx+43U_!tik_YlN)>$7G! zhkE!s;%oku3;IwG3U^2kw?z+HM)jB{@zFhK8P#KMSytSthr+4!c(5c%+^UBn`0X*2 zy3(k600_CSZj?O$Qu%&$;|TGUJrptR(HzyIx>5E(2r{eA(<6t3e3I0B)7d6s7?Z5J zZ!rtKvA{MiEBm&KFtoifx>5P^Z=vl)95XJn()aS5%ad(s?4-=Tkis9IGu{`Fy8r+H07*qoM6N<$f20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU literal 0 HcmV?d00001 diff --git a/mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0ec303439225b78712f49115768196d8d76f6790 GIT binary patch literal 862 zcmV-k1EKthP)20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU literal 0 HcmV?d00001 diff --git a/mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..e9f5fea27c705180eb716271f41b582e76dcbd90 GIT binary patch literal 1674 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 literal 0 HcmV?d00001 diff --git a/mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/mobile/app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0467bf12aa4d28f374bb26596605a46dcbb3e7c8 GIT binary patch literal 1418 zcmV;51$Fv~P)q zKfU)WzW*n(@|xWGCA9ScMt*e9`2kdxPQ&&>|-UCa7_51w+ zLUsW@ZzZSW0y$)Hp~e9%PvP|a03ks1`~K?q{u;6NC8*{AOqIUq{CL&;p56Lf$oQGq z^={4hPQv)y=I|4n+?>7Fim=dxt1 z2H+Dm+1+fh+IF>G0SjJMkQQre1x4|G*Z==(Ot&kCnUrL4I(rf(ucITwmuHf^hXiJT zkdTm&kdTm&kdTm&kdP`esgWG0BcWCVkVZ&2dUwN`cgM8QJb`Z7Z~e<&Yj2(}>Tmf` zm1{eLgw!b{bXkjWbF%dTkTZEJWyWOb##Lfw4EK2}<0d6%>AGS{po>WCOy&f$Tay_> z?NBlkpo@s-O;0V%Y_Xa-G#_O08q5LR*~F%&)}{}r&L%Sbs8AS4t7Y0NEx*{soY=0MZExqA5XHQkqi#4gW3 zqODM^iyZl;dvf)-bOXtOru(s)Uc7~BFx{w-FK;2{`VA?(g&@3z&bfLFyctOH!cVsF z7IL=fo-qBndRUm;kAdXR4e6>k-z|21AaN%ubeVrHl*<|s&Ax@W-t?LR(P-24A5=>a z*R9#QvjzF8n%@1Nw@?CG@6(%>+-0ASK~jEmCV|&a*7-GKT72W<(TbSjf)&Eme6nGE z>Gkj4Sq&2e+-G%|+NM8OOm5zVl9{Z8Dd8A5z3y8mZ=4Bv4%>as_{9cN#bm~;h>62( zdqY93Zy}v&c4n($Vv!UybR8ocs7#zbfX1IY-*w~)p}XyZ-SFC~4w>BvMVr`dFbelV{lLL0bx7@*ZZdebr3`sP;? zVImji)kG)(6Juv0lz@q`F!k1FE;CQ(D0iG$wchPbKZQELlsZ#~rt8#90Y_Xh&3U-< z{s<&cCV_1`^TD^ia9!*mQDq& zn2{r`j};V|uV%_wsP!zB?m%;FeaRe+X47K0e+KE!8C{gAWF8)lCd1u1%~|M!XNRvw zvtqy3iz0WSpWdhn6$hP8PaRBmp)q`#PCA`Vd#Tc$@f1tAcM>f_I@bC)hkI9|o(Iqv zo}Piadq!j76}004RBio<`)70k^`K1NK)q>w?p^C6J2ZC!+UppiK6&y3Kmbv&O!oYF z34$0Z;QO!JOY#!`qyGH<3Pd}Pt@q*A0V=3SVtWKRR8d8Z&@)3qLPA19LPA19LPEUC YUoZo%k(ykuW&i*H07*qoM6N<$f+CH{y8r+H literal 0 HcmV?d00001 diff --git a/mobile/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/mobile/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 0000000..0bedcf2 --- /dev/null +++ b/mobile/app/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/mobile/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/mobile/app/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/mobile/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/mobile/app/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/mobile/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/mobile/app/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/mobile/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/mobile/app/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000..89c2725 --- /dev/null +++ b/mobile/app/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/mobile/app/ios/Runner/Base.lproj/LaunchScreen.storyboard b/mobile/app/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..f2e259c --- /dev/null +++ b/mobile/app/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/app/ios/Runner/Base.lproj/Main.storyboard b/mobile/app/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f3c2851 --- /dev/null +++ b/mobile/app/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/app/ios/Runner/Info.plist b/mobile/app/ios/Runner/Info.plist new file mode 100644 index 0000000..c2e05c5 --- /dev/null +++ b/mobile/app/ios/Runner/Info.plist @@ -0,0 +1,49 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + App + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + app + 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/mobile/app/ios/Runner/Runner-Bridging-Header.h b/mobile/app/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..308a2a5 --- /dev/null +++ b/mobile/app/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/mobile/app/ios/RunnerTests/RunnerTests.swift b/mobile/app/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000..86a7c3b --- /dev/null +++ b/mobile/app/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/mobile/app/lib/components/ConversationBubblesModel.dart b/mobile/app/lib/components/ConversationBubblesModel.dart new file mode 100644 index 0000000..e69de29 diff --git a/mobile/app/lib/components/ConversationBubblesWidget.dart b/mobile/app/lib/components/ConversationBubblesWidget.dart new file mode 100644 index 0000000..e69de29 diff --git a/mobile/app/lib/components/ConversationDetailsOverlayModel.dart b/mobile/app/lib/components/ConversationDetailsOverlayModel.dart new file mode 100644 index 0000000..8767242 --- /dev/null +++ b/mobile/app/lib/components/ConversationDetailsOverlayModel.dart @@ -0,0 +1,30 @@ +import '/components/options_dialog/options_dialog_widget.dart'; +import '/flutter_flow/flutter_flow_icon_button.dart'; +import '/flutter_flow/flutter_flow_theme.dart'; +import '/flutter_flow/flutter_flow_util.dart'; +import '/flutter_flow/flutter_flow_widgets.dart'; +import 'dart:ui'; +import 'conversation_details_overlay_widget.dart' + show ConversationDetailsOverlayWidget; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:provider/provider.dart'; + +class ConversationDetailsOverlayModel + extends FlutterFlowModel { + /// State fields for stateful widgets in this component. + + // Model for OptionsDialog component. + late OptionsDialogModel optionsDialogModel; + + @override + void initState(BuildContext context) { + optionsDialogModel = createModel(context, () => OptionsDialogModel()); + } + + @override + void dispose() { + optionsDialogModel.dispose(); + } +} diff --git a/mobile/app/lib/components/ConversationDetailsOverlayWidget.dart b/mobile/app/lib/components/ConversationDetailsOverlayWidget.dart new file mode 100644 index 0000000..0386225 --- /dev/null +++ b/mobile/app/lib/components/ConversationDetailsOverlayWidget.dart @@ -0,0 +1,225 @@ +import '/components/options_dialog/options_dialog_widget.dart'; +import '/flutter_flow/flutter_flow_icon_button.dart'; +import '/flutter_flow/flutter_flow_theme.dart'; +import '/flutter_flow/flutter_flow_util.dart'; +import '/flutter_flow/flutter_flow_widgets.dart'; +import 'dart:ui'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:provider/provider.dart'; + +import 'conversation_details_overlay_model.dart'; +export 'conversation_details_overlay_model.dart'; + +class ConversationDetailsOverlayWidget extends StatefulWidget { + const ConversationDetailsOverlayWidget({super.key}); + + @override + State createState() => + _ConversationDetailsOverlayWidgetState(); +} + +class _ConversationDetailsOverlayWidgetState + extends State { + late ConversationDetailsOverlayModel _model; + + @override + void setState(VoidCallback callback) { + super.setState(callback); + _model.onUpdate(); + } + + @override + void initState() { + super.initState(); + _model = createModel(context, () => ConversationDetailsOverlayModel()); + } + + @override + void dispose() { + _model.maybeDispose(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return ClipRRect( + borderRadius: BorderRadius.circular(0), + child: BackdropFilter( + filter: ImageFilter.blur( + sigmaX: 4, + sigmaY: 4, + ), + child: Container( + width: double.infinity, + height: double.infinity, + decoration: BoxDecoration(), + child: Align( + alignment: AlignmentDirectional(0, -1), + child: Container( + width: double.infinity, + height: double.infinity, + constraints: BoxConstraints( + maxWidth: 700, + ), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(0), + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 70, 0, 0), + child: Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: EdgeInsetsDirectional.fromSTEB(16, 0, 0, 0), + child: Text( + 'Chat Details', + style: FlutterFlowTheme.of(context) + .headlineSmall + .override( + fontFamily: 'Outfit', + letterSpacing: 0, + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 0, 16, 0), + child: FlutterFlowIconButton( + borderColor: FlutterFlowTheme.of(context).alternate, + borderRadius: 12, + borderWidth: 1, + buttonSize: 40, + fillColor: FlutterFlowTheme.of(context).accent4, + icon: Icon( + Icons.close_rounded, + color: FlutterFlowTheme.of(context).primaryText, + size: 24, + ), + onPressed: () async { + Navigator.pop(context); + }, + ), + ), + ], + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB(16, 4, 0, 4), + child: RichText( + textScaler: MediaQuery.of(context).textScaler, + text: TextSpan( + children: [ + TextSpan( + text: 'Group Chat ID: ', + style: TextStyle(), + ), + TextSpan( + text: 'Hello World ', + style: TextStyle( + color: FlutterFlowTheme.of(context).primary, + fontWeight: FontWeight.bold, + ), + ) + ], + style: + FlutterFlowTheme.of(context).labelMedium.override( + fontFamily: 'Readex Pro', + letterSpacing: 0, + ), + ), + ), + ), + Expanded( + child: Align( + alignment: AlignmentDirectional(0, 0), + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Divider( + thickness: 1, + color: FlutterFlowTheme.of(context).tertiary, + ), + ], + ), + ), + ), + Align( + alignment: AlignmentDirectional(0, -1), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(16, 0, 16, 0), + child: ClipRRect( + borderRadius: BorderRadius.circular(12), + child: Container( + decoration: BoxDecoration( + color: FlutterFlowTheme.of(context) + .secondaryBackground, + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: FlutterFlowTheme.of(context).alternate, + ), + ), + child: wrapWithModel( + model: _model.optionsDialogModel, + updateCallback: () => setState(() {}), + updateOnChange: true, + child: OptionsDialogWidget(), + ), + ), + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB(16, 16, 16, 44), + child: FFButtonWidget( + onPressed: () async { + Navigator.pop(context); + }, + text: 'Close', + options: FFButtonOptions( + width: double.infinity, + height: 52, + padding: EdgeInsetsDirectional.fromSTEB(44, 0, 44, 0), + iconPadding: + EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), + color: + FlutterFlowTheme.of(context).secondaryBackground, + textStyle: + FlutterFlowTheme.of(context).titleLarge.override( + fontFamily: 'Outfit', + fontSize: 18, + letterSpacing: 0, + ), + elevation: 0, + borderSide: BorderSide( + color: FlutterFlowTheme.of(context).alternate, + width: 2, + ), + borderRadius: BorderRadius.circular(12), + hoverColor: FlutterFlowTheme.of(context).alternate, + hoverBorderSide: BorderSide( + color: FlutterFlowTheme.of(context).alternate, + width: 2, + ), + hoverTextColor: + FlutterFlowTheme.of(context).primaryText, + hoverElevation: 3, + ), + ), + ), + ], + ), + ), + ), + ), + ), + ), + ); + } +} diff --git a/mobile/app/lib/components/ConversationOptionsModel.dart b/mobile/app/lib/components/ConversationOptionsModel.dart new file mode 100644 index 0000000..42c8a86 --- /dev/null +++ b/mobile/app/lib/components/ConversationOptionsModel.dart @@ -0,0 +1,15 @@ +import '/flutter_flow/flutter_flow_theme.dart'; +import '/flutter_flow/flutter_flow_util.dart'; +import 'conversation_options_widget.dart' show ConversationOptionsWidget; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:provider/provider.dart'; + +class ConversationOptionsModel + extends FlutterFlowModel { + @override + void initState(BuildContext context) {} + + @override + void dispose() {} +} diff --git a/mobile/app/lib/components/ConversationOptionsWidget.dart b/mobile/app/lib/components/ConversationOptionsWidget.dart new file mode 100644 index 0000000..8390957 --- /dev/null +++ b/mobile/app/lib/components/ConversationOptionsWidget.dart @@ -0,0 +1,254 @@ +import '/flutter_flow/flutter_flow_theme.dart'; +import '/flutter_flow/flutter_flow_util.dart'; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:provider/provider.dart'; + +import 'conversation_options_model.dart'; +export 'conversation_options_model.dart'; + +class ConversationOptionsWidget extends StatefulWidget { + const ConversationOptionsWidget({super.key}); + + @override + State createState() => + _ConversationOptionsWidgetState(); +} + +class _ConversationOptionsWidgetState extends State { + late ConversationOptionsModel _model; + + @override + void setState(VoidCallback callback) { + super.setState(callback); + _model.onUpdate(); + } + + @override + void initState() { + super.initState(); + _model = createModel(context, () => ConversationOptionsModel()); + } + + @override + void dispose() { + _model.maybeDispose(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + decoration: BoxDecoration( + color: FlutterFlowTheme.of(context).secondaryBackground, + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 12, 0, 24), + child: Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: double.infinity, + height: 60, + decoration: BoxDecoration(), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(12, 8, 12, 8), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Card( + clipBehavior: Clip.antiAliasWithSaveLayer, + color: FlutterFlowTheme.of(context).primaryBackground, + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(40), + ), + child: Padding( + padding: EdgeInsets.all(8), + child: Icon( + Icons.share_rounded, + color: FlutterFlowTheme.of(context).secondaryText, + size: 20, + ), + ), + ), + Expanded( + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(12, 0, 0, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Share', + style: FlutterFlowTheme.of(context) + .labelLarge + .override( + fontFamily: 'Readex Pro', + letterSpacing: 0, + ), + ), + ], + ), + ), + ), + ], + ), + ), + ), + Container( + width: double.infinity, + height: 60, + decoration: BoxDecoration(), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(12, 8, 12, 8), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Card( + clipBehavior: Clip.antiAliasWithSaveLayer, + color: FlutterFlowTheme.of(context).primaryBackground, + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(40), + ), + child: Padding( + padding: EdgeInsets.all(8), + child: Icon( + Icons.insert_link, + color: FlutterFlowTheme.of(context).secondaryText, + size: 20, + ), + ), + ), + Expanded( + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(12, 0, 0, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Get Link', + style: FlutterFlowTheme.of(context) + .labelLarge + .override( + fontFamily: 'Readex Pro', + letterSpacing: 0, + ), + ), + ], + ), + ), + ), + ], + ), + ), + ), + Container( + width: double.infinity, + height: 60, + decoration: BoxDecoration(), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(12, 8, 12, 8), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Card( + clipBehavior: Clip.antiAliasWithSaveLayer, + color: FlutterFlowTheme.of(context).primaryBackground, + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(40), + ), + child: Padding( + padding: EdgeInsets.all(8), + child: Icon( + Icons.mode_edit, + color: FlutterFlowTheme.of(context).secondaryText, + size: 20, + ), + ), + ), + Expanded( + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(12, 0, 0, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Model selection', + style: FlutterFlowTheme.of(context) + .labelLarge + .override( + fontFamily: 'Readex Pro', + letterSpacing: 0, + ), + ), + ], + ), + ), + ), + ], + ), + ), + ), + Container( + width: double.infinity, + height: 60, + decoration: BoxDecoration(), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(12, 8, 12, 8), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Card( + clipBehavior: Clip.antiAliasWithSaveLayer, + color: FlutterFlowTheme.of(context).primaryBackground, + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(40), + ), + child: Padding( + padding: EdgeInsets.all(8), + child: Icon( + Icons.hide_source, + color: FlutterFlowTheme.of(context).secondaryText, + size: 20, + ), + ), + ), + Expanded( + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(12, 0, 0, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Private', + style: FlutterFlowTheme.of(context) + .labelLarge + .override( + fontFamily: 'Readex Pro', + letterSpacing: 0, + ), + ), + ], + ), + ), + ), + ], + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/mobile/app/lib/components/ConversationThreadModel.dart b/mobile/app/lib/components/ConversationThreadModel.dart new file mode 100644 index 0000000..9bf6598 --- /dev/null +++ b/mobile/app/lib/components/ConversationThreadModel.dart @@ -0,0 +1,36 @@ +import '/components/conversation_bubbles/conversation_bubbles_widget.dart'; +import '/components/prompt_box/prompt_box_widget.dart'; +import '/flutter_flow/flutter_flow_icon_button.dart'; +import '/flutter_flow/flutter_flow_media_display.dart'; +import '/flutter_flow/flutter_flow_theme.dart'; +import '/flutter_flow/flutter_flow_util.dart'; +import '/flutter_flow/flutter_flow_video_player.dart'; +import 'conversation_thread_widget.dart' show ConversationThreadWidget; +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:provider/provider.dart'; + +class ConversationThreadModel + extends FlutterFlowModel { + /// State fields for stateful widgets in this component. + + final formKey = GlobalKey(); + // Model for ConversationBubbles component. + late ConversationBubblesModel conversationBubblesModel; + // Model for PromptBox component. + late PromptBoxModel promptBoxModel; + + @override + void initState(BuildContext context) { + conversationBubblesModel = + createModel(context, () => ConversationBubblesModel()); + promptBoxModel = createModel(context, () => PromptBoxModel()); + } + + @override + void dispose() { + conversationBubblesModel.dispose(); + promptBoxModel.dispose(); + } +} diff --git a/mobile/app/lib/components/ConversationThreadWidget.dart b/mobile/app/lib/components/ConversationThreadWidget.dart new file mode 100644 index 0000000..bc35cd6 --- /dev/null +++ b/mobile/app/lib/components/ConversationThreadWidget.dart @@ -0,0 +1,191 @@ +import '/components/conversation_bubbles/conversation_bubbles_widget.dart'; +import '/components/prompt_box/prompt_box_widget.dart'; +import '/flutter_flow/flutter_flow_icon_button.dart'; +import '/flutter_flow/flutter_flow_media_display.dart'; +import '/flutter_flow/flutter_flow_theme.dart'; +import '/flutter_flow/flutter_flow_util.dart'; +import '/flutter_flow/flutter_flow_video_player.dart'; +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:provider/provider.dart'; + +import 'conversation_thread_model.dart'; +export 'conversation_thread_model.dart'; + +class ConversationThreadWidget extends StatefulWidget { + const ConversationThreadWidget({super.key}); + + @override + State createState() => + _ConversationThreadWidgetState(); +} + +class _ConversationThreadWidgetState extends State { + late ConversationThreadModel _model; + + @override + void setState(VoidCallback callback) { + super.setState(callback); + _model.onUpdate(); + } + + @override + void initState() { + super.initState(); + _model = createModel(context, () => ConversationThreadModel()); + } + + @override + void dispose() { + _model.maybeDispose(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + height: double.infinity, + decoration: BoxDecoration( + color: FlutterFlowTheme.of(context).primaryBackground, + ), + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: ListView( + padding: EdgeInsets.fromLTRB( + 0, + 12, + 0, + 24, + ), + reverse: true, + scrollDirection: Axis.vertical, + children: [ + Container( + decoration: BoxDecoration(), + ), + Container( + height: MediaQuery.sizeOf(context).height * 1, + constraints: BoxConstraints( + minHeight: MediaQuery.sizeOf(context).height * 1, + ), + decoration: BoxDecoration(), + child: wrapWithModel( + model: _model.conversationBubblesModel, + updateCallback: () => setState(() {}), + child: ConversationBubblesWidget(), + ), + ), + ], + ), + ), + Container( + width: double.infinity, + decoration: BoxDecoration( + color: FlutterFlowTheme.of(context).secondaryBackground, + boxShadow: [ + BoxShadow( + blurRadius: 3, + color: Color(0x33000000), + offset: Offset( + 0, + -2, + ), + ) + ], + ), + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Expanded( + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 12, 0, 0), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + FlutterFlowMediaDisplay( + path: '', + imageBuilder: (path) => ClipRRect( + borderRadius: BorderRadius.circular(8), + child: CachedNetworkImage( + fadeInDuration: Duration(milliseconds: 500), + fadeOutDuration: + Duration(milliseconds: 500), + imageUrl: path, + width: 120, + height: 100, + fit: BoxFit.cover, + ), + ), + videoPlayerBuilder: (path) => + FlutterFlowVideoPlayer( + path: path, + width: 300, + autoPlay: false, + looping: true, + showControls: true, + allowFullScreen: true, + allowPlaybackSpeedMenu: false, + ), + ), + Align( + alignment: AlignmentDirectional(-1, -1), + child: FlutterFlowIconButton( + borderColor: + FlutterFlowTheme.of(context).error, + borderRadius: 20, + borderWidth: 2, + buttonSize: 40, + fillColor: FlutterFlowTheme.of(context) + .primaryBackground, + icon: Icon( + Icons.delete_outline_rounded, + color: FlutterFlowTheme.of(context).error, + size: 24, + ), + onPressed: () { + print('IconButton pressed ...'); + }, + ), + ), + ] + .divide(SizedBox(width: 8)) + .addToStart(SizedBox(width: 16)) + .addToEnd(SizedBox(width: 16)), + ), + ), + ), + ), + ], + ), + Form( + key: _model.formKey, + autovalidateMode: AutovalidateMode.disabled, + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 0, 0, 8), + child: wrapWithModel( + model: _model.promptBoxModel, + updateCallback: () => setState(() {}), + child: PromptBoxWidget(), + ), + ), + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/mobile/app/lib/components/ModelItemModel.dart b/mobile/app/lib/components/ModelItemModel.dart new file mode 100644 index 0000000..2faa5ab --- /dev/null +++ b/mobile/app/lib/components/ModelItemModel.dart @@ -0,0 +1,19 @@ +import '/flutter_flow/flutter_flow_theme.dart'; +import '/flutter_flow/flutter_flow_util.dart'; +import 'model_item_widget.dart' show ModelItemWidget; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:provider/provider.dart'; + +class ModelItemModel extends FlutterFlowModel { + /// State fields for stateful widgets in this component. + + // State field(s) for iuser widget. + bool iuserHovered = false; + + @override + void initState(BuildContext context) {} + + @override + void dispose() {} +} diff --git a/mobile/app/lib/components/ModelItemWidget.dart b/mobile/app/lib/components/ModelItemWidget.dart new file mode 100644 index 0000000..2ba9817 --- /dev/null +++ b/mobile/app/lib/components/ModelItemWidget.dart @@ -0,0 +1,122 @@ +import '/flutter_flow/flutter_flow_theme.dart'; +import '/flutter_flow/flutter_flow_util.dart'; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:provider/provider.dart'; + +import 'model_item_model.dart'; +export 'model_item_model.dart'; + +class ModelItemWidget extends StatefulWidget { + const ModelItemWidget({super.key}); + + @override + State createState() => _ModelItemWidgetState(); +} + +class _ModelItemWidgetState extends State { + late ModelItemModel _model; + + @override + void setState(VoidCallback callback) { + super.setState(callback); + _model.onUpdate(); + } + + @override + void initState() { + super.initState(); + _model = createModel(context, () => ModelItemModel()); + } + + @override + void dispose() { + _model.maybeDispose(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return MouseRegion( + opaque: false, + cursor: MouseCursor.defer ?? MouseCursor.defer, + child: Container( + decoration: BoxDecoration( + color: FlutterFlowTheme.of(context).primaryBackground, + borderRadius: BorderRadius.circular(0), + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(12, 8, 12, 8), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: FlutterFlowTheme.of(context).accent1, + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: FlutterFlowTheme.of(context).primary, + width: 2, + ), + ), + child: Align( + alignment: AlignmentDirectional(0, 0), + child: Text( + 'A', + textAlign: TextAlign.center, + style: FlutterFlowTheme.of(context).bodyLarge.override( + fontFamily: 'Readex Pro', + letterSpacing: 0, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + Expanded( + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(12, 0, 8, 0), + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Randy Peterson', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Readex Pro', + letterSpacing: 0, + fontWeight: FontWeight.bold, + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 4, 0, 0), + child: Text( + 'name@domainname.com', + style: + FlutterFlowTheme.of(context).bodySmall.override( + fontFamily: 'Readex Pro', + color: FlutterFlowTheme.of(context).primary, + letterSpacing: 0, + ), + ), + ), + ], + ), + ), + ), + ], + ), + ), + ), + onEnter: ((event) async { + setState(() => _model.iuserHovered = true); + }), + onExit: ((event) async { + setState(() => _model.iuserHovered = false); + }), + ); + } +} diff --git a/mobile/app/lib/components/NoChatsModel.dart b/mobile/app/lib/components/NoChatsModel.dart new file mode 100644 index 0000000..24e70a3 --- /dev/null +++ b/mobile/app/lib/components/NoChatsModel.dart @@ -0,0 +1,14 @@ +import '/flutter_flow/flutter_flow_theme.dart'; +import '/flutter_flow/flutter_flow_util.dart'; +import 'no_chats_widget.dart' show NoChatsWidget; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:provider/provider.dart'; + +class NoChatsModel extends FlutterFlowModel { + @override + void initState(BuildContext context) {} + + @override + void dispose() {} +} diff --git a/mobile/app/lib/components/NoChatsWidget.dart b/mobile/app/lib/components/NoChatsWidget.dart new file mode 100644 index 0000000..b1274a3 --- /dev/null +++ b/mobile/app/lib/components/NoChatsWidget.dart @@ -0,0 +1,85 @@ +import '/flutter_flow/flutter_flow_theme.dart'; +import '/flutter_flow/flutter_flow_util.dart'; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:provider/provider.dart'; + +import 'no_chats_model.dart'; +export 'no_chats_model.dart'; + +class NoChatsWidget extends StatefulWidget { + const NoChatsWidget({ + super.key, + this.icon, + String? title, + String? body, + }) : this.title = title ?? 'No Comments', + this.body = body ?? 'There are no comments associated with this post.'; + + final Widget? icon; + final String title; + final String body; + + @override + State createState() => _NoChatsWidgetState(); +} + +class _NoChatsWidgetState extends State { + late NoChatsModel _model; + + @override + void setState(VoidCallback callback) { + super.setState(callback); + _model.onUpdate(); + } + + @override + void initState() { + super.initState(); + _model = createModel(context, () => NoChatsModel()); + } + + @override + void dispose() { + _model.maybeDispose(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Align( + alignment: AlignmentDirectional(0, -1), + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + widget.icon!, + Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 16, 0, 0), + child: Text( + widget.title, + textAlign: TextAlign.center, + style: FlutterFlowTheme.of(context).headlineSmall.override( + fontFamily: 'Outfit', + color: FlutterFlowTheme.of(context).primaryText, + letterSpacing: 0, + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB(16, 8, 16, 0), + child: Text( + widget.body, + textAlign: TextAlign.center, + style: FlutterFlowTheme.of(context).labelMedium.override( + fontFamily: 'Readex Pro', + letterSpacing: 0, + ), + ), + ), + ], + ), + ); + } +} diff --git a/mobile/app/lib/components/OptionsDialogModel.dart b/mobile/app/lib/components/OptionsDialogModel.dart new file mode 100644 index 0000000..8910477 --- /dev/null +++ b/mobile/app/lib/components/OptionsDialogModel.dart @@ -0,0 +1,21 @@ +import '/flutter_flow/flutter_flow_theme.dart'; +import '/flutter_flow/flutter_flow_util.dart'; +import 'options_dialog_widget.dart' show OptionsDialogWidget; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:provider/provider.dart'; + +class OptionsDialogModel extends FlutterFlowModel { + /// State fields for stateful widgets in this component. + + // State field(s) for MouseRegion widget. + bool mouseRegionHovered1 = false; + // State field(s) for MouseRegion widget. + bool mouseRegionHovered2 = false; + + @override + void initState(BuildContext context) {} + + @override + void dispose() {} +} diff --git a/mobile/app/lib/components/OptionsDialogWidget.dart b/mobile/app/lib/components/OptionsDialogWidget.dart new file mode 100644 index 0000000..a70dde1 --- /dev/null +++ b/mobile/app/lib/components/OptionsDialogWidget.dart @@ -0,0 +1,210 @@ +import '/flutter_flow/flutter_flow_icon_button.dart'; +import '/flutter_flow/flutter_flow_theme.dart'; +import '/flutter_flow/flutter_flow_util.dart'; +import '/flutter_flow/flutter_flow_widgets.dart'; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:provider/provider.dart'; + +import 'popup_model.dart'; +export 'popup_model.dart'; + +class PopupWidget extends StatefulWidget { + const PopupWidget({super.key}); + + @override + State createState() => _PopupWidgetState(); +} + +class _PopupWidgetState extends State { + late PopupModel _model; + + @override + void setState(VoidCallback callback) { + super.setState(callback); + _model.onUpdate(); + } + + @override + void initState() { + super.initState(); + _model = createModel(context, () => PopupModel()); + } + + @override + void dispose() { + _model.maybeDispose(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Visibility( + visible: true, + child: Align( + alignment: AlignmentDirectional(0, 0), + child: Container( + width: double.infinity, + constraints: BoxConstraints( + maxWidth: 570, + maxHeight: 375, + ), + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + blurRadius: 64, + color: Color(0xB2000000), + offset: Offset( + 2, + 2, + ), + spreadRadius: 225, + ) + ], + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: Color(0xFFE0E3E7), + ), + ), + alignment: AlignmentDirectional(0, 0), + child: Align( + alignment: AlignmentDirectional(0, 0), + child: Padding( + padding: EdgeInsets.all(24), + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Expanded( + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 0, 12, 0), + child: Text( + 'Refine the components modal.', + style: FlutterFlowTheme.of(context) + .headlineMedium + .override( + fontFamily: 'Outfit', + color: Color(0xFF14181B), + fontSize: 24, + letterSpacing: 0, + fontWeight: FontWeight.normal, + ), + ), + ), + ), + FlutterFlowIconButton( + borderColor: Color(0xFFF1F4F8), + borderRadius: 30, + borderWidth: 2, + buttonSize: 44, + icon: Icon( + Icons.close_rounded, + color: Color(0xFF57636C), + size: 24, + ), + onPressed: () async { + Navigator.pop(context); + }, + ), + ], + ), + Divider( + height: 24, + thickness: 2, + color: Color(0xFFF1F4F8), + ), + Text( + 'FlutterFlow is a visual development platform that allows you to easily create beautiful and responsive user interfaces for your mobile and web applications. With its drag-and-drop interface and pre-built components, you can quickly prototype and build your app without writing any code. \nAdditionally, FlutterFlow\'s real-time preview feature allows you to see your changes in real-time and make adjustments on the fly.', + style: FlutterFlowTheme.of(context).labelMedium.override( + fontFamily: 'Readex Pro', + color: Color(0xFF57636C), + fontSize: 14, + letterSpacing: 0, + fontWeight: FontWeight.normal, + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 16, 0, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + FFButtonWidget( + onPressed: () async { + Navigator.pop(context); + }, + text: 'Cancel', + options: FFButtonOptions( + height: 40, + padding: + EdgeInsetsDirectional.fromSTEB(24, 0, 24, 0), + iconPadding: + EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), + color: Colors.white, + textStyle: + FlutterFlowTheme.of(context).bodySmall.override( + fontFamily: 'Readex Pro', + color: Color(0xFF14181B), + fontSize: 12, + letterSpacing: 0, + fontWeight: FontWeight.normal, + ), + elevation: 0, + borderSide: BorderSide( + color: Color(0xFFF1F4F8), + width: 2, + ), + borderRadius: BorderRadius.circular(8), + hoverColor: Color(0xFFF1F4F8), + hoverTextColor: Color(0xFF14181B), + ), + ), + FFButtonWidget( + onPressed: () { + print('Button pressed ...'); + }, + text: 'Create Task', + options: FFButtonOptions( + width: 130, + height: 40, + padding: EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), + iconPadding: + EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), + color: Color(0xFF4B39EF), + textStyle: FlutterFlowTheme.of(context) + .titleSmall + .override( + fontFamily: 'Readex Pro', + color: Colors.white, + fontSize: 16, + letterSpacing: 0, + fontWeight: FontWeight.w500, + ), + elevation: 3, + borderSide: BorderSide( + color: Colors.transparent, + width: 1, + ), + borderRadius: BorderRadius.circular(8), + hoverColor: Color(0xFF2B16ED), + hoverTextColor: Colors.white, + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + ), + ); + } +} diff --git a/mobile/app/lib/components/PopupModel.dart b/mobile/app/lib/components/PopupModel.dart new file mode 100644 index 0000000..4950211 --- /dev/null +++ b/mobile/app/lib/components/PopupModel.dart @@ -0,0 +1,16 @@ +import '/flutter_flow/flutter_flow_icon_button.dart'; +import '/flutter_flow/flutter_flow_theme.dart'; +import '/flutter_flow/flutter_flow_util.dart'; +import '/flutter_flow/flutter_flow_widgets.dart'; +import 'popup_widget.dart' show PopupWidget; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:provider/provider.dart'; + +class PopupModel extends FlutterFlowModel { + @override + void initState(BuildContext context) {} + + @override + void dispose() {} +} diff --git a/mobile/app/lib/components/PopupWidget.dart b/mobile/app/lib/components/PopupWidget.dart new file mode 100644 index 0000000..a70dde1 --- /dev/null +++ b/mobile/app/lib/components/PopupWidget.dart @@ -0,0 +1,210 @@ +import '/flutter_flow/flutter_flow_icon_button.dart'; +import '/flutter_flow/flutter_flow_theme.dart'; +import '/flutter_flow/flutter_flow_util.dart'; +import '/flutter_flow/flutter_flow_widgets.dart'; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:provider/provider.dart'; + +import 'popup_model.dart'; +export 'popup_model.dart'; + +class PopupWidget extends StatefulWidget { + const PopupWidget({super.key}); + + @override + State createState() => _PopupWidgetState(); +} + +class _PopupWidgetState extends State { + late PopupModel _model; + + @override + void setState(VoidCallback callback) { + super.setState(callback); + _model.onUpdate(); + } + + @override + void initState() { + super.initState(); + _model = createModel(context, () => PopupModel()); + } + + @override + void dispose() { + _model.maybeDispose(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Visibility( + visible: true, + child: Align( + alignment: AlignmentDirectional(0, 0), + child: Container( + width: double.infinity, + constraints: BoxConstraints( + maxWidth: 570, + maxHeight: 375, + ), + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + blurRadius: 64, + color: Color(0xB2000000), + offset: Offset( + 2, + 2, + ), + spreadRadius: 225, + ) + ], + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: Color(0xFFE0E3E7), + ), + ), + alignment: AlignmentDirectional(0, 0), + child: Align( + alignment: AlignmentDirectional(0, 0), + child: Padding( + padding: EdgeInsets.all(24), + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Expanded( + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 0, 12, 0), + child: Text( + 'Refine the components modal.', + style: FlutterFlowTheme.of(context) + .headlineMedium + .override( + fontFamily: 'Outfit', + color: Color(0xFF14181B), + fontSize: 24, + letterSpacing: 0, + fontWeight: FontWeight.normal, + ), + ), + ), + ), + FlutterFlowIconButton( + borderColor: Color(0xFFF1F4F8), + borderRadius: 30, + borderWidth: 2, + buttonSize: 44, + icon: Icon( + Icons.close_rounded, + color: Color(0xFF57636C), + size: 24, + ), + onPressed: () async { + Navigator.pop(context); + }, + ), + ], + ), + Divider( + height: 24, + thickness: 2, + color: Color(0xFFF1F4F8), + ), + Text( + 'FlutterFlow is a visual development platform that allows you to easily create beautiful and responsive user interfaces for your mobile and web applications. With its drag-and-drop interface and pre-built components, you can quickly prototype and build your app without writing any code. \nAdditionally, FlutterFlow\'s real-time preview feature allows you to see your changes in real-time and make adjustments on the fly.', + style: FlutterFlowTheme.of(context).labelMedium.override( + fontFamily: 'Readex Pro', + color: Color(0xFF57636C), + fontSize: 14, + letterSpacing: 0, + fontWeight: FontWeight.normal, + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 16, 0, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + FFButtonWidget( + onPressed: () async { + Navigator.pop(context); + }, + text: 'Cancel', + options: FFButtonOptions( + height: 40, + padding: + EdgeInsetsDirectional.fromSTEB(24, 0, 24, 0), + iconPadding: + EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), + color: Colors.white, + textStyle: + FlutterFlowTheme.of(context).bodySmall.override( + fontFamily: 'Readex Pro', + color: Color(0xFF14181B), + fontSize: 12, + letterSpacing: 0, + fontWeight: FontWeight.normal, + ), + elevation: 0, + borderSide: BorderSide( + color: Color(0xFFF1F4F8), + width: 2, + ), + borderRadius: BorderRadius.circular(8), + hoverColor: Color(0xFFF1F4F8), + hoverTextColor: Color(0xFF14181B), + ), + ), + FFButtonWidget( + onPressed: () { + print('Button pressed ...'); + }, + text: 'Create Task', + options: FFButtonOptions( + width: 130, + height: 40, + padding: EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), + iconPadding: + EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), + color: Color(0xFF4B39EF), + textStyle: FlutterFlowTheme.of(context) + .titleSmall + .override( + fontFamily: 'Readex Pro', + color: Colors.white, + fontSize: 16, + letterSpacing: 0, + fontWeight: FontWeight.w500, + ), + elevation: 3, + borderSide: BorderSide( + color: Colors.transparent, + width: 1, + ), + borderRadius: BorderRadius.circular(8), + hoverColor: Color(0xFF2B16ED), + hoverTextColor: Colors.white, + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + ), + ); + } +} diff --git a/mobile/app/lib/components/PromptBoxModel.dart b/mobile/app/lib/components/PromptBoxModel.dart new file mode 100644 index 0000000..bf0906b --- /dev/null +++ b/mobile/app/lib/components/PromptBoxModel.dart @@ -0,0 +1,26 @@ +import '/flutter_flow/flutter_flow_icon_button.dart'; +import '/flutter_flow/flutter_flow_theme.dart'; +import '/flutter_flow/flutter_flow_util.dart'; +import 'prompt_box_widget.dart' show PromptBoxWidget; +import 'package:easy_debounce/easy_debounce.dart'; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:provider/provider.dart'; + +class PromptBoxModel extends FlutterFlowModel { + /// State fields for stateful widgets in this component. + + // State field(s) for TextField widget. + FocusNode? textFieldFocusNode; + TextEditingController? textController; + String? Function(BuildContext, String?)? textControllerValidator; + + @override + void initState(BuildContext context) {} + + @override + void dispose() { + textFieldFocusNode?.dispose(); + textController?.dispose(); + } +} diff --git a/mobile/app/lib/components/PromptBoxWidget.dart b/mobile/app/lib/components/PromptBoxWidget.dart new file mode 100644 index 0000000..7713f62 --- /dev/null +++ b/mobile/app/lib/components/PromptBoxWidget.dart @@ -0,0 +1,244 @@ +import '/flutter_flow/flutter_flow_icon_button.dart'; +import '/flutter_flow/flutter_flow_theme.dart'; +import '/flutter_flow/flutter_flow_util.dart'; +import 'package:easy_debounce/easy_debounce.dart'; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:provider/provider.dart'; + +import 'prompt_box_model.dart'; +export 'prompt_box_model.dart'; + +class PromptBoxWidget extends StatefulWidget { + const PromptBoxWidget({super.key}); + + @override + State createState() => _PromptBoxWidgetState(); +} + +class _PromptBoxWidgetState extends State { + late PromptBoxModel _model; + + @override + void setState(VoidCallback callback) { + super.setState(callback); + _model.onUpdate(); + } + + @override + void initState() { + super.initState(); + _model = createModel(context, () => PromptBoxModel()); + + _model.textController ??= TextEditingController(); + _model.textFieldFocusNode ??= FocusNode(); + } + + @override + void dispose() { + _model.maybeDispose(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return SafeArea( + child: Container( + width: double.infinity, + decoration: BoxDecoration( + color: FlutterFlowTheme.of(context).secondaryBackground, + borderRadius: BorderRadius.circular(10), + ), + alignment: AlignmentDirectional(0, 0), + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Expanded( + child: Padding( + padding: EdgeInsets.all(14), + child: FlutterFlowIconButton( + borderColor: Colors.transparent, + borderRadius: 30, + borderWidth: 1, + buttonSize: 40, + icon: Icon( + Icons.file_upload_outlined, + color: FlutterFlowTheme.of(context).secondaryText, + size: 30, + ), + onPressed: () { + print('IconButton pressed ...'); + }, + ), + ), + ), + Expanded( + child: Padding( + padding: EdgeInsets.all(14), + child: FlutterFlowIconButton( + borderColor: Colors.transparent, + borderRadius: 30, + borderWidth: 1, + buttonSize: 40, + icon: Icon( + Icons.photo_outlined, + color: FlutterFlowTheme.of(context).secondaryText, + size: 30, + ), + onPressed: () { + print('IconButton pressed ...'); + }, + ), + ), + ), + Expanded( + child: Padding( + padding: EdgeInsets.all(14), + child: FlutterFlowIconButton( + borderColor: Colors.transparent, + borderRadius: 30, + borderWidth: 1, + buttonSize: 40, + icon: Icon( + Icons.mic_none, + color: FlutterFlowTheme.of(context).secondaryText, + size: 30, + ), + onPressed: () { + print('IconButton pressed ...'); + }, + ), + ), + ), + Expanded( + child: Padding( + padding: EdgeInsets.all(14), + child: FlutterFlowIconButton( + borderColor: Colors.transparent, + borderRadius: 30, + borderWidth: 1, + buttonSize: 40, + icon: Icon( + Icons.videocam_outlined, + color: FlutterFlowTheme.of(context).secondaryText, + size: 30, + ), + onPressed: () { + print('IconButton pressed ...'); + }, + ), + ), + ), + Expanded( + child: Padding( + padding: EdgeInsets.all(14), + child: FlutterFlowIconButton( + borderColor: Colors.transparent, + borderRadius: 30, + borderWidth: 1, + buttonSize: 40, + icon: Icon( + Icons.camera_alt_outlined, + color: FlutterFlowTheme.of(context).secondaryText, + size: 30, + ), + onPressed: () { + print('IconButton pressed ...'); + }, + ), + ), + ), + ], + ), + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Expanded( + flex: 7, + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(12, 0, 12, 0), + child: TextFormField( + controller: _model.textController, + focusNode: _model.textFieldFocusNode, + onChanged: (_) => EasyDebounce.debounce( + '_model.textController', + Duration(milliseconds: 2000), + () => setState(() {}), + ), + autofocus: true, + textCapitalization: TextCapitalization.none, + textInputAction: TextInputAction.go, + obscureText: false, + decoration: InputDecoration( + isDense: false, + counterStyle: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Readex Pro', + color: FlutterFlowTheme.of(context).secondaryText, + letterSpacing: 0, + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: FlutterFlowTheme.of(context).secondaryText, + width: 2, + ), + borderRadius: BorderRadius.circular(12), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Color(0x00000000), + width: 2, + ), + borderRadius: BorderRadius.circular(12), + ), + errorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Color(0x00000000), + width: 2, + ), + borderRadius: BorderRadius.circular(12), + ), + focusedErrorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Color(0x00000000), + width: 2, + ), + borderRadius: BorderRadius.circular(12), + ), + filled: true, + fillColor: FlutterFlowTheme.of(context).alternate, + suffixIcon: Icon( + Icons.send, + color: FlutterFlowTheme.of(context).primary, + size: 30, + ), + ), + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Lato', + fontSize: 14, + letterSpacing: 0, + fontWeight: FontWeight.normal, + ), + textAlign: TextAlign.start, + maxLines: 5, + minLines: 1, + validator: + _model.textControllerValidator.asValidator(context), + ), + ), + ), + ], + ), + ], + ), + ), + ); + } +} diff --git a/mobile/app/lib/main.dart b/mobile/app/lib/main.dart new file mode 100644 index 0000000..8e94089 --- /dev/null +++ b/mobile/app/lib/main.dart @@ -0,0 +1,125 @@ +import 'package:flutter/material.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Demo', + theme: ThemeData( + // This is the theme of your application. + // + // TRY THIS: Try running your application with "flutter run". You'll see + // the application has a purple toolbar. Then, without quitting the app, + // try changing the seedColor in the colorScheme below to Colors.green + // and then invoke "hot reload" (save your changes or press the "hot + // reload" button in a Flutter-supported IDE, or press "r" if you used + // the command line to start the app). + // + // Notice that the counter didn't reset back to zero; the application + // state is not lost during the reload. To reset the state, use hot + // restart instead. + // + // This works for code too, not just values: Most code changes can be + // tested with just a hot reload. + colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), + useMaterial3: true, + ), + home: const MyHomePage(title: 'Flutter Demo Home Page'), + ); + } +} + +class MyHomePage extends StatefulWidget { + const MyHomePage({super.key, required this.title}); + + // This widget is the home page of your application. It is stateful, meaning + // that it has a State object (defined below) that contains fields that affect + // how it looks. + + // This class is the configuration for the state. It holds the values (in this + // case the title) provided by the parent (in this case the App widget) and + // used by the build method of the State. Fields in a Widget subclass are + // always marked "final". + + final String title; + + @override + State createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + int _counter = 0; + + void _incrementCounter() { + setState(() { + // This call to setState tells the Flutter framework that something has + // changed in this State, which causes it to rerun the build method below + // so that the display can reflect the updated values. If we changed + // _counter without calling setState(), then the build method would not be + // called again, and so nothing would appear to happen. + _counter++; + }); + } + + @override + Widget build(BuildContext context) { + // This method is rerun every time setState is called, for instance as done + // by the _incrementCounter method above. + // + // The Flutter framework has been optimized to make rerunning build methods + // fast, so that you can just rebuild anything that needs updating rather + // than having to individually change instances of widgets. + return Scaffold( + appBar: AppBar( + // TRY THIS: Try changing the color here to a specific color (to + // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar + // change color while the other colors stay the same. + backgroundColor: Theme.of(context).colorScheme.inversePrimary, + // Here we take the value from the MyHomePage object that was created by + // the App.build method, and use it to set our appbar title. + title: Text(widget.title), + ), + body: Center( + // Center is a layout widget. It takes a single child and positions it + // in the middle of the parent. + child: Column( + // Column is also a layout widget. It takes a list of children and + // arranges them vertically. By default, it sizes itself to fit its + // children horizontally, and tries to be as tall as its parent. + // + // Column has various properties to control how it sizes itself and + // how it positions its children. Here we use mainAxisAlignment to + // center the children vertically; the main axis here is the vertical + // axis because Columns are vertical (the cross axis would be + // horizontal). + // + // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint" + // action in the IDE, or press "p" in the console), to see the + // wireframe for each widget. + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text( + 'You have pushed the button this many times:', + ), + Text( + '$_counter', + style: Theme.of(context).textTheme.headlineMedium, + ), + ], + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: _incrementCounter, + tooltip: 'Increment', + child: const Icon(Icons.add), + ), // This trailing comma makes auto-formatting nicer for build methods. + ); + } +} diff --git a/mobile/app/lib/pages/ConversationModel.dart b/mobile/app/lib/pages/ConversationModel.dart new file mode 100644 index 0000000..36fcfb1 --- /dev/null +++ b/mobile/app/lib/pages/ConversationModel.dart @@ -0,0 +1,35 @@ +import '/components/conversation_details_overlay/conversation_details_overlay_widget.dart'; +import '/components/conversation_thread/conversation_thread_widget.dart'; +import '/components/model_item/model_item_widget.dart'; +import '/flutter_flow/flutter_flow_icon_button.dart'; +import '/flutter_flow/flutter_flow_theme.dart'; +import '/flutter_flow/flutter_flow_util.dart'; +import '/flutter_flow/flutter_flow_widgets.dart'; +import 'conversation_widget.dart' show ConversationWidget; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:provider/provider.dart'; + +class ConversationModel extends FlutterFlowModel { + /// State fields for stateful widgets in this page. + + final unfocusNode = FocusNode(); + // Model for ConversationThread component. + late ConversationThreadModel conversationThreadModel; + // Model for ModelItem component. + late ModelItemModel modelItemModel; + + @override + void initState(BuildContext context) { + conversationThreadModel = + createModel(context, () => ConversationThreadModel()); + modelItemModel = createModel(context, () => ModelItemModel()); + } + + @override + void dispose() { + unfocusNode.dispose(); + conversationThreadModel.dispose(); + modelItemModel.dispose(); + } +} diff --git a/mobile/app/lib/pages/ConversationWidget.dart b/mobile/app/lib/pages/ConversationWidget.dart new file mode 100644 index 0000000..0954514 --- /dev/null +++ b/mobile/app/lib/pages/ConversationWidget.dart @@ -0,0 +1,131 @@ +import '/components/conversation_details_overlay/conversation_details_overlay_widget.dart'; +import '/components/conversation_thread/conversation_thread_widget.dart'; +import '/components/model_item/model_item_widget.dart'; +import '/flutter_flow/flutter_flow_icon_button.dart'; +import '/flutter_flow/flutter_flow_theme.dart'; +import '/flutter_flow/flutter_flow_util.dart'; +import '/flutter_flow/flutter_flow_widgets.dart'; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:provider/provider.dart'; + +import 'conversation_model.dart'; +export 'conversation_model.dart'; + +class ConversationWidget extends StatefulWidget { + const ConversationWidget({super.key}); + + @override + State createState() => _ConversationWidgetState(); +} + +class _ConversationWidgetState extends State { + late ConversationModel _model; + + final scaffoldKey = GlobalKey(); + + @override + void initState() { + super.initState(); + _model = createModel(context, () => ConversationModel()); + } + + @override + void dispose() { + _model.dispose(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () => _model.unfocusNode.canRequestFocus + ? FocusScope.of(context).requestFocus(_model.unfocusNode) + : FocusScope.of(context).unfocus(), + child: Scaffold( + key: scaffoldKey, + backgroundColor: FlutterFlowTheme.of(context).secondaryBackground, + appBar: AppBar( + backgroundColor: FlutterFlowTheme.of(context).primaryBackground, + automaticallyImplyLeading: false, + leading: FlutterFlowIconButton( + borderColor: Colors.transparent, + borderRadius: 30, + borderWidth: 1, + buttonSize: 60, + icon: Icon( + Icons.arrow_back_rounded, + color: FlutterFlowTheme.of(context).primaryText, + size: 30, + ), + onPressed: () async { + context.goNamed( + 'ConversationsList', + extra: { + kTransitionInfoKey: TransitionInfo( + hasTransition: true, + transitionType: PageTransitionType.leftToRight, + duration: Duration(milliseconds: 250), + ), + }, + ); + }, + ), + title: wrapWithModel( + model: _model.modelItemModel, + updateCallback: () => setState(() {}), + child: ModelItemWidget(), + ), + actions: [ + Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 8, 16, 8), + child: FlutterFlowIconButton( + borderColor: FlutterFlowTheme.of(context).alternate, + borderRadius: 12, + borderWidth: 2, + buttonSize: 40, + fillColor: FlutterFlowTheme.of(context).primaryBackground, + icon: Icon( + Icons.more_vert, + color: FlutterFlowTheme.of(context).primaryText, + size: 24, + ), + onPressed: () async { + await showModalBottomSheet( + isScrollControlled: true, + backgroundColor: Colors.transparent, + enableDrag: false, + context: context, + builder: (context) { + return GestureDetector( + onTap: () => _model.unfocusNode.canRequestFocus + ? FocusScope.of(context) + .requestFocus(_model.unfocusNode) + : FocusScope.of(context).unfocus(), + child: Padding( + padding: MediaQuery.viewInsetsOf(context), + child: ConversationDetailsOverlayWidget(), + ), + ); + }, + ).then((value) => safeSetState(() {})); + }, + ), + ), + ], + centerTitle: false, + elevation: 0, + ), + body: SafeArea( + top: true, + child: wrapWithModel( + model: _model.conversationThreadModel, + updateCallback: () => setState(() {}), + child: ConversationThreadWidget(), + ), + ), + ), + ); + } +} diff --git a/mobile/app/lib/pages/ConversationsListModel.dart b/mobile/app/lib/pages/ConversationsListModel.dart new file mode 100644 index 0000000..b5339a6 --- /dev/null +++ b/mobile/app/lib/pages/ConversationsListModel.dart @@ -0,0 +1,22 @@ +import '/components/conversation_options/conversation_options_widget.dart'; +import '/flutter_flow/flutter_flow_theme.dart'; +import '/flutter_flow/flutter_flow_util.dart'; +import '/flutter_flow/flutter_flow_widgets.dart'; +import 'conversations_list_widget.dart' show ConversationsListWidget; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:provider/provider.dart'; + +class ConversationsListModel extends FlutterFlowModel { + /// State fields for stateful widgets in this page. + + final unfocusNode = FocusNode(); + + @override + void initState(BuildContext context) {} + + @override + void dispose() { + unfocusNode.dispose(); + } +} diff --git a/mobile/app/lib/pages/ConversationsListWidget.dart b/mobile/app/lib/pages/ConversationsListWidget.dart new file mode 100644 index 0000000..da3b2cb --- /dev/null +++ b/mobile/app/lib/pages/ConversationsListWidget.dart @@ -0,0 +1,252 @@ +import '/components/conversation_options/conversation_options_widget.dart'; +import '/flutter_flow/flutter_flow_theme.dart'; +import '/flutter_flow/flutter_flow_util.dart'; +import '/flutter_flow/flutter_flow_widgets.dart'; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:provider/provider.dart'; + +import 'conversations_list_model.dart'; +export 'conversations_list_model.dart'; + +class ConversationsListWidget extends StatefulWidget { + const ConversationsListWidget({super.key}); + + @override + State createState() => + _ConversationsListWidgetState(); +} + +class _ConversationsListWidgetState extends State { + late ConversationsListModel _model; + + final scaffoldKey = GlobalKey(); + + @override + void initState() { + super.initState(); + _model = createModel(context, () => ConversationsListModel()); + } + + @override + void dispose() { + _model.dispose(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () => _model.unfocusNode.canRequestFocus + ? FocusScope.of(context).requestFocus(_model.unfocusNode) + : FocusScope.of(context).unfocus(), + child: Scaffold( + key: scaffoldKey, + backgroundColor: FlutterFlowTheme.of(context).secondaryBackground, + floatingActionButton: InkWell( + splashColor: Colors.transparent, + focusColor: Colors.transparent, + hoverColor: Colors.transparent, + highlightColor: Colors.transparent, + onLongPress: () async { + await showModalBottomSheet( + isScrollControlled: true, + backgroundColor: Colors.transparent, + enableDrag: false, + context: context, + builder: (context) { + return GestureDetector( + onTap: () => _model.unfocusNode.canRequestFocus + ? FocusScope.of(context).requestFocus(_model.unfocusNode) + : FocusScope.of(context).unfocus(), + child: Padding( + padding: MediaQuery.viewInsetsOf(context), + child: ConversationOptionsWidget(), + ), + ); + }, + ).then((value) => safeSetState(() {})); + + context.pushNamed('Conversation'); + }, + child: FloatingActionButton( + onPressed: () async { + context.pushNamed('Conversation'); + }, + backgroundColor: FlutterFlowTheme.of(context).primary, + elevation: 8, + child: Icon( + Icons.add, + color: FlutterFlowTheme.of(context).info, + size: 24, + ), + ), + ), + appBar: AppBar( + backgroundColor: FlutterFlowTheme.of(context).secondaryBackground, + automaticallyImplyLeading: false, + title: Text( + 'My Chats', + style: FlutterFlowTheme.of(context).headlineLarge.override( + fontFamily: 'Outfit', + letterSpacing: 0, + ), + ), + actions: [], + centerTitle: false, + elevation: 0, + ), + body: SafeArea( + top: true, + child: Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsetsDirectional.fromSTEB(16, 0, 0, 0), + child: Text( + 'AI conversations', + style: FlutterFlowTheme.of(context).labelMedium.override( + fontFamily: 'Readex Pro', + letterSpacing: 0, + ), + ), + ), + Expanded( + child: Align( + alignment: AlignmentDirectional(0, 0), + child: ListView( + padding: EdgeInsets.zero, + reverse: true, + scrollDirection: Axis.vertical, + children: [ + Align( + alignment: AlignmentDirectional(0, 0), + child: InkWell( + splashColor: Colors.transparent, + focusColor: Colors.transparent, + hoverColor: Colors.transparent, + highlightColor: Colors.transparent, + onTap: () async { + context.pushNamed('Conversation'); + }, + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + width: 100, + height: 100, + decoration: BoxDecoration( + color: FlutterFlowTheme.of(context) + .secondaryBackground, + ), + child: Align( + alignment: AlignmentDirectional(0, 0), + child: Icon( + Icons.settings_outlined, + color: FlutterFlowTheme.of(context) + .secondaryText, + size: 48, + ), + ), + ), + Expanded( + child: Align( + alignment: AlignmentDirectional(-1, 0), + child: Container( + width: 100, + height: 100, + constraints: BoxConstraints( + minWidth: + MediaQuery.sizeOf(context).width, + ), + decoration: BoxDecoration( + color: FlutterFlowTheme.of(context) + .secondaryBackground, + shape: BoxShape.rectangle, + ), + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.spaceEvenly, + crossAxisAlignment: + CrossAxisAlignment.center, + children: [ + Align( + alignment: + AlignmentDirectional(-1, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Text( + 'Hello World', + style: + FlutterFlowTheme.of(context) + .headlineLarge + .override( + fontFamily: 'Outfit', + letterSpacing: 0, + ), + ), + ], + ), + ), + Align( + alignment: + AlignmentDirectional(-1, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Expanded( + child: Text( + 'Hello World', + style: FlutterFlowTheme.of( + context) + .bodyMedium + .override( + fontFamily: + 'Readex Pro', + letterSpacing: 0, + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + Container( + width: 100, + height: 100, + decoration: BoxDecoration( + color: FlutterFlowTheme.of(context) + .secondaryBackground, + ), + child: Icon( + Icons.chevron_right_rounded, + color: FlutterFlowTheme.of(context) + .secondaryText, + size: 48, + ), + ), + ], + ), + ), + ), + ], + ), + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/mobile/app/lib/pages/ImageDetailsModel.dart b/mobile/app/lib/pages/ImageDetailsModel.dart new file mode 100644 index 0000000..6398018 --- /dev/null +++ b/mobile/app/lib/pages/ImageDetailsModel.dart @@ -0,0 +1,28 @@ +import '/flutter_flow/flutter_flow_animations.dart'; +import '/flutter_flow/flutter_flow_icon_button.dart'; +import '/flutter_flow/flutter_flow_theme.dart'; +import '/flutter_flow/flutter_flow_util.dart'; +import '/flutter_flow/flutter_flow_widgets.dart'; +import 'dart:math'; +import 'image_details_widget.dart' show ImageDetailsWidget; +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:flutter_animate/flutter_animate.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:provider/provider.dart'; + +class ImageDetailsModel extends FlutterFlowModel { + /// State fields for stateful widgets in this page. + + final unfocusNode = FocusNode(); + + @override + void initState(BuildContext context) {} + + @override + void dispose() { + unfocusNode.dispose(); + } +} diff --git a/mobile/app/lib/pages/ImageDetailsWidget.dart b/mobile/app/lib/pages/ImageDetailsWidget.dart new file mode 100644 index 0000000..e5d5c74 --- /dev/null +++ b/mobile/app/lib/pages/ImageDetailsWidget.dart @@ -0,0 +1,264 @@ +import '/flutter_flow/flutter_flow_animations.dart'; +import '/flutter_flow/flutter_flow_icon_button.dart'; +import '/flutter_flow/flutter_flow_theme.dart'; +import '/flutter_flow/flutter_flow_util.dart'; +import '/flutter_flow/flutter_flow_widgets.dart'; +import 'dart:math'; +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:flutter_animate/flutter_animate.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:provider/provider.dart'; + +import 'image_details_model.dart'; +export 'image_details_model.dart'; + +class ImageDetailsWidget extends StatefulWidget { + const ImageDetailsWidget({super.key}); + + @override + State createState() => _ImageDetailsWidgetState(); +} + +class _ImageDetailsWidgetState extends State + with TickerProviderStateMixin { + late ImageDetailsModel _model; + + final scaffoldKey = GlobalKey(); + + final animationsMap = {}; + + @override + void initState() { + super.initState(); + _model = createModel(context, () => ImageDetailsModel()); + + animationsMap.addAll({ + 'imageOnPageLoadAnimation': AnimationInfo( + trigger: AnimationTrigger.onPageLoad, + effectsBuilder: () => [ + VisibilityEffect(duration: 1.ms), + FadeEffect( + curve: Curves.easeInOut, + delay: 0.0.ms, + duration: 300.0.ms, + begin: 0.0, + end: 1.0, + ), + MoveEffect( + curve: Curves.easeInOut, + delay: 0.0.ms, + duration: 300.0.ms, + begin: Offset(0.0, 30.0), + end: Offset(0.0, 0.0), + ), + ScaleEffect( + curve: Curves.easeInOut, + delay: 0.0.ms, + duration: 300.0.ms, + begin: Offset(0.7, 0.7), + end: Offset(1.0, 1.0), + ), + ], + ), + 'rowOnPageLoadAnimation': AnimationInfo( + trigger: AnimationTrigger.onPageLoad, + effectsBuilder: () => [ + VisibilityEffect(duration: 1.ms), + FadeEffect( + curve: Curves.easeInOut, + delay: 0.0.ms, + duration: 300.0.ms, + begin: 0.0, + end: 1.0, + ), + MoveEffect( + curve: Curves.easeInOut, + delay: 0.0.ms, + duration: 300.0.ms, + begin: Offset(0.0, 30.0), + end: Offset(0.0, 0.0), + ), + TiltEffect( + curve: Curves.easeInOut, + delay: 0.0.ms, + duration: 300.0.ms, + begin: Offset(-0.698, 0), + end: Offset(0, 0), + ), + ], + ), + }); + } + + @override + void dispose() { + _model.dispose(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () => _model.unfocusNode.canRequestFocus + ? FocusScope.of(context).requestFocus(_model.unfocusNode) + : FocusScope.of(context).unfocus(), + child: Scaffold( + key: scaffoldKey, + backgroundColor: FlutterFlowTheme.of(context).secondaryBackground, + appBar: AppBar( + backgroundColor: FlutterFlowTheme.of(context).secondaryBackground, + automaticallyImplyLeading: false, + leading: FlutterFlowIconButton( + borderColor: Colors.transparent, + borderRadius: 30, + borderWidth: 1, + buttonSize: 60, + icon: Icon( + Icons.arrow_back_rounded, + color: FlutterFlowTheme.of(context).primaryText, + size: 30, + ), + onPressed: () async { + context.pop(); + }, + ), + actions: [], + centerTitle: false, + elevation: 0, + ), + body: SafeArea( + top: true, + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Expanded( + child: Padding( + padding: EdgeInsets.all(12), + child: Hero( + tag: 'hero', + transitionOnUserGestures: true, + child: ClipRRect( + borderRadius: BorderRadius.circular(8), + child: CachedNetworkImage( + fadeInDuration: Duration(milliseconds: 500), + fadeOutDuration: Duration(milliseconds: 500), + imageUrl: 'https://picsum.photos/seed/150/600', + width: double.infinity, + fit: BoxFit.fitWidth, + memCacheWidth: 1200, + memCacheHeight: 1200, + ), + ), + ).animateOnPageLoad( + animationsMap['imageOnPageLoadAnimation']!), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB(12, 12, 12, 32), + child: Row( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 4, 8, 16), + child: Container( + width: 36, + height: 36, + decoration: BoxDecoration( + color: FlutterFlowTheme.of(context).accent1, + borderRadius: BorderRadius.circular(12), + shape: BoxShape.rectangle, + border: Border.all( + color: FlutterFlowTheme.of(context).primary, + width: 2, + ), + ), + child: Padding( + padding: EdgeInsets.all(2), + child: ClipRRect( + borderRadius: BorderRadius.circular(8), + child: CachedNetworkImage( + fadeInDuration: Duration(milliseconds: 200), + fadeOutDuration: Duration(milliseconds: 200), + imageUrl: + 'https://images.unsplash.com/photo-1521572267360-ee0c2909d518?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w0NTYyMDF8MHwxfHNlYXJjaHwyMHx8cHJvZmlsZXxlbnwwfHx8fDE2OTU4MjQ5OTN8MA&ixlib=rb-4.0.3&q=80&w=1080', + width: 44, + height: 44, + fit: BoxFit.cover, + ), + ), + ), + ), + ), + Flexible( + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 0, 0, 8), + child: Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + SelectionArea( + child: AutoSizeText( + 'Thanks so much! We really do appreciate it!', + textAlign: TextAlign.start, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Readex Pro', + letterSpacing: 0, + lineHeight: 1.5, + ), + )), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0, 4, 0, 0), + child: Text( + 'Just Now', + style: FlutterFlowTheme.of(context) + .labelSmall + .override( + fontFamily: 'Readex Pro', + letterSpacing: 0, + ), + ), + ), + ].divide(SizedBox(width: 4)), + ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 4, 0, 0), + child: SelectionArea( + child: AutoSizeText( + 'Thanks so much! We really do appreciate it!', + textAlign: TextAlign.start, + style: FlutterFlowTheme.of(context) + .labelLarge + .override( + fontFamily: 'Readex Pro', + letterSpacing: 0, + lineHeight: 1.5, + ), + )), + ), + ], + ), + ), + ), + ], + ).animateOnPageLoad(animationsMap['rowOnPageLoadAnimation']!), + ), + ], + ), + ), + ), + ); + } +} diff --git a/mobile/app/lib/pages/SettingsModel.dart b/mobile/app/lib/pages/SettingsModel.dart new file mode 100644 index 0000000..19e74a3 --- /dev/null +++ b/mobile/app/lib/pages/SettingsModel.dart @@ -0,0 +1,21 @@ +import '/components/popup/popup_widget.dart'; +import '/flutter_flow/flutter_flow_animations.dart'; +import '/flutter_flow/flutter_flow_theme.dart'; +import '/flutter_flow/flutter_flow_util.dart'; +import '/flutter_flow/flutter_flow_widgets.dart'; +import 'dart:math'; +import 'settings_widget.dart' show SettingsWidget; +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:flutter_animate/flutter_animate.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:provider/provider.dart'; + +class SettingsModel extends FlutterFlowModel { + @override + void initState(BuildContext context) {} + + @override + void dispose() {} +} diff --git a/mobile/app/lib/pages/SettingsWidget.dart b/mobile/app/lib/pages/SettingsWidget.dart new file mode 100644 index 0000000..7905835 --- /dev/null +++ b/mobile/app/lib/pages/SettingsWidget.dart @@ -0,0 +1,1809 @@ +import '/components/popup/popup_widget.dart'; +import '/flutter_flow/flutter_flow_animations.dart'; +import '/flutter_flow/flutter_flow_theme.dart'; +import '/flutter_flow/flutter_flow_util.dart'; +import '/flutter_flow/flutter_flow_widgets.dart'; +import 'dart:math'; +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:flutter_animate/flutter_animate.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:provider/provider.dart'; + +import 'settings_model.dart'; +export 'settings_model.dart'; + +class SettingsWidget extends StatefulWidget { + const SettingsWidget({super.key}); + + @override + State createState() => _SettingsWidgetState(); +} + +class _SettingsWidgetState extends State + with TickerProviderStateMixin { + late SettingsModel _model; + + final scaffoldKey = GlobalKey(); + + final animationsMap = {}; + + @override + void initState() { + super.initState(); + _model = createModel(context, () => SettingsModel()); + + animationsMap.addAll({ + 'containerOnPageLoadAnimation1': AnimationInfo( + trigger: AnimationTrigger.onPageLoad, + effectsBuilder: () => [ + FadeEffect( + curve: Curves.easeInOut, + delay: 0.0.ms, + duration: 600.0.ms, + begin: 0.0, + end: 1.0, + ), + MoveEffect( + curve: Curves.easeInOut, + delay: 0.0.ms, + duration: 600.0.ms, + begin: Offset(0.0, 90.0), + end: Offset(0.0, 0.0), + ), + ], + ), + 'containerOnPageLoadAnimation2': AnimationInfo( + trigger: AnimationTrigger.onPageLoad, + effectsBuilder: () => [ + FadeEffect( + curve: Curves.easeInOut, + delay: 0.0.ms, + duration: 600.0.ms, + begin: 0.0, + end: 1.0, + ), + MoveEffect( + curve: Curves.easeInOut, + delay: 0.0.ms, + duration: 600.0.ms, + begin: Offset(0.0, 90.0), + end: Offset(0.0, 0.0), + ), + ], + ), + }); + setupAnimations( + animationsMap.values.where((anim) => + anim.trigger == AnimationTrigger.onActionTrigger || + !anim.applyInitialState), + this, + ); + } + + @override + void dispose() { + _model.dispose(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + key: scaffoldKey, + backgroundColor: FlutterFlowTheme.of(context).secondaryBackground, + appBar: MediaQuery.sizeOf(context).width <= 991.0 + ? AppBar( + backgroundColor: FlutterFlowTheme.of(context).secondaryBackground, + automaticallyImplyLeading: false, + title: Text( + 'My Profile', + style: FlutterFlowTheme.of(context).headlineMedium.override( + fontFamily: 'Outfit', + letterSpacing: 0, + ), + ), + actions: [], + centerTitle: false, + elevation: 0, + ) + : null, + body: SafeArea( + top: true, + child: Stack( + children: [ + Row( + mainAxisSize: MainAxisSize.max, + children: [ + if (responsiveVisibility( + context: context, + phone: false, + tablet: false, + )) + Container( + width: 270, + height: double.infinity, + decoration: BoxDecoration( + color: FlutterFlowTheme.of(context).primaryBackground, + borderRadius: BorderRadius.circular(0), + border: Border.all( + color: FlutterFlowTheme.of(context).alternate, + width: 1, + ), + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 24, 0, 16), + child: Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(16, 0, 16, 12), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Icon( + Icons.add_task_rounded, + color: FlutterFlowTheme.of(context).primary, + size: 32, + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 12, 0, 0, 0), + child: Text( + 'check.io', + style: FlutterFlowTheme.of(context) + .headlineMedium + .override( + fontFamily: 'Outfit', + letterSpacing: 0, + ), + ), + ), + ], + ), + ), + Divider( + height: 12, + thickness: 2, + color: FlutterFlowTheme.of(context).alternate, + ), + Expanded( + child: Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 16, 12, 0, 0), + child: Text( + 'Platform Navigation', + style: FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: 'Readex Pro', + letterSpacing: 0, + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 16, 0, 16, 0), + child: AnimatedContainer( + duration: Duration(milliseconds: 200), + curve: Curves.easeInOut, + width: double.infinity, + height: 44, + decoration: BoxDecoration( + color: FlutterFlowTheme.of(context) + .primaryBackground, + borderRadius: BorderRadius.circular(12), + shape: BoxShape.rectangle, + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 8, 0, 6, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Icon( + Icons.space_dashboard, + color: FlutterFlowTheme.of(context) + .primaryText, + size: 24, + ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB( + 12, 0, 0, 0), + child: Text( + 'Dashboard', + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: + 'Readex Pro', + letterSpacing: 0, + ), + ), + ), + ], + ), + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 16, 0, 16, 0), + child: AnimatedContainer( + duration: Duration(milliseconds: 200), + curve: Curves.easeInOut, + width: double.infinity, + height: 44, + decoration: BoxDecoration( + color: FlutterFlowTheme.of(context) + .primaryBackground, + borderRadius: BorderRadius.circular(12), + shape: BoxShape.rectangle, + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 8, 0, 6, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Icon( + Icons.forum_rounded, + color: FlutterFlowTheme.of(context) + .primaryText, + size: 24, + ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB( + 12, 0, 0, 0), + child: Text( + 'Chats', + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: + 'Readex Pro', + letterSpacing: 0, + ), + ), + ), + ], + ), + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 16, 0, 16, 0), + child: AnimatedContainer( + duration: Duration(milliseconds: 200), + curve: Curves.easeInOut, + width: double.infinity, + height: 44, + decoration: BoxDecoration( + color: FlutterFlowTheme.of(context) + .primaryBackground, + borderRadius: BorderRadius.circular(12), + shape: BoxShape.rectangle, + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 8, 0, 6, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Icon( + Icons.work, + color: FlutterFlowTheme.of(context) + .primaryText, + size: 24, + ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB( + 12, 0, 0, 0), + child: Text( + 'Projects', + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: + 'Readex Pro', + letterSpacing: 0, + ), + ), + ), + ], + ), + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 16, 0, 16, 0), + child: AnimatedContainer( + duration: Duration(milliseconds: 200), + curve: Curves.easeInOut, + width: double.infinity, + height: 44, + decoration: BoxDecoration( + color: + FlutterFlowTheme.of(context).accent1, + borderRadius: BorderRadius.circular(12), + shape: BoxShape.rectangle, + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 8, 0, 6, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Icon( + Icons.receipt_rounded, + color: FlutterFlowTheme.of(context) + .primary, + size: 24, + ), + Expanded( + child: Padding( + padding: EdgeInsetsDirectional + .fromSTEB(12, 0, 0, 0), + child: Text( + 'Recent Orders', + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: + 'Readex Pro', + letterSpacing: 0, + ), + ), + ), + ), + Container( + height: 32, + decoration: BoxDecoration( + color: + FlutterFlowTheme.of(context) + .primary, + borderRadius: + BorderRadius.circular(8), + ), + child: Align( + alignment: + AlignmentDirectional(0, 0), + child: Padding( + padding: EdgeInsetsDirectional + .fromSTEB(8, 4, 8, 4), + child: Text( + '12', + style: FlutterFlowTheme.of( + context) + .bodyMedium + .override( + fontFamily: + 'Readex Pro', + color: + FlutterFlowTheme.of( + context) + .info, + letterSpacing: 0, + ), + ), + ), + ), + ), + ], + ), + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 16, 0, 0, 0), + child: Text( + 'Settings', + style: FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: 'Readex Pro', + letterSpacing: 0, + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 16, 0, 16, 0), + child: AnimatedContainer( + duration: Duration(milliseconds: 200), + curve: Curves.easeInOut, + width: double.infinity, + height: 44, + decoration: BoxDecoration( + color: FlutterFlowTheme.of(context) + .primaryBackground, + borderRadius: BorderRadius.circular(12), + shape: BoxShape.rectangle, + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 8, 0, 6, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Icon( + Icons.attach_money_rounded, + color: FlutterFlowTheme.of(context) + .primaryText, + size: 24, + ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB( + 12, 0, 0, 0), + child: Text( + 'Billing', + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: + 'Readex Pro', + letterSpacing: 0, + ), + ), + ), + ], + ), + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 16, 0, 16, 0), + child: AnimatedContainer( + duration: Duration(milliseconds: 200), + curve: Curves.easeInOut, + width: double.infinity, + height: 44, + decoration: BoxDecoration( + color: FlutterFlowTheme.of(context) + .primaryBackground, + borderRadius: BorderRadius.circular(12), + shape: BoxShape.rectangle, + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 8, 0, 6, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Icon( + Icons.wifi_tethering_rounded, + color: FlutterFlowTheme.of(context) + .primaryText, + size: 24, + ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB( + 12, 0, 0, 0), + child: Text( + 'Explore', + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: + 'Readex Pro', + letterSpacing: 0, + ), + ), + ), + ], + ), + ), + ), + ), + ].divide(SizedBox(height: 12)), + ), + ), + Align( + alignment: AlignmentDirectional(0, -1), + child: Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 8, 0, 16), + child: Container( + width: 250, + height: 50, + decoration: BoxDecoration( + color: FlutterFlowTheme.of(context) + .primaryBackground, + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: + FlutterFlowTheme.of(context).alternate, + width: 1, + ), + ), + child: Padding( + padding: EdgeInsets.all(4), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Expanded( + child: InkWell( + splashColor: Colors.transparent, + focusColor: Colors.transparent, + hoverColor: Colors.transparent, + highlightColor: Colors.transparent, + onTap: () async { + setDarkModeSetting( + context, ThemeMode.light); + }, + child: Container( + width: 115, + height: 100, + decoration: BoxDecoration( + color: Theme.of(context) + .brightness == + Brightness.light + ? FlutterFlowTheme.of(context) + .secondaryBackground + : FlutterFlowTheme.of(context) + .primaryBackground, + borderRadius: + BorderRadius.circular(10), + border: Border.all( + color: valueOrDefault( + Theme.of(context) + .brightness == + Brightness.light + ? FlutterFlowTheme.of( + context) + .alternate + : FlutterFlowTheme.of( + context) + .primaryBackground, + FlutterFlowTheme.of(context) + .alternate, + ), + width: 1, + ), + ), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + Icon( + Icons.wb_sunny_rounded, + color: Theme.of(context) + .brightness == + Brightness.light + ? FlutterFlowTheme.of( + context) + .primaryText + : FlutterFlowTheme.of( + context) + .secondaryText, + size: 16, + ), + Padding( + padding: EdgeInsetsDirectional + .fromSTEB(4, 0, 0, 0), + child: Text( + 'Light Mode', + style: + FlutterFlowTheme.of( + context) + .bodyMedium + .override( + fontFamily: + 'Readex Pro', + color: Theme.of(context) + .brightness == + Brightness + .light + ? FlutterFlowTheme.of( + context) + .primaryText + : FlutterFlowTheme.of( + context) + .secondaryText, + letterSpacing: 0, + ), + ), + ), + ], + ), + ), + ), + ), + Expanded( + child: InkWell( + splashColor: Colors.transparent, + focusColor: Colors.transparent, + hoverColor: Colors.transparent, + highlightColor: Colors.transparent, + onTap: () async { + setDarkModeSetting( + context, ThemeMode.dark); + }, + child: Container( + width: 115, + height: 100, + decoration: BoxDecoration( + color: Theme.of(context) + .brightness == + Brightness.dark + ? FlutterFlowTheme.of(context) + .secondaryBackground + : FlutterFlowTheme.of(context) + .primaryBackground, + borderRadius: + BorderRadius.circular(10), + border: Border.all( + color: valueOrDefault( + Theme.of(context) + .brightness == + Brightness.dark + ? FlutterFlowTheme.of( + context) + .alternate + : FlutterFlowTheme.of( + context) + .primaryBackground, + FlutterFlowTheme.of(context) + .primaryBackground, + ), + width: 1, + ), + ), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + Icon( + Icons.nightlight_round, + color: Theme.of(context) + .brightness == + Brightness.dark + ? FlutterFlowTheme.of( + context) + .primaryText + : FlutterFlowTheme.of( + context) + .secondaryText, + size: 16, + ), + Padding( + padding: EdgeInsetsDirectional + .fromSTEB(4, 0, 0, 0), + child: Text( + 'Dark Mode', + style: + FlutterFlowTheme.of( + context) + .bodyMedium + .override( + fontFamily: + 'Readex Pro', + color: Theme.of(context) + .brightness == + Brightness + .dark + ? FlutterFlowTheme.of( + context) + .primaryText + : FlutterFlowTheme.of( + context) + .secondaryText, + letterSpacing: 0, + ), + ), + ), + ], + ), + ), + ), + ), + ], + ), + ), + ), + ), + ), + Divider( + height: 12, + thickness: 2, + color: FlutterFlowTheme.of(context).alternate, + ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(16, 12, 16, 12), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Container( + width: 50, + height: 50, + decoration: BoxDecoration( + color: FlutterFlowTheme.of(context).accent1, + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: + FlutterFlowTheme.of(context).primary, + width: 2, + ), + ), + child: Padding( + padding: EdgeInsets.all(2), + child: ClipRRect( + borderRadius: BorderRadius.circular(8), + child: CachedNetworkImage( + fadeInDuration: + Duration(milliseconds: 500), + fadeOutDuration: + Duration(milliseconds: 500), + imageUrl: + 'https://images.unsplash.com/photo-1624561172888-ac93c696e10c?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8NjJ8fHVzZXJzfGVufDB8fDB8fA%3D%3D&auto=format&fit=crop&w=900&q=60', + width: 44, + height: 44, + fit: BoxFit.cover, + ), + ), + ), + ), + Expanded( + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 12, 0, 0, 0), + child: Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + 'Casper Ghost', + style: FlutterFlowTheme.of(context) + .bodyLarge + .override( + fontFamily: 'Readex Pro', + letterSpacing: 0, + ), + ), + Text( + 'admin@gmail.com', + style: FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: 'Readex Pro', + letterSpacing: 0, + ), + ), + ], + ), + ), + ), + ], + ), + ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(16, 0, 16, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Icon( + Icons.menu_open_rounded, + color: FlutterFlowTheme.of(context) + .secondaryText, + size: 24, + ), + ], + ), + ), + ], + ), + ), + ), + Expanded( + child: Align( + alignment: AlignmentDirectional(0, 0), + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + if (responsiveVisibility( + context: context, + phone: false, + tablet: false, + )) + Container( + width: double.infinity, + height: 24, + decoration: BoxDecoration(), + ), + Align( + alignment: AlignmentDirectional(0, -1), + child: Padding( + padding: + EdgeInsetsDirectional.fromSTEB(16, 16, 16, 0), + child: Wrap( + spacing: 16, + runSpacing: 16, + alignment: WrapAlignment.center, + crossAxisAlignment: WrapCrossAlignment.start, + direction: Axis.horizontal, + runAlignment: WrapAlignment.center, + verticalDirection: VerticalDirection.down, + clipBehavior: Clip.none, + children: [ + Container( + width: double.infinity, + constraints: BoxConstraints( + maxWidth: 390, + ), + decoration: BoxDecoration( + color: FlutterFlowTheme.of(context) + .secondaryBackground, + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: FlutterFlowTheme.of(context) + .alternate, + width: 1, + ), + ), + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Padding( + padding: EdgeInsets.all(16), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Container( + width: 72, + height: 72, + decoration: BoxDecoration( + color: FlutterFlowTheme.of( + context) + .accent1, + borderRadius: + BorderRadius.circular(12), + border: Border.all( + color: FlutterFlowTheme.of( + context) + .primary, + width: 2, + ), + ), + child: Padding( + padding: EdgeInsets.all(2), + child: ClipRRect( + borderRadius: + BorderRadius.circular( + 8), + child: CachedNetworkImage( + fadeInDuration: Duration( + milliseconds: 500), + fadeOutDuration: Duration( + milliseconds: 500), + imageUrl: + 'https://images.unsplash.com/photo-1624561172888-ac93c696e10c?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8NjJ8fHVzZXJzfGVufDB8fDB8fA%3D%3D&auto=format&fit=crop&w=900&q=60', + width: 44, + height: 44, + fit: BoxFit.cover, + ), + ), + ), + ), + Expanded( + child: Padding( + padding: EdgeInsetsDirectional + .fromSTEB(16, 4, 0, 4), + child: Column( + mainAxisSize: + MainAxisSize.max, + crossAxisAlignment: + CrossAxisAlignment + .start, + children: [ + Text( + 'Casper Ghost', + style: FlutterFlowTheme + .of(context) + .headlineSmall + .override( + fontFamily: + 'Outfit', + letterSpacing: 0, + ), + ), + Padding( + padding: + EdgeInsetsDirectional + .fromSTEB( + 0, 4, 0, 0), + child: Text( + 'casper@ghustbusters.com', + style: FlutterFlowTheme + .of(context) + .labelMedium + .override( + fontFamily: + 'Readex Pro', + letterSpacing: + 0, + ), + ), + ), + ], + ), + ), + ), + ], + ), + ), + Divider( + height: 2, + thickness: 1, + color: FlutterFlowTheme.of(context) + .alternate, + ), + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Expanded( + child: Padding( + padding: EdgeInsetsDirectional + .fromSTEB(0, 0, 0, 12), + child: Container( + width: 200, + decoration: BoxDecoration( + color: FlutterFlowTheme.of( + context) + .secondaryBackground, + borderRadius: + BorderRadius.circular( + 8), + ), + child: Padding( + padding: + EdgeInsetsDirectional + .fromSTEB( + 12, 12, 12, 0), + child: Column( + mainAxisSize: + MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment + .center, + children: [ + Icon( + Icons.receipt_rounded, + color: FlutterFlowTheme + .of(context) + .primary, + size: 44, + ), + Padding( + padding: + EdgeInsetsDirectional + .fromSTEB(0, + 8, 0, 4), + child: Text( + '2,200', + textAlign: TextAlign + .center, + style: FlutterFlowTheme + .of(context) + .headlineSmall + .override( + fontFamily: + 'Outfit', + letterSpacing: + 0, + ), + ), + ), + Text( + 'Orders Placed', + textAlign: + TextAlign.center, + style: FlutterFlowTheme + .of(context) + .labelMedium + .override( + fontFamily: + 'Readex Pro', + letterSpacing: + 0, + ), + ), + ], + ), + ), + ).animateOnPageLoad(animationsMap[ + 'containerOnPageLoadAnimation1']!), + ), + ), + Expanded( + child: Padding( + padding: EdgeInsetsDirectional + .fromSTEB(0, 0, 0, 12), + child: Container( + width: 200, + decoration: BoxDecoration( + color: FlutterFlowTheme.of( + context) + .secondaryBackground, + borderRadius: + BorderRadius.circular( + 8), + ), + child: Padding( + padding: + EdgeInsetsDirectional + .fromSTEB( + 12, 12, 12, 0), + child: Column( + mainAxisSize: + MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment + .center, + children: [ + Icon( + Icons + .ssid_chart_rounded, + color: FlutterFlowTheme + .of(context) + .primary, + size: 44, + ), + Padding( + padding: + EdgeInsetsDirectional + .fromSTEB(0, + 8, 0, 4), + child: Text( + '\$212.4k', + textAlign: TextAlign + .center, + style: FlutterFlowTheme + .of(context) + .headlineSmall + .override( + fontFamily: + 'Outfit', + letterSpacing: + 0, + ), + ), + ), + Text( + 'Money Earned', + textAlign: + TextAlign.center, + style: FlutterFlowTheme + .of(context) + .labelMedium + .override( + fontFamily: + 'Readex Pro', + letterSpacing: + 0, + ), + ), + ], + ), + ), + ).animateOnPageLoad(animationsMap[ + 'containerOnPageLoadAnimation2']!), + ), + ), + ], + ), + ], + ), + ), + Container( + width: double.infinity, + constraints: BoxConstraints( + maxWidth: 470, + ), + decoration: BoxDecoration(), + child: Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Container( + width: double.infinity, + decoration: BoxDecoration( + color: FlutterFlowTheme.of(context) + .primaryBackground, + borderRadius: + BorderRadius.circular(12), + border: Border.all( + color: + FlutterFlowTheme.of(context) + .alternate, + ), + ), + child: Padding( + padding: + EdgeInsetsDirectional.fromSTEB( + 16, 12, 0, 12), + child: Text( + 'My Account Information', + style: + FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: + 'Readex Pro', + letterSpacing: 0, + ), + ), + ), + ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB( + 0, 0, 0, 1), + child: Container( + width: double.infinity, + height: 60, + decoration: BoxDecoration( + color: + FlutterFlowTheme.of(context) + .secondaryBackground, + boxShadow: [ + BoxShadow( + blurRadius: 0, + color: FlutterFlowTheme.of( + context) + .alternate, + offset: Offset( + 0, + 1, + ), + ) + ], + shape: BoxShape.rectangle, + ), + child: Padding( + padding: EdgeInsetsDirectional + .fromSTEB(16, 0, 16, 0), + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + Row( + mainAxisSize: + MainAxisSize.max, + children: [ + Text( + 'Change Password', + style: FlutterFlowTheme + .of(context) + .bodyLarge + .override( + fontFamily: + 'Readex Pro', + letterSpacing: 0, + ), + ), + Expanded( + child: Align( + alignment: + AlignmentDirectional( + 1, 0), + child: Icon( + Icons + .chevron_right_rounded, + color: FlutterFlowTheme + .of(context) + .secondaryText, + size: 20, + ), + ), + ), + ], + ), + ], + ), + ), + ), + ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB( + 0, 0, 0, 1), + child: Container( + width: double.infinity, + height: 60, + decoration: BoxDecoration( + color: + FlutterFlowTheme.of(context) + .secondaryBackground, + boxShadow: [ + BoxShadow( + blurRadius: 0, + color: FlutterFlowTheme.of( + context) + .alternate, + offset: Offset( + 0, + 1, + ), + ) + ], + shape: BoxShape.rectangle, + ), + child: Padding( + padding: EdgeInsetsDirectional + .fromSTEB(16, 0, 16, 0), + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + Row( + mainAxisSize: + MainAxisSize.max, + children: [ + Text( + 'Edit Profile', + style: + FlutterFlowTheme.of( + context) + .bodyLarge + .override( + fontFamily: + 'Readex Pro', + color: FlutterFlowTheme.of( + context) + .primaryText, + letterSpacing: + 0, + ), + ), + Expanded( + child: Align( + alignment: + AlignmentDirectional( + 1, 0), + child: Icon( + Icons + .chevron_right_rounded, + color: FlutterFlowTheme + .of(context) + .secondaryText, + size: 20, + ), + ), + ), + ], + ), + ], + ), + ), + ), + ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB( + 0, 12, 0, 0), + child: Container( + width: double.infinity, + decoration: BoxDecoration( + color: + FlutterFlowTheme.of(context) + .primaryBackground, + borderRadius: + BorderRadius.circular(12), + border: Border.all( + color: + FlutterFlowTheme.of(context) + .alternate, + ), + ), + child: Padding( + padding: EdgeInsetsDirectional + .fromSTEB(16, 12, 0, 12), + child: Text( + 'Support', + style: + FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: + 'Readex Pro', + letterSpacing: 0, + ), + ), + ), + ), + ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB( + 0, 0, 0, 1), + child: InkWell( + splashColor: Colors.transparent, + focusColor: Colors.transparent, + hoverColor: Colors.transparent, + highlightColor: Colors.transparent, + onTap: () async { + context.pushNamed( + 'SupportForm', + extra: { + kTransitionInfoKey: + TransitionInfo( + hasTransition: true, + transitionType: + PageTransitionType + .rightToLeft, + duration: Duration( + milliseconds: 250), + ), + }, + ); + }, + child: Container( + width: double.infinity, + height: 60, + decoration: BoxDecoration( + color: + FlutterFlowTheme.of(context) + .secondaryBackground, + boxShadow: [ + BoxShadow( + blurRadius: 0, + color: FlutterFlowTheme.of( + context) + .alternate, + offset: Offset( + 0, + 1, + ), + ) + ], + shape: BoxShape.rectangle, + ), + child: Padding( + padding: EdgeInsetsDirectional + .fromSTEB(16, 0, 16, 0), + child: Column( + mainAxisSize: + MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + Row( + mainAxisSize: + MainAxisSize.max, + children: [ + Text( + 'Bug or Feature Request', + style: FlutterFlowTheme + .of(context) + .bodyLarge + .override( + fontFamily: + 'Readex Pro', + color: FlutterFlowTheme.of( + context) + .primaryText, + letterSpacing: + 0, + ), + ), + Expanded( + child: Align( + alignment: + AlignmentDirectional( + 1, 0), + child: Icon( + Icons + .chevron_right_rounded, + color: FlutterFlowTheme + .of(context) + .secondaryText, + size: 20, + ), + ), + ), + ], + ), + ], + ), + ), + ), + ), + ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB( + 0, 0, 0, 1), + child: InkWell( + splashColor: Colors.transparent, + focusColor: Colors.transparent, + hoverColor: Colors.transparent, + highlightColor: Colors.transparent, + onTap: () async { + await showModalBottomSheet( + isScrollControlled: true, + backgroundColor: + Colors.transparent, + enableDrag: false, + useSafeArea: true, + context: context, + builder: (context) { + return Padding( + padding: + MediaQuery.viewInsetsOf( + context), + child: PopupWidget(), + ); + }, + ).then((value) => + safeSetState(() {})); + }, + child: Container( + width: double.infinity, + height: 60, + decoration: BoxDecoration( + color: + FlutterFlowTheme.of(context) + .secondaryBackground, + boxShadow: [ + BoxShadow( + blurRadius: 0, + color: FlutterFlowTheme.of( + context) + .alternate, + offset: Offset( + 0, + 1, + ), + ) + ], + shape: BoxShape.rectangle, + ), + child: Padding( + padding: EdgeInsetsDirectional + .fromSTEB(16, 0, 16, 0), + child: Column( + mainAxisSize: + MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + Row( + mainAxisSize: + MainAxisSize.max, + children: [ + Text( + 'Open-source licenses & other info', + style: FlutterFlowTheme + .of(context) + .bodyLarge + .override( + fontFamily: + 'Readex Pro', + letterSpacing: + 0, + ), + ), + Expanded( + child: Align( + alignment: + AlignmentDirectional( + 1, 0), + child: Icon( + Icons + .chevron_right_rounded, + color: FlutterFlowTheme + .of(context) + .secondaryText, + size: 20, + ), + ), + ), + ], + ), + ], + ), + ), + ), + ), + ), + Align( + alignment: + AlignmentDirectional(0, -1), + child: Padding( + padding: EdgeInsets.all(16), + child: Container( + width: double.infinity, + height: 50, + constraints: BoxConstraints( + maxWidth: 500, + ), + decoration: BoxDecoration( + color: + FlutterFlowTheme.of(context) + .primaryBackground, + borderRadius: + BorderRadius.circular(12), + border: Border.all( + color: FlutterFlowTheme.of( + context) + .alternate, + width: 1, + ), + ), + child: Padding( + padding: EdgeInsets.all(4), + child: Row( + mainAxisSize: + MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.start, + children: [ + Expanded( + child: InkWell( + splashColor: + Colors.transparent, + focusColor: + Colors.transparent, + hoverColor: + Colors.transparent, + highlightColor: + Colors.transparent, + onTap: () async { + setDarkModeSetting( + context, + ThemeMode.light); + }, + child: Container( + width: 115, + height: 100, + decoration: + BoxDecoration( + color: Theme.of(context) + .brightness == + Brightness + .light + ? FlutterFlowTheme.of( + context) + .secondaryBackground + : FlutterFlowTheme.of( + context) + .primaryBackground, + borderRadius: + BorderRadius + .circular( + 10), + border: Border.all( + color: + valueOrDefault< + Color>( + Theme.of(context) + .brightness == + Brightness + .light + ? FlutterFlowTheme.of( + context) + .alternate + : FlutterFlowTheme.of( + context) + .primaryBackground, + FlutterFlowTheme.of( + context) + .alternate, + ), + width: 1, + ), + ), + child: Row( + mainAxisSize: + MainAxisSize + .max, + mainAxisAlignment: + MainAxisAlignment + .center, + children: [ + Icon( + Icons + .wb_sunny_rounded, + color: Theme.of(context) + .brightness == + Brightness + .light + ? FlutterFlowTheme.of( + context) + .primaryText + : FlutterFlowTheme.of( + context) + .secondaryText, + size: 16, + ), + Padding( + padding: + EdgeInsetsDirectional + .fromSTEB( + 4, + 0, + 0, + 0), + child: Text( + 'Light ', + style: FlutterFlowTheme.of( + context) + .bodyMedium + .override( + fontFamily: + 'Readex Pro', + color: Theme.of(context).brightness == + Brightness.light + ? FlutterFlowTheme.of(context).primaryText + : FlutterFlowTheme.of(context).secondaryText, + letterSpacing: + 0, + ), + ), + ), + ], + ), + ), + ), + ), + Expanded( + child: InkWell( + splashColor: + Colors.transparent, + focusColor: + Colors.transparent, + hoverColor: + Colors.transparent, + highlightColor: + Colors.transparent, + onTap: () async { + setDarkModeSetting( + context, + ThemeMode.dark); + }, + child: Container( + width: 115, + height: 100, + decoration: + BoxDecoration( + color: Theme.of(context) + .brightness == + Brightness + .dark + ? FlutterFlowTheme.of( + context) + .secondaryBackground + : FlutterFlowTheme.of( + context) + .primaryBackground, + borderRadius: + BorderRadius + .circular( + 10), + border: Border.all( + color: Theme.of(context) + .brightness == + Brightness + .dark + ? FlutterFlowTheme.of( + context) + .alternate + : FlutterFlowTheme.of( + context) + .primaryBackground, + width: 1, + ), + ), + child: Row( + mainAxisSize: + MainAxisSize + .max, + mainAxisAlignment: + MainAxisAlignment + .center, + children: [ + Icon( + Icons + .nightlight_round, + color: Theme.of(context) + .brightness == + Brightness + .dark + ? FlutterFlowTheme.of( + context) + .primaryText + : FlutterFlowTheme.of( + context) + .secondaryText, + size: 16, + ), + Padding( + padding: + EdgeInsetsDirectional + .fromSTEB( + 4, + 0, + 0, + 0), + child: Text( + 'Dark', + style: FlutterFlowTheme.of( + context) + .bodyMedium + .override( + fontFamily: + 'Readex Pro', + color: Theme.of(context).brightness == + Brightness.dark + ? FlutterFlowTheme.of(context).primaryText + : FlutterFlowTheme.of(context).secondaryText, + letterSpacing: + 0, + ), + ), + ), + ], + ), + ), + ), + ), + ], + ), + ), + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ].addToEnd(SizedBox(height: 72)), + ), + ), + ), + ), + ], + ), + ], + ), + ), + ); + } +} diff --git a/mobile/app/lib/pages/SetupModel.dart b/mobile/app/lib/pages/SetupModel.dart new file mode 100644 index 0000000..959f48a --- /dev/null +++ b/mobile/app/lib/pages/SetupModel.dart @@ -0,0 +1,25 @@ +import '/flutter_flow/flutter_flow_animations.dart'; +import '/flutter_flow/flutter_flow_theme.dart'; +import '/flutter_flow/flutter_flow_util.dart'; +import '/flutter_flow/flutter_flow_widgets.dart'; +import 'dart:math'; +import 'setup_widget.dart' show SetupWidget; +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:flutter_animate/flutter_animate.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:provider/provider.dart'; + +class SetupModel extends FlutterFlowModel { + /// State fields for stateful widgets in this page. + + final unfocusNode = FocusNode(); + + @override + void initState(BuildContext context) {} + + @override + void dispose() { + unfocusNode.dispose(); + } +} diff --git a/mobile/app/lib/pages/SetupWidget.dart b/mobile/app/lib/pages/SetupWidget.dart new file mode 100644 index 0000000..13d9f34 --- /dev/null +++ b/mobile/app/lib/pages/SetupWidget.dart @@ -0,0 +1,325 @@ +import '/flutter_flow/flutter_flow_animations.dart'; +import '/flutter_flow/flutter_flow_theme.dart'; +import '/flutter_flow/flutter_flow_util.dart'; +import '/flutter_flow/flutter_flow_widgets.dart'; +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:flutter_animate/flutter_animate.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:provider/provider.dart'; + +import 'setup_model.dart'; +export 'setup_model.dart'; + +class SetupWidget extends StatefulWidget { + const SetupWidget({super.key}); + + @override + State createState() => _SetupWidgetState(); +} + +class _SetupWidgetState extends State + with TickerProviderStateMixin { + late SetupModel _model; + + final scaffoldKey = GlobalKey(); + + final animationsMap = {}; + + @override + void initState() { + super.initState(); + _model = createModel(context, () => SetupModel()); + + animationsMap.addAll({ + 'containerOnPageLoadAnimation1': AnimationInfo( + trigger: AnimationTrigger.onPageLoad, + effectsBuilder: () => [ + VisibilityEffect(duration: 1.ms), + FadeEffect( + curve: Curves.easeInOut, + delay: 0.0.ms, + duration: 400.0.ms, + begin: 0.0, + end: 1.0, + ), + ScaleEffect( + curve: Curves.easeInOut, + delay: 0.0.ms, + duration: 400.0.ms, + begin: Offset(3.0, 3.0), + end: Offset(1.0, 1.0), + ), + ], + ), + 'containerOnPageLoadAnimation2': AnimationInfo( + trigger: AnimationTrigger.onPageLoad, + effectsBuilder: () => [ + VisibilityEffect(duration: 300.ms), + FadeEffect( + curve: Curves.easeInOut, + delay: 300.0.ms, + duration: 300.0.ms, + begin: 0.0, + end: 1.0, + ), + ScaleEffect( + curve: Curves.bounceOut, + delay: 300.0.ms, + duration: 300.0.ms, + begin: Offset(0.6, 0.6), + end: Offset(1.0, 1.0), + ), + ], + ), + 'textOnPageLoadAnimation1': AnimationInfo( + trigger: AnimationTrigger.onPageLoad, + effectsBuilder: () => [ + VisibilityEffect(duration: 350.ms), + FadeEffect( + curve: Curves.easeInOut, + delay: 350.0.ms, + duration: 400.0.ms, + begin: 0.0, + end: 1.0, + ), + MoveEffect( + curve: Curves.easeInOut, + delay: 350.0.ms, + duration: 400.0.ms, + begin: Offset(0.0, 30.0), + end: Offset(0.0, 0.0), + ), + ], + ), + 'textOnPageLoadAnimation2': AnimationInfo( + trigger: AnimationTrigger.onPageLoad, + effectsBuilder: () => [ + VisibilityEffect(duration: 400.ms), + FadeEffect( + curve: Curves.easeInOut, + delay: 400.0.ms, + duration: 400.0.ms, + begin: 0.0, + end: 1.0, + ), + MoveEffect( + curve: Curves.easeInOut, + delay: 400.0.ms, + duration: 400.0.ms, + begin: Offset(0.0, 30.0), + end: Offset(0.0, 0.0), + ), + ], + ), + 'rowOnPageLoadAnimation': AnimationInfo( + trigger: AnimationTrigger.onPageLoad, + effectsBuilder: () => [ + VisibilityEffect(duration: 300.ms), + FadeEffect( + curve: Curves.easeInOut, + delay: 300.0.ms, + duration: 600.0.ms, + begin: 0.0, + end: 1.0, + ), + ScaleEffect( + curve: Curves.bounceOut, + delay: 300.0.ms, + duration: 600.0.ms, + begin: Offset(0.6, 0.6), + end: Offset(1.0, 1.0), + ), + ], + ), + }); + } + + @override + void dispose() { + _model.dispose(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () => _model.unfocusNode.canRequestFocus + ? FocusScope.of(context).requestFocus(_model.unfocusNode) + : FocusScope.of(context).unfocus(), + child: Scaffold( + key: scaffoldKey, + backgroundColor: FlutterFlowTheme.of(context).secondaryBackground, + body: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Container( + width: double.infinity, + height: 500, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + FlutterFlowTheme.of(context).primary, + FlutterFlowTheme.of(context).error, + FlutterFlowTheme.of(context).tertiary + ], + stops: [0, 0.5, 1], + begin: AlignmentDirectional(-1, -1), + end: AlignmentDirectional(1, 1), + ), + ), + child: Container( + width: 100, + height: 100, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + Color(0x00FFFFFF), + FlutterFlowTheme.of(context).secondaryBackground + ], + stops: [0, 1], + begin: AlignmentDirectional(0, -1), + end: AlignmentDirectional(0, 1), + ), + ), + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: 120, + height: 120, + decoration: BoxDecoration( + color: FlutterFlowTheme.of(context).accent4, + shape: BoxShape.circle, + ), + child: Padding( + padding: EdgeInsets.all(8), + child: Image.network( + 'https://storage.googleapis.com/flutterflow-io-6f20.appspot.com/projects/f-f-templates-q1-23-fbcr63/assets/ax4fvwjz7awx/@4xff_badgeDesign_dark_small.png', + width: 100, + height: 100, + fit: BoxFit.cover, + ), + ), + ).animateOnPageLoad( + animationsMap['containerOnPageLoadAnimation2']!), + Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 44, 0, 0), + child: Text( + 'Welcome!', + style: FlutterFlowTheme.of(context) + .displaySmall + .override( + fontFamily: 'Outfit', + letterSpacing: 0, + ), + ).animateOnPageLoad( + animationsMap['textOnPageLoadAnimation1']!), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB(44, 8, 44, 0), + child: Text( + 'Thanks for joining! Access or create your account below, and get started on your journey!', + textAlign: TextAlign.center, + style: + FlutterFlowTheme.of(context).labelMedium.override( + fontFamily: 'Readex Pro', + letterSpacing: 0, + ), + ).animateOnPageLoad( + animationsMap['textOnPageLoadAnimation2']!), + ), + ], + ), + ), + ).animateOnPageLoad( + animationsMap['containerOnPageLoadAnimation1']!), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB(16, 24, 16, 44), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Expanded( + child: Align( + alignment: AlignmentDirectional(0, 0), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 0, 8, 16), + child: FFButtonWidget( + onPressed: () { + print('Button pressed ...'); + }, + text: 'Get Started', + options: FFButtonOptions( + width: 230, + height: 52, + padding: EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), + iconPadding: + EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), + color: FlutterFlowTheme.of(context) + .secondaryBackground, + textStyle: + FlutterFlowTheme.of(context).bodyLarge.override( + fontFamily: 'Readex Pro', + letterSpacing: 0, + ), + elevation: 0, + borderSide: BorderSide( + color: FlutterFlowTheme.of(context).alternate, + width: 2, + ), + borderRadius: BorderRadius.circular(12), + ), + ), + ), + ), + ), + Expanded( + child: Align( + alignment: AlignmentDirectional(0, 0), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(8, 0, 0, 16), + child: FFButtonWidget( + onPressed: () { + print('Button pressed ...'); + }, + text: 'My Account', + options: FFButtonOptions( + width: 230, + height: 52, + padding: EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), + iconPadding: + EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), + color: FlutterFlowTheme.of(context).primary, + textStyle: FlutterFlowTheme.of(context) + .titleSmall + .override( + fontFamily: 'Readex Pro', + color: Colors.white, + letterSpacing: 0, + ), + elevation: 3, + borderSide: BorderSide( + color: Colors.transparent, + width: 1, + ), + borderRadius: BorderRadius.circular(12), + ), + ), + ), + ), + ), + ], + ).animateOnPageLoad(animationsMap['rowOnPageLoadAnimation']!), + ), + ], + ), + ), + ); + } +} diff --git a/mobile/app/lib/pages/SupportFormModel.dart b/mobile/app/lib/pages/SupportFormModel.dart new file mode 100644 index 0000000..246d59e --- /dev/null +++ b/mobile/app/lib/pages/SupportFormModel.dart @@ -0,0 +1,46 @@ +import '/flutter_flow/flutter_flow_animations.dart'; +import '/flutter_flow/flutter_flow_icon_button.dart'; +import '/flutter_flow/flutter_flow_theme.dart'; +import '/flutter_flow/flutter_flow_util.dart'; +import '/flutter_flow/flutter_flow_widgets.dart'; +import 'dart:math'; +import 'support_form_widget.dart' show SupportFormWidget; +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:flutter_animate/flutter_animate.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:provider/provider.dart'; + +class SupportFormModel extends FlutterFlowModel { + /// State fields for stateful widgets in this page. + + final unfocusNode = FocusNode(); + // State field(s) for TextField widget. + FocusNode? textFieldFocusNode1; + TextEditingController? textController1; + String? Function(BuildContext, String?)? textController1Validator; + // State field(s) for TextField widget. + FocusNode? textFieldFocusNode2; + TextEditingController? textController2; + String? Function(BuildContext, String?)? textController2Validator; + // State field(s) for TextField widget. + FocusNode? textFieldFocusNode3; + TextEditingController? textController3; + String? Function(BuildContext, String?)? textController3Validator; + + @override + void initState(BuildContext context) {} + + @override + void dispose() { + unfocusNode.dispose(); + textFieldFocusNode1?.dispose(); + textController1?.dispose(); + + textFieldFocusNode2?.dispose(); + textController2?.dispose(); + + textFieldFocusNode3?.dispose(); + textController3?.dispose(); + } +} diff --git a/mobile/app/lib/pages/SupportFormWidget.dart b/mobile/app/lib/pages/SupportFormWidget.dart new file mode 100644 index 0000000..cb6740c --- /dev/null +++ b/mobile/app/lib/pages/SupportFormWidget.dart @@ -0,0 +1,576 @@ +import '/flutter_flow/flutter_flow_animations.dart'; +import '/flutter_flow/flutter_flow_icon_button.dart'; +import '/flutter_flow/flutter_flow_theme.dart'; +import '/flutter_flow/flutter_flow_util.dart'; +import '/flutter_flow/flutter_flow_widgets.dart'; +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:flutter_animate/flutter_animate.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:provider/provider.dart'; + +import 'support_form_model.dart'; +export 'support_form_model.dart'; + +class SupportFormWidget extends StatefulWidget { + const SupportFormWidget({super.key}); + + @override + State createState() => _SupportFormWidgetState(); +} + +class _SupportFormWidgetState extends State + with TickerProviderStateMixin { + late SupportFormModel _model; + + final scaffoldKey = GlobalKey(); + + final animationsMap = {}; + + @override + void initState() { + super.initState(); + _model = createModel(context, () => SupportFormModel()); + + _model.textController1 ??= TextEditingController(); + _model.textFieldFocusNode1 ??= FocusNode(); + + _model.textController2 ??= TextEditingController(); + _model.textFieldFocusNode2 ??= FocusNode(); + + _model.textController3 ??= TextEditingController(); + _model.textFieldFocusNode3 ??= FocusNode(); + + animationsMap.addAll({ + 'containerOnPageLoadAnimation1': AnimationInfo( + trigger: AnimationTrigger.onPageLoad, + effectsBuilder: () => [ + FadeEffect( + curve: Curves.easeInOut, + delay: 0.0.ms, + duration: 600.0.ms, + begin: 0.0, + end: 1.0, + ), + MoveEffect( + curve: Curves.easeInOut, + delay: 0.0.ms, + duration: 600.0.ms, + begin: Offset(0.0, 110.0), + end: Offset(0.0, 0.0), + ), + ], + ), + 'containerOnPageLoadAnimation2': AnimationInfo( + trigger: AnimationTrigger.onPageLoad, + effectsBuilder: () => [ + FadeEffect( + curve: Curves.easeInOut, + delay: 0.0.ms, + duration: 600.0.ms, + begin: 0.0, + end: 1.0, + ), + MoveEffect( + curve: Curves.easeInOut, + delay: 0.0.ms, + duration: 600.0.ms, + begin: Offset(0.0, 110.0), + end: Offset(0.0, 0.0), + ), + ], + ), + 'containerOnPageLoadAnimation3': AnimationInfo( + trigger: AnimationTrigger.onPageLoad, + effectsBuilder: () => [ + FadeEffect( + curve: Curves.easeInOut, + delay: 0.0.ms, + duration: 600.0.ms, + begin: 0.0, + end: 1.0, + ), + MoveEffect( + curve: Curves.easeInOut, + delay: 0.0.ms, + duration: 600.0.ms, + begin: Offset(0.0, 110.0), + end: Offset(0.0, 0.0), + ), + ], + ), + }); + setupAnimations( + animationsMap.values.where((anim) => + anim.trigger == AnimationTrigger.onActionTrigger || + !anim.applyInitialState), + this, + ); + } + + @override + void dispose() { + _model.dispose(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () => _model.unfocusNode.canRequestFocus + ? FocusScope.of(context).requestFocus(_model.unfocusNode) + : FocusScope.of(context).unfocus(), + child: Scaffold( + key: scaffoldKey, + backgroundColor: FlutterFlowTheme.of(context).secondaryBackground, + appBar: AppBar( + backgroundColor: FlutterFlowTheme.of(context).secondaryBackground, + automaticallyImplyLeading: false, + leading: FlutterFlowIconButton( + borderColor: Colors.transparent, + borderRadius: 30, + borderWidth: 1, + buttonSize: 60, + icon: Icon( + Icons.arrow_back_rounded, + color: FlutterFlowTheme.of(context).primaryText, + size: 30, + ), + onPressed: () async { + context.pop(); + }, + ), + title: Text( + 'Submit Ticket', + style: FlutterFlowTheme.of(context).titleLarge.override( + fontFamily: 'Outfit', + letterSpacing: 0, + ), + ), + actions: [], + centerTitle: false, + elevation: 0, + ), + body: SafeArea( + top: true, + child: Align( + alignment: AlignmentDirectional(0, 1), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(16, 12, 16, 0), + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Welcome to support', + style: FlutterFlowTheme.of(context).labelLarge.override( + fontFamily: 'Readex Pro', + letterSpacing: 0, + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 4, 0, 0), + child: Text( + 'Submit a bug', + style: FlutterFlowTheme.of(context) + .headlineMedium + .override( + fontFamily: 'Outfit', + letterSpacing: 0, + ), + ), + ), + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Expanded( + child: Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 16, 0, 0), + child: Container( + width: 120, + constraints: BoxConstraints( + maxWidth: 500, + ), + decoration: BoxDecoration( + color: FlutterFlowTheme.of(context) + .secondaryBackground, + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: FlutterFlowTheme.of(context).alternate, + width: 2, + ), + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 8, 16, 8, 16), + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Icon( + Icons.email_outlined, + color: + FlutterFlowTheme.of(context).primary, + size: 36, + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0, 12, 0, 0), + child: Text( + 'Email Us', + textAlign: TextAlign.center, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Readex Pro', + letterSpacing: 0, + ), + ), + ), + ], + ), + ), + ).animateOnPageLoad(animationsMap[ + 'containerOnPageLoadAnimation1']!), + ), + ), + Expanded( + child: Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 16, 0, 0), + child: Container( + width: 120, + constraints: BoxConstraints( + maxWidth: 500, + ), + decoration: BoxDecoration( + color: FlutterFlowTheme.of(context) + .secondaryBackground, + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: FlutterFlowTheme.of(context).alternate, + width: 2, + ), + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 8, 16, 8, 16), + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Icon( + Icons.search_rounded, + color: + FlutterFlowTheme.of(context).primary, + size: 36, + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0, 12, 0, 0), + child: Text( + 'Search FAQs', + textAlign: TextAlign.center, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Readex Pro', + letterSpacing: 0, + ), + ), + ), + ], + ), + ), + ).animateOnPageLoad(animationsMap[ + 'containerOnPageLoadAnimation2']!), + ), + ), + ].divide(SizedBox(width: 12)), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 16, 0, 0), + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + TextFormField( + controller: _model.textController1, + focusNode: _model.textFieldFocusNode1, + autofocus: true, + obscureText: false, + decoration: InputDecoration( + labelText: 'Channel Name', + labelStyle: FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: 'Readex Pro', + letterSpacing: 0, + ), + hintStyle: FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: 'Readex Pro', + letterSpacing: 0, + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: FlutterFlowTheme.of(context).alternate, + width: 2, + ), + borderRadius: BorderRadius.circular(12), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: FlutterFlowTheme.of(context).primary, + width: 2, + ), + borderRadius: BorderRadius.circular(12), + ), + errorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: FlutterFlowTheme.of(context).error, + width: 2, + ), + borderRadius: BorderRadius.circular(12), + ), + focusedErrorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: FlutterFlowTheme.of(context).error, + width: 2, + ), + borderRadius: BorderRadius.circular(12), + ), + contentPadding: EdgeInsetsDirectional.fromSTEB( + 16, 12, 16, 12), + ), + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Readex Pro', + letterSpacing: 0, + ), + cursorColor: FlutterFlowTheme.of(context).primary, + validator: _model.textController1Validator + .asValidator(context), + ), + TextFormField( + controller: _model.textController2, + focusNode: _model.textFieldFocusNode2, + autofocus: true, + obscureText: false, + decoration: InputDecoration( + labelText: 'Channel ID', + labelStyle: FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: 'Readex Pro', + letterSpacing: 0, + ), + hintStyle: FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: 'Readex Pro', + letterSpacing: 0, + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: FlutterFlowTheme.of(context).alternate, + width: 2, + ), + borderRadius: BorderRadius.circular(12), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: FlutterFlowTheme.of(context).primary, + width: 2, + ), + borderRadius: BorderRadius.circular(12), + ), + errorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: FlutterFlowTheme.of(context).error, + width: 2, + ), + borderRadius: BorderRadius.circular(12), + ), + focusedErrorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: FlutterFlowTheme.of(context).error, + width: 2, + ), + borderRadius: BorderRadius.circular(12), + ), + contentPadding: EdgeInsetsDirectional.fromSTEB( + 16, 12, 16, 12), + ), + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Readex Pro', + letterSpacing: 0, + ), + cursorColor: FlutterFlowTheme.of(context).primary, + validator: _model.textController2Validator + .asValidator(context), + ), + TextFormField( + controller: _model.textController3, + focusNode: _model.textFieldFocusNode3, + autofocus: true, + obscureText: false, + decoration: InputDecoration( + labelStyle: FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: 'Readex Pro', + letterSpacing: 0, + ), + hintText: + 'Short Description of what is going on...', + hintStyle: FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: 'Readex Pro', + letterSpacing: 0, + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: FlutterFlowTheme.of(context).alternate, + width: 2, + ), + borderRadius: BorderRadius.circular(12), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: FlutterFlowTheme.of(context).primary, + width: 2, + ), + borderRadius: BorderRadius.circular(12), + ), + errorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: FlutterFlowTheme.of(context).error, + width: 2, + ), + borderRadius: BorderRadius.circular(12), + ), + focusedErrorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: FlutterFlowTheme.of(context).error, + width: 2, + ), + borderRadius: BorderRadius.circular(12), + ), + contentPadding: EdgeInsetsDirectional.fromSTEB( + 16, 24, 16, 12), + ), + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Readex Pro', + letterSpacing: 0, + ), + maxLines: 16, + minLines: 6, + cursorColor: FlutterFlowTheme.of(context).primary, + validator: _model.textController3Validator + .asValidator(context), + ), + ].divide(SizedBox(height: 12)), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 16, 0, 0), + child: Container( + width: double.infinity, + constraints: BoxConstraints( + maxWidth: 500, + ), + decoration: BoxDecoration( + color: + FlutterFlowTheme.of(context).secondaryBackground, + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: FlutterFlowTheme.of(context).alternate, + width: 2, + ), + ), + child: Padding( + padding: EdgeInsets.all(8), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Icon( + Icons.add_a_photo_rounded, + color: FlutterFlowTheme.of(context).primary, + size: 32, + ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(16, 0, 0, 0), + child: Text( + 'Upload Screenshot', + textAlign: TextAlign.center, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Readex Pro', + letterSpacing: 0, + ), + ), + ), + ], + ), + ), + ).animateOnPageLoad( + animationsMap['containerOnPageLoadAnimation3']!), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 24, 0, 12), + child: FFButtonWidget( + onPressed: () { + print('Button pressed ...'); + }, + text: 'Submit Ticket', + icon: Icon( + Icons.receipt_long, + size: 15, + ), + options: FFButtonOptions( + width: double.infinity, + height: 48, + padding: EdgeInsets.all(0), + iconPadding: + EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), + color: FlutterFlowTheme.of(context).primary, + textStyle: + FlutterFlowTheme.of(context).titleSmall.override( + fontFamily: 'Readex Pro', + color: Colors.white, + letterSpacing: 0, + ), + elevation: 4, + borderSide: BorderSide( + color: Colors.transparent, + width: 1, + ), + borderRadius: BorderRadius.circular(60), + ), + ), + ), + ], + ), + ), + ), + ), + ), + ), + ); + } +} diff --git a/mobile/app/linux/.gitignore b/mobile/app/linux/.gitignore new file mode 100644 index 0000000..d3896c9 --- /dev/null +++ b/mobile/app/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/mobile/app/linux/CMakeLists.txt b/mobile/app/linux/CMakeLists.txt new file mode 100644 index 0000000..b99d63d --- /dev/null +++ b/mobile/app/linux/CMakeLists.txt @@ -0,0 +1,145 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "app") +# The unique GTK application identifier for this application. See: +# https://wiki.gnome.org/HowDoI/ChooseApplicationID +set(APPLICATION_ID "com.example.app") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Load bundled libraries from the lib/ directory relative to the binary. +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Root filesystem for cross-building. +if(FLUTTER_TARGET_PLATFORM_SYSROOT) + set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +endif() + +# Define build configuration options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Define the application target. To change its name, change BINARY_NAME above, +# not the value here, or `flutter run` will no longer work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add dependency libraries. Add any application-specific dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) + install(FILES "${bundled_library}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endforeach(bundled_library) + +# 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/mobile/app/linux/flutter/CMakeLists.txt b/mobile/app/linux/flutter/CMakeLists.txt new file mode 100644 index 0000000..d5bd016 --- /dev/null +++ b/mobile/app/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/mobile/app/linux/flutter/generated_plugin_registrant.cc b/mobile/app/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 0000000..e71a16d --- /dev/null +++ b/mobile/app/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/mobile/app/linux/flutter/generated_plugin_registrant.h b/mobile/app/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 0000000..e0f0a47 --- /dev/null +++ b/mobile/app/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/mobile/app/linux/flutter/generated_plugins.cmake b/mobile/app/linux/flutter/generated_plugins.cmake new file mode 100644 index 0000000..2e1de87 --- /dev/null +++ b/mobile/app/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/mobile/app/linux/main.cc b/mobile/app/linux/main.cc new file mode 100644 index 0000000..e7c5c54 --- /dev/null +++ b/mobile/app/linux/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/mobile/app/linux/my_application.cc b/mobile/app/linux/my_application.cc new file mode 100644 index 0000000..c4b824f --- /dev/null +++ b/mobile/app/linux/my_application.cc @@ -0,0 +1,124 @@ +#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, "app"); + 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, "app"); + } + + 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() { + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, + "flags", G_APPLICATION_NON_UNIQUE, + nullptr)); +} diff --git a/mobile/app/linux/my_application.h b/mobile/app/linux/my_application.h new file mode 100644 index 0000000..72271d5 --- /dev/null +++ b/mobile/app/linux/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/mobile/app/macos/.gitignore b/mobile/app/macos/.gitignore new file mode 100644 index 0000000..746adbb --- /dev/null +++ b/mobile/app/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/mobile/app/macos/Flutter/Flutter-Debug.xcconfig b/mobile/app/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 0000000..c2efd0b --- /dev/null +++ b/mobile/app/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/mobile/app/macos/Flutter/Flutter-Release.xcconfig b/mobile/app/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 0000000..c2efd0b --- /dev/null +++ b/mobile/app/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/mobile/app/macos/Flutter/GeneratedPluginRegistrant.swift b/mobile/app/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 0000000..cccf817 --- /dev/null +++ b/mobile/app/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,10 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { +} diff --git a/mobile/app/macos/Runner.xcodeproj/project.pbxproj b/mobile/app/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..45a7908 --- /dev/null +++ b/mobile/app/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 /* app.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "app.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 /* app.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 /* app.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.app.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/app.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/app"; + }; + 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.app.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/app.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/app"; + }; + 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.app.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/app.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/app"; + }; + 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/mobile/app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/mobile/app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/mobile/app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/mobile/app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/mobile/app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..c8582ae --- /dev/null +++ b/mobile/app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/app/macos/Runner.xcworkspace/contents.xcworkspacedata b/mobile/app/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..1d526a1 --- /dev/null +++ b/mobile/app/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/mobile/app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/mobile/app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/mobile/app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/mobile/app/macos/Runner/AppDelegate.swift b/mobile/app/macos/Runner/AppDelegate.swift new file mode 100644 index 0000000..d53ef64 --- /dev/null +++ b/mobile/app/macos/Runner/AppDelegate.swift @@ -0,0 +1,9 @@ +import Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/mobile/app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/mobile/app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..a2ec33f --- /dev/null +++ b/mobile/app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/mobile/app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/mobile/app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000000000000000000000000000000000000..82b6f9d9a33e198f5747104729e1fcef999772a5 GIT binary patch literal 102994 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ⅅ8MglG&ER{i5lN315cO?EdHNLrg? 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 z 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 Pe7HEdAEg|=c=LY&YVNkY literal 0 HcmV?d00001 diff --git a/mobile/app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/mobile/app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000000000000000000000000000000000000..13b35eba55c6dabc3aac36f33d859266c18fa0d0 GIT binary patch literal 5680 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 literal 0 HcmV?d00001 diff --git a/mobile/app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/mobile/app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 0000000000000000000000000000000000000000..0a3f5fa40fb3d1e0710331a48de5d256da3f275d GIT binary patch literal 520 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 literal 0 HcmV?d00001 diff --git a/mobile/app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/mobile/app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 0000000000000000000000000000000000000000..2f1632cfddf3d9dade342351e627a0a75609fb46 GIT binary patch literal 2218 zcmV;b2vzrqP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91K%fHv1ONa40RR91KmY&$07g+lumAuE6iGxuRCodHTWf3-RTMruyW6Fu zQYeUM04eX6D5c0FCjKKPrco1(K`<0SL=crI{PC3-^hZU0kQie$gh-5!7z6SH6Q0J% zqot*`H1q{R5fHFYS}dje@;kG=v$L0(yY0?wY2%*c?A&{2?!D*x?m71{of2gv!$5|C z3>qG_BW}7K_yUcT3A5C6QD<+{aq?x;MAUyAiJn#Jv8_zZtQ{P zTRzbL3U9!qVuZzS$xKU10KiW~Bgdcv1-!uAhQxf3a7q+dU6lj?yoO4Lq4TUN4}h{N z*fIM=SS8|C2$(T>w$`t@3Tka!(r!7W`x z-isCVgQD^mG-MJ;XtJuK3V{Vy72GQ83KRWsHU?e*wrhKk=ApIYeDqLi;JI1e zuvv}5^Dc=k7F7?nm3nIw$NVmU-+R>> zyqOR$-2SDpJ}Pt;^RkJytDVXNTsu|mI1`~G7yw`EJR?VkGfNdqK9^^8P`JdtTV&tX4CNcV4 z&N06nZa??Fw1AgQOUSE2AmPE@WO(Fvo`%m`cDgiv(fAeRA%3AGXUbsGw{7Q`cY;1BI#ac3iN$$Hw z0LT0;xc%=q)me?Y*$xI@GRAw?+}>=9D+KTk??-HJ4=A>`V&vKFS75@MKdSF1JTq{S zc1!^8?YA|t+uKigaq!sT;Z!&0F2=k7F0PIU;F$leJLaw2UI6FL^w}OG&!;+b%ya1c z1n+6-inU<0VM-Y_s5iTElq)ThyF?StVcebpGI znw#+zLx2@ah{$_2jn+@}(zJZ{+}_N9BM;z)0yr|gF-4=Iyu@hI*Lk=-A8f#bAzc9f z`Kd6K--x@t04swJVC3JK1cHY-Hq+=|PN-VO;?^_C#;coU6TDP7Bt`;{JTG;!+jj(` zw5cLQ-(Cz-Tlb`A^w7|R56Ce;Wmr0)$KWOUZ6ai0PhzPeHwdl0H(etP zUV`va_i0s-4#DkNM8lUlqI7>YQLf)(lz9Q3Uw`)nc(z3{m5ZE77Ul$V%m)E}3&8L0 z-XaU|eB~Is08eORPk;=<>!1w)Kf}FOVS2l&9~A+@R#koFJ$Czd%Y(ENTV&A~U(IPI z;UY+gf+&6ioZ=roly<0Yst8ck>(M=S?B-ys3mLdM&)ex!hbt+ol|T6CTS+Sc0jv(& z7ijdvFwBq;0a{%3GGwkDKTeG`b+lyj0jjS1OMkYnepCdoosNY`*zmBIo*981BU%%U z@~$z0V`OVtIbEx5pa|Tct|Lg#ZQf5OYMUMRD>Wdxm5SAqV2}3!ceE-M2 z@O~lQ0OiKQp}o9I;?uxCgYVV?FH|?Riri*U$Zi_`V2eiA>l zdSm6;SEm6#T+SpcE8Ro_f2AwxzI z44hfe^WE3!h@W3RDyA_H440cpmYkv*)6m1XazTqw%=E5Xv7^@^^T7Q2wxr+Z2kVYr + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/app/macos/Runner/Configs/AppInfo.xcconfig b/mobile/app/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 0000000..85215c1 --- /dev/null +++ b/mobile/app/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 = app + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.example.app + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2024 com.example. All rights reserved. diff --git a/mobile/app/macos/Runner/Configs/Debug.xcconfig b/mobile/app/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 0000000..36b0fd9 --- /dev/null +++ b/mobile/app/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/mobile/app/macos/Runner/Configs/Release.xcconfig b/mobile/app/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 0000000..dff4f49 --- /dev/null +++ b/mobile/app/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/mobile/app/macos/Runner/Configs/Warnings.xcconfig b/mobile/app/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 0000000..42bcbf4 --- /dev/null +++ b/mobile/app/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/mobile/app/macos/Runner/DebugProfile.entitlements b/mobile/app/macos/Runner/DebugProfile.entitlements new file mode 100644 index 0000000..dddb8a3 --- /dev/null +++ b/mobile/app/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/mobile/app/macos/Runner/Info.plist b/mobile/app/macos/Runner/Info.plist new file mode 100644 index 0000000..4789daa --- /dev/null +++ b/mobile/app/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/mobile/app/macos/Runner/MainFlutterWindow.swift b/mobile/app/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 0000000..3cc05eb --- /dev/null +++ b/mobile/app/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/mobile/app/macos/Runner/Release.entitlements b/mobile/app/macos/Runner/Release.entitlements new file mode 100644 index 0000000..852fa1a --- /dev/null +++ b/mobile/app/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/mobile/app/macos/RunnerTests/RunnerTests.swift b/mobile/app/macos/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000..61f3bd1 --- /dev/null +++ b/mobile/app/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/mobile/app/pubspec.lock b/mobile/app/pubspec.lock new file mode 100644 index 0000000..ed4160b --- /dev/null +++ b/mobile/app/pubspec.lock @@ -0,0 +1,213 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" + source: hosted + version: "1.18.0" + 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: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + url: "https://pub.dev" + source: hosted + version: "10.0.4" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + url: "https://pub.dev" + source: hosted + version: "3.0.3" + 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: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 + url: "https://pub.dev" + source: hosted + version: "3.0.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + url: "https://pub.dev" + source: hosted + version: "0.12.16+1" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + url: "https://pub.dev" + source: hosted + version: "0.8.0" + meta: + dependency: transitive + description: + name: meta + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + url: "https://pub.dev" + source: hosted + version: "1.12.0" + path: + dependency: transitive + description: + name: path + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + url: "https://pub.dev" + source: hosted + version: "1.9.0" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" + source: hosted + version: "1.11.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" + source: hosted + version: "2.1.2" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + url: "https://pub.dev" + source: hosted + version: "0.7.0" + 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: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + url: "https://pub.dev" + source: hosted + version: "14.2.1" +sdks: + dart: ">=3.4.3 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/mobile/app/pubspec.yaml b/mobile/app/pubspec.yaml new file mode 100644 index 0000000..11f607d --- /dev/null +++ b/mobile/app/pubspec.yaml @@ -0,0 +1,90 @@ +name: app +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.4.3 <4.0.0' + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.6 + +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: ^3.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter 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: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # 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/custom-fonts/#from-packages diff --git a/mobile/app/test/widget_test.dart b/mobile/app/test/widget_test.dart new file mode 100644 index 0000000..4e2a713 --- /dev/null +++ b/mobile/app/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:app/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/mobile/app/web/favicon.png b/mobile/app/web/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8aaa46ac1ae21512746f852a42ba87e4165dfdd1 GIT binary patch literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM literal 0 HcmV?d00001 diff --git a/mobile/app/web/icons/Icon-192.png b/mobile/app/web/icons/Icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b749bfef07473333cf1dd31e9eed89862a5d52aa GIT binary patch literal 5292 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 literal 0 HcmV?d00001 diff --git a/mobile/app/web/icons/Icon-512.png b/mobile/app/web/icons/Icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..88cfd48dff1169879ba46840804b412fe02fefd6 GIT binary patch literal 8252 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 literal 0 HcmV?d00001 diff --git a/mobile/app/web/icons/Icon-maskable-192.png b/mobile/app/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000000000000000000000000000000000000..eb9b4d76e525556d5d89141648c724331630325d GIT binary patch literal 5594 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 X>c!)3PgKfL&lX^$Z>Cpu&6)6jvi^Z! literal 0 HcmV?d00001 diff --git a/mobile/app/web/icons/Icon-maskable-512.png b/mobile/app/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000000000000000000000000000000000000..d69c56691fbdb0b7efa65097c7cc1edac12a6d3e GIT binary patch literal 20998 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 MPgg&ebxsLQ025IeI{*Lx literal 0 HcmV?d00001 diff --git a/mobile/app/web/index.html b/mobile/app/web/index.html new file mode 100644 index 0000000..f02f705 --- /dev/null +++ b/mobile/app/web/index.html @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + app + + + + + + diff --git a/mobile/app/web/manifest.json b/mobile/app/web/manifest.json new file mode 100644 index 0000000..5620a33 --- /dev/null +++ b/mobile/app/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "app", + "short_name": "app", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "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" + } + ] +} diff --git a/mobile/app/windows/.gitignore b/mobile/app/windows/.gitignore new file mode 100644 index 0000000..d492d0d --- /dev/null +++ b/mobile/app/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/mobile/app/windows/CMakeLists.txt b/mobile/app/windows/CMakeLists.txt new file mode 100644 index 0000000..19c9efd --- /dev/null +++ b/mobile/app/windows/CMakeLists.txt @@ -0,0 +1,108 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(app 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 "app") + +# 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/mobile/app/windows/flutter/CMakeLists.txt b/mobile/app/windows/flutter/CMakeLists.txt new file mode 100644 index 0000000..903f489 --- /dev/null +++ b/mobile/app/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/mobile/app/windows/flutter/generated_plugin_registrant.cc b/mobile/app/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 0000000..8b6d468 --- /dev/null +++ b/mobile/app/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,11 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + + +void RegisterPlugins(flutter::PluginRegistry* registry) { +} diff --git a/mobile/app/windows/flutter/generated_plugin_registrant.h b/mobile/app/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 0000000..dc139d8 --- /dev/null +++ b/mobile/app/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/mobile/app/windows/flutter/generated_plugins.cmake b/mobile/app/windows/flutter/generated_plugins.cmake new file mode 100644 index 0000000..b93c4c3 --- /dev/null +++ b/mobile/app/windows/flutter/generated_plugins.cmake @@ -0,0 +1,23 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/mobile/app/windows/runner/CMakeLists.txt b/mobile/app/windows/runner/CMakeLists.txt new file mode 100644 index 0000000..394917c --- /dev/null +++ b/mobile/app/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/mobile/app/windows/runner/Runner.rc b/mobile/app/windows/runner/Runner.rc new file mode 100644 index 0000000..7cd9f35 --- /dev/null +++ b/mobile/app/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", "app" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "app" "\0" + VALUE "LegalCopyright", "Copyright (C) 2024 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "app.exe" "\0" + VALUE "ProductName", "app" "\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/mobile/app/windows/runner/flutter_window.cpp b/mobile/app/windows/runner/flutter_window.cpp new file mode 100644 index 0000000..955ee30 --- /dev/null +++ b/mobile/app/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/mobile/app/windows/runner/flutter_window.h b/mobile/app/windows/runner/flutter_window.h new file mode 100644 index 0000000..6da0652 --- /dev/null +++ b/mobile/app/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/mobile/app/windows/runner/main.cpp b/mobile/app/windows/runner/main.cpp new file mode 100644 index 0000000..bc9bfa3 --- /dev/null +++ b/mobile/app/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"app", 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/mobile/app/windows/runner/resource.h b/mobile/app/windows/runner/resource.h new file mode 100644 index 0000000..66a65d1 --- /dev/null +++ b/mobile/app/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/mobile/app/windows/runner/resources/app_icon.ico b/mobile/app/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 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 literal 0 HcmV?d00001 diff --git a/mobile/app/windows/runner/runner.exe.manifest b/mobile/app/windows/runner/runner.exe.manifest new file mode 100644 index 0000000..a42ea76 --- /dev/null +++ b/mobile/app/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/mobile/app/windows/runner/utils.cpp b/mobile/app/windows/runner/utils.cpp new file mode 100644 index 0000000..3a0b465 --- /dev/null +++ b/mobile/app/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/mobile/app/windows/runner/utils.h b/mobile/app/windows/runner/utils.h new file mode 100644 index 0000000..3879d54 --- /dev/null +++ b/mobile/app/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/mobile/app/windows/runner/win32_window.cpp b/mobile/app/windows/runner/win32_window.cpp new file mode 100644 index 0000000..60608d0 --- /dev/null +++ b/mobile/app/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/mobile/app/windows/runner/win32_window.h b/mobile/app/windows/runner/win32_window.h new file mode 100644 index 0000000..e901dde --- /dev/null +++ b/mobile/app/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_ diff --git a/requirements.txt b/server/requirements.txt similarity index 100% rename from requirements.txt rename to server/requirements.txt diff --git a/stt.py b/server/stt.py similarity index 100% rename from stt.py rename to server/stt.py From dfa84c4acf23deb32fdcfa74229ea8f30b82657f Mon Sep 17 00:00:00 2001 From: Ashton Date: Fri, 5 Jul 2024 21:26:57 -0600 Subject: [PATCH 03/24] fixed project structure issues. now removing flutterflow dependencies. --- .../conversation_bubbles_model.dart} | 0 .../conversation_bubbles_widget.dart} | 0 .../conversation_details_overlay_model.dart} | 4 +- .../conversation_details_overlay_widget.dart} | 29 +- .../conversation_options_model.dart} | 0 .../conversation_options_widget.dart} | 34 +- .../conversation_thread_model.dart} | 0 .../conversation_thread_widget.dart} | 24 +- .../model_item_model.dart} | 0 .../model_item_widget.dart} | 20 +- ...{NoChatsModel.dart => no_chats_model.dart} | 0 ...oChatsWidget.dart => no_chats_widget.dart} | 10 +- .../options_dialog_model.dart} | 0 .../options_dialog_widget.dart} | 54 ++-- .../popup_model.dart} | 0 .../popup_widget.dart} | 50 +-- .../prompt_box_model.dart} | 0 .../prompt_box_widget.dart} | 22 +- ...tionModel.dart => conversation_model.dart} | 0 ...onWidget.dart => conversation_widget.dart} | 4 +- ...del.dart => conversations_list_model.dart} | 0 ...et.dart => conversations_list_widget.dart} | 16 +- ...ilsModel.dart => image_details_model.dart} | 2 - ...sWidget.dart => image_details_widget.dart} | 44 ++- ...SettingsModel.dart => settings_model.dart} | 2 - ...ttingsWidget.dart => settings_widget.dart} | 190 ++++++------ .../{SetupModel.dart => setup_model.dart} | 2 - .../{SetupWidget.dart => setup_widget.dart} | 62 ++-- ...FormModel.dart => support_form_model.dart} | 2 - ...rmWidget.dart => support_form_widget.dart} | 68 ++-- .../Flutter/GeneratedPluginRegistrant.swift | 4 + mobile/app/pubspec.lock | 290 +++++++++++++++++- mobile/app/pubspec.yaml | 6 + 33 files changed, 609 insertions(+), 330 deletions(-) rename mobile/app/lib/components/{ConversationBubblesModel.dart => conversation_bubbles/conversation_bubbles_model.dart} (100%) rename mobile/app/lib/components/{ConversationBubblesWidget.dart => conversation_bubbles/conversation_bubbles_widget.dart} (100%) rename mobile/app/lib/components/{ConversationDetailsOverlayModel.dart => conversation_details_overlay/conversation_details_overlay_model.dart} (89%) rename mobile/app/lib/components/{ConversationDetailsOverlayWidget.dart => conversation_details_overlay/conversation_details_overlay_widget.dart} (88%) rename mobile/app/lib/components/{ConversationOptionsModel.dart => conversation_options/conversation_options_model.dart} (100%) rename mobile/app/lib/components/{ConversationOptionsWidget.dart => conversation_options/conversation_options_widget.dart} (88%) rename mobile/app/lib/components/{ConversationThreadModel.dart => conversation_thread/conversation_thread_model.dart} (100%) rename mobile/app/lib/components/{ConversationThreadWidget.dart => conversation_thread/conversation_thread_widget.dart} (88%) rename mobile/app/lib/components/{ModelItemModel.dart => model_item/model_item_model.dart} (100%) rename mobile/app/lib/components/{ModelItemWidget.dart => model_item/model_item_widget.dart} (92%) rename mobile/app/lib/components/{NoChatsModel.dart => no_chats_model.dart} (100%) rename mobile/app/lib/components/{NoChatsWidget.dart => no_chats_widget.dart} (86%) rename mobile/app/lib/components/{OptionsDialogModel.dart => options_dialog/options_dialog_model.dart} (100%) rename mobile/app/lib/components/{PopupWidget.dart => options_dialog/options_dialog_widget.dart} (80%) rename mobile/app/lib/components/{PopupModel.dart => popup/popup_model.dart} (100%) rename mobile/app/lib/components/{OptionsDialogWidget.dart => popup/popup_widget.dart} (81%) rename mobile/app/lib/components/{PromptBoxModel.dart => prompt_box/prompt_box_model.dart} (100%) rename mobile/app/lib/components/{PromptBoxWidget.dart => prompt_box/prompt_box_widget.dart} (93%) rename mobile/app/lib/pages/{ConversationModel.dart => conversation_model.dart} (100%) rename mobile/app/lib/pages/{ConversationWidget.dart => conversation_widget.dart} (96%) rename mobile/app/lib/pages/{ConversationsListModel.dart => conversations_list_model.dart} (100%) rename mobile/app/lib/pages/{ConversationsListWidget.dart => conversations_list_widget.dart} (94%) rename mobile/app/lib/pages/{ImageDetailsModel.dart => image_details_model.dart} (93%) rename mobile/app/lib/pages/{ImageDetailsWidget.dart => image_details_widget.dart} (87%) rename mobile/app/lib/pages/{SettingsModel.dart => settings_model.dart} (91%) rename mobile/app/lib/pages/{SettingsWidget.dart => settings_widget.dart} (93%) rename mobile/app/lib/pages/{SetupModel.dart => setup_model.dart} (91%) rename mobile/app/lib/pages/{SetupWidget.dart => setup_widget.dart} (84%) rename mobile/app/lib/pages/{SupportFormModel.dart => support_form_model.dart} (96%) rename mobile/app/lib/pages/{SupportFormWidget.dart => support_form_widget.dart} (91%) diff --git a/mobile/app/lib/components/ConversationBubblesModel.dart b/mobile/app/lib/components/conversation_bubbles/conversation_bubbles_model.dart similarity index 100% rename from mobile/app/lib/components/ConversationBubblesModel.dart rename to mobile/app/lib/components/conversation_bubbles/conversation_bubbles_model.dart diff --git a/mobile/app/lib/components/ConversationBubblesWidget.dart b/mobile/app/lib/components/conversation_bubbles/conversation_bubbles_widget.dart similarity index 100% rename from mobile/app/lib/components/ConversationBubblesWidget.dart rename to mobile/app/lib/components/conversation_bubbles/conversation_bubbles_widget.dart diff --git a/mobile/app/lib/components/ConversationDetailsOverlayModel.dart b/mobile/app/lib/components/conversation_details_overlay/conversation_details_overlay_model.dart similarity index 89% rename from mobile/app/lib/components/ConversationDetailsOverlayModel.dart rename to mobile/app/lib/components/conversation_details_overlay/conversation_details_overlay_model.dart index 8767242..ee7aede 100644 --- a/mobile/app/lib/components/ConversationDetailsOverlayModel.dart +++ b/mobile/app/lib/components/conversation_details_overlay/conversation_details_overlay_model.dart @@ -3,10 +3,8 @@ import '/flutter_flow/flutter_flow_icon_button.dart'; import '/flutter_flow/flutter_flow_theme.dart'; import '/flutter_flow/flutter_flow_util.dart'; import '/flutter_flow/flutter_flow_widgets.dart'; -import 'dart:ui'; -import 'conversation_details_overlay_widget.dart' +import '../conversation_details_overlay_widget.dart' show ConversationDetailsOverlayWidget; -import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; diff --git a/mobile/app/lib/components/ConversationDetailsOverlayWidget.dart b/mobile/app/lib/components/conversation_details_overlay/conversation_details_overlay_widget.dart similarity index 88% rename from mobile/app/lib/components/ConversationDetailsOverlayWidget.dart rename to mobile/app/lib/components/conversation_details_overlay/conversation_details_overlay_widget.dart index 0386225..da46e5c 100644 --- a/mobile/app/lib/components/ConversationDetailsOverlayWidget.dart +++ b/mobile/app/lib/components/conversation_details_overlay/conversation_details_overlay_widget.dart @@ -4,7 +4,6 @@ import '/flutter_flow/flutter_flow_theme.dart'; import '/flutter_flow/flutter_flow_util.dart'; import '/flutter_flow/flutter_flow_widgets.dart'; import 'dart:ui'; -import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; @@ -55,20 +54,20 @@ class _ConversationDetailsOverlayWidgetState child: Container( width: double.infinity, height: double.infinity, - decoration: BoxDecoration(), + decoration: const BoxDecoration(), child: Align( - alignment: AlignmentDirectional(0, -1), + alignment: const AlignmentDirectional(0, -1), child: Container( width: double.infinity, height: double.infinity, - constraints: BoxConstraints( + constraints: const BoxConstraints( maxWidth: 700, ), decoration: BoxDecoration( borderRadius: BorderRadius.circular(0), ), child: Padding( - padding: EdgeInsetsDirectional.fromSTEB(0, 70, 0, 0), + padding: const EdgeInsetsDirectional.fromSTEB(0, 70, 0, 0), child: Column( mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.start, @@ -78,7 +77,7 @@ class _ConversationDetailsOverlayWidgetState mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Padding( - padding: EdgeInsetsDirectional.fromSTEB(16, 0, 0, 0), + padding: const EdgeInsetsDirectional.fromSTEB(16, 0, 0, 0), child: Text( 'Chat Details', style: FlutterFlowTheme.of(context) @@ -90,7 +89,7 @@ class _ConversationDetailsOverlayWidgetState ), ), Padding( - padding: EdgeInsetsDirectional.fromSTEB(0, 0, 16, 0), + padding: const EdgeInsetsDirectional.fromSTEB(0, 0, 16, 0), child: FlutterFlowIconButton( borderColor: FlutterFlowTheme.of(context).alternate, borderRadius: 12, @@ -110,12 +109,12 @@ class _ConversationDetailsOverlayWidgetState ], ), Padding( - padding: EdgeInsetsDirectional.fromSTEB(16, 4, 0, 4), + padding: const EdgeInsetsDirectional.fromSTEB(16, 4, 0, 4), child: RichText( textScaler: MediaQuery.of(context).textScaler, text: TextSpan( children: [ - TextSpan( + const TextSpan( text: 'Group Chat ID: ', style: TextStyle(), ), @@ -137,7 +136,7 @@ class _ConversationDetailsOverlayWidgetState ), Expanded( child: Align( - alignment: AlignmentDirectional(0, 0), + alignment: const AlignmentDirectional(0, 0), child: Column( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.start, @@ -151,9 +150,9 @@ class _ConversationDetailsOverlayWidgetState ), ), Align( - alignment: AlignmentDirectional(0, -1), + alignment: const AlignmentDirectional(0, -1), child: Padding( - padding: EdgeInsetsDirectional.fromSTEB(16, 0, 16, 0), + padding: const EdgeInsetsDirectional.fromSTEB(16, 0, 16, 0), child: ClipRRect( borderRadius: BorderRadius.circular(12), child: Container( @@ -176,7 +175,7 @@ class _ConversationDetailsOverlayWidgetState ), ), Padding( - padding: EdgeInsetsDirectional.fromSTEB(16, 16, 16, 44), + padding: const EdgeInsetsDirectional.fromSTEB(16, 16, 16, 44), child: FFButtonWidget( onPressed: () async { Navigator.pop(context); @@ -185,9 +184,9 @@ class _ConversationDetailsOverlayWidgetState options: FFButtonOptions( width: double.infinity, height: 52, - padding: EdgeInsetsDirectional.fromSTEB(44, 0, 44, 0), + padding: const EdgeInsetsDirectional.fromSTEB(44, 0, 44, 0), iconPadding: - EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), + const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), color: FlutterFlowTheme.of(context).secondaryBackground, textStyle: diff --git a/mobile/app/lib/components/ConversationOptionsModel.dart b/mobile/app/lib/components/conversation_options/conversation_options_model.dart similarity index 100% rename from mobile/app/lib/components/ConversationOptionsModel.dart rename to mobile/app/lib/components/conversation_options/conversation_options_model.dart diff --git a/mobile/app/lib/components/ConversationOptionsWidget.dart b/mobile/app/lib/components/conversation_options/conversation_options_widget.dart similarity index 88% rename from mobile/app/lib/components/ConversationOptionsWidget.dart rename to mobile/app/lib/components/conversation_options/conversation_options_widget.dart index 8390957..96306aa 100644 --- a/mobile/app/lib/components/ConversationOptionsWidget.dart +++ b/mobile/app/lib/components/conversation_options/conversation_options_widget.dart @@ -45,7 +45,7 @@ class _ConversationOptionsWidgetState extends State { color: FlutterFlowTheme.of(context).secondaryBackground, ), child: Padding( - padding: EdgeInsetsDirectional.fromSTEB(0, 12, 0, 24), + padding: const EdgeInsetsDirectional.fromSTEB(0, 12, 0, 24), child: Column( mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.start, @@ -53,9 +53,9 @@ class _ConversationOptionsWidgetState extends State { Container( width: double.infinity, height: 60, - decoration: BoxDecoration(), + decoration: const BoxDecoration(), child: Padding( - padding: EdgeInsetsDirectional.fromSTEB(12, 8, 12, 8), + padding: const EdgeInsetsDirectional.fromSTEB(12, 8, 12, 8), child: Row( mainAxisSize: MainAxisSize.max, children: [ @@ -67,7 +67,7 @@ class _ConversationOptionsWidgetState extends State { borderRadius: BorderRadius.circular(40), ), child: Padding( - padding: EdgeInsets.all(8), + padding: const EdgeInsets.all(8), child: Icon( Icons.share_rounded, color: FlutterFlowTheme.of(context).secondaryText, @@ -77,7 +77,7 @@ class _ConversationOptionsWidgetState extends State { ), Expanded( child: Padding( - padding: EdgeInsetsDirectional.fromSTEB(12, 0, 0, 0), + padding: const EdgeInsetsDirectional.fromSTEB(12, 0, 0, 0), child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -102,9 +102,9 @@ class _ConversationOptionsWidgetState extends State { Container( width: double.infinity, height: 60, - decoration: BoxDecoration(), + decoration: const BoxDecoration(), child: Padding( - padding: EdgeInsetsDirectional.fromSTEB(12, 8, 12, 8), + padding: const EdgeInsetsDirectional.fromSTEB(12, 8, 12, 8), child: Row( mainAxisSize: MainAxisSize.max, children: [ @@ -116,7 +116,7 @@ class _ConversationOptionsWidgetState extends State { borderRadius: BorderRadius.circular(40), ), child: Padding( - padding: EdgeInsets.all(8), + padding: const EdgeInsets.all(8), child: Icon( Icons.insert_link, color: FlutterFlowTheme.of(context).secondaryText, @@ -126,7 +126,7 @@ class _ConversationOptionsWidgetState extends State { ), Expanded( child: Padding( - padding: EdgeInsetsDirectional.fromSTEB(12, 0, 0, 0), + padding: const EdgeInsetsDirectional.fromSTEB(12, 0, 0, 0), child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -151,9 +151,9 @@ class _ConversationOptionsWidgetState extends State { Container( width: double.infinity, height: 60, - decoration: BoxDecoration(), + decoration: const BoxDecoration(), child: Padding( - padding: EdgeInsetsDirectional.fromSTEB(12, 8, 12, 8), + padding: const EdgeInsetsDirectional.fromSTEB(12, 8, 12, 8), child: Row( mainAxisSize: MainAxisSize.max, children: [ @@ -165,7 +165,7 @@ class _ConversationOptionsWidgetState extends State { borderRadius: BorderRadius.circular(40), ), child: Padding( - padding: EdgeInsets.all(8), + padding: const EdgeInsets.all(8), child: Icon( Icons.mode_edit, color: FlutterFlowTheme.of(context).secondaryText, @@ -175,7 +175,7 @@ class _ConversationOptionsWidgetState extends State { ), Expanded( child: Padding( - padding: EdgeInsetsDirectional.fromSTEB(12, 0, 0, 0), + padding: const EdgeInsetsDirectional.fromSTEB(12, 0, 0, 0), child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -200,9 +200,9 @@ class _ConversationOptionsWidgetState extends State { Container( width: double.infinity, height: 60, - decoration: BoxDecoration(), + decoration: const BoxDecoration(), child: Padding( - padding: EdgeInsetsDirectional.fromSTEB(12, 8, 12, 8), + padding: const EdgeInsetsDirectional.fromSTEB(12, 8, 12, 8), child: Row( mainAxisSize: MainAxisSize.max, children: [ @@ -214,7 +214,7 @@ class _ConversationOptionsWidgetState extends State { borderRadius: BorderRadius.circular(40), ), child: Padding( - padding: EdgeInsets.all(8), + padding: const EdgeInsets.all(8), child: Icon( Icons.hide_source, color: FlutterFlowTheme.of(context).secondaryText, @@ -224,7 +224,7 @@ class _ConversationOptionsWidgetState extends State { ), Expanded( child: Padding( - padding: EdgeInsetsDirectional.fromSTEB(12, 0, 0, 0), + padding: const EdgeInsetsDirectional.fromSTEB(12, 0, 0, 0), child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, diff --git a/mobile/app/lib/components/ConversationThreadModel.dart b/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart similarity index 100% rename from mobile/app/lib/components/ConversationThreadModel.dart rename to mobile/app/lib/components/conversation_thread/conversation_thread_model.dart diff --git a/mobile/app/lib/components/ConversationThreadWidget.dart b/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart similarity index 88% rename from mobile/app/lib/components/ConversationThreadWidget.dart rename to mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart index bc35cd6..51ceb3e 100644 --- a/mobile/app/lib/components/ConversationThreadWidget.dart +++ b/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart @@ -57,7 +57,7 @@ class _ConversationThreadWidgetState extends State { children: [ Expanded( child: ListView( - padding: EdgeInsets.fromLTRB( + padding: const EdgeInsets.fromLTRB( 0, 12, 0, @@ -67,14 +67,14 @@ class _ConversationThreadWidgetState extends State { scrollDirection: Axis.vertical, children: [ Container( - decoration: BoxDecoration(), + decoration: const BoxDecoration(), ), Container( height: MediaQuery.sizeOf(context).height * 1, constraints: BoxConstraints( minHeight: MediaQuery.sizeOf(context).height * 1, ), - decoration: BoxDecoration(), + decoration: const BoxDecoration(), child: wrapWithModel( model: _model.conversationBubblesModel, updateCallback: () => setState(() {}), @@ -88,7 +88,7 @@ class _ConversationThreadWidgetState extends State { width: double.infinity, decoration: BoxDecoration( color: FlutterFlowTheme.of(context).secondaryBackground, - boxShadow: [ + boxShadow: const [ BoxShadow( blurRadius: 3, color: Color(0x33000000), @@ -107,7 +107,7 @@ class _ConversationThreadWidgetState extends State { children: [ Expanded( child: Padding( - padding: EdgeInsetsDirectional.fromSTEB(0, 12, 0, 0), + padding: const EdgeInsetsDirectional.fromSTEB(0, 12, 0, 0), child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( @@ -119,9 +119,9 @@ class _ConversationThreadWidgetState extends State { imageBuilder: (path) => ClipRRect( borderRadius: BorderRadius.circular(8), child: CachedNetworkImage( - fadeInDuration: Duration(milliseconds: 500), + fadeInDuration: const Duration(milliseconds: 500), fadeOutDuration: - Duration(milliseconds: 500), + const Duration(milliseconds: 500), imageUrl: path, width: 120, height: 100, @@ -140,7 +140,7 @@ class _ConversationThreadWidgetState extends State { ), ), Align( - alignment: AlignmentDirectional(-1, -1), + alignment: const AlignmentDirectional(-1, -1), child: FlutterFlowIconButton( borderColor: FlutterFlowTheme.of(context).error, @@ -160,9 +160,9 @@ class _ConversationThreadWidgetState extends State { ), ), ] - .divide(SizedBox(width: 8)) - .addToStart(SizedBox(width: 16)) - .addToEnd(SizedBox(width: 16)), + .divide(const SizedBox(width: 8)) + .addToStart(const SizedBox(width: 16)) + .addToEnd(const SizedBox(width: 16)), ), ), ), @@ -173,7 +173,7 @@ class _ConversationThreadWidgetState extends State { key: _model.formKey, autovalidateMode: AutovalidateMode.disabled, child: Padding( - padding: EdgeInsetsDirectional.fromSTEB(0, 0, 0, 8), + padding: const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 8), child: wrapWithModel( model: _model.promptBoxModel, updateCallback: () => setState(() {}), diff --git a/mobile/app/lib/components/ModelItemModel.dart b/mobile/app/lib/components/model_item/model_item_model.dart similarity index 100% rename from mobile/app/lib/components/ModelItemModel.dart rename to mobile/app/lib/components/model_item/model_item_model.dart diff --git a/mobile/app/lib/components/ModelItemWidget.dart b/mobile/app/lib/components/model_item/model_item_widget.dart similarity index 92% rename from mobile/app/lib/components/ModelItemWidget.dart rename to mobile/app/lib/components/model_item/model_item_widget.dart index 2ba9817..90c244f 100644 --- a/mobile/app/lib/components/ModelItemWidget.dart +++ b/mobile/app/lib/components/model_item/model_item_widget.dart @@ -41,13 +41,19 @@ class _ModelItemWidgetState extends State { return MouseRegion( opaque: false, cursor: MouseCursor.defer ?? MouseCursor.defer, + onEnter: ((event) async { + setState(() => _model.iuserHovered = true); + }), + onExit: ((event) async { + setState(() => _model.iuserHovered = false); + }), child: Container( decoration: BoxDecoration( color: FlutterFlowTheme.of(context).primaryBackground, borderRadius: BorderRadius.circular(0), ), child: Padding( - padding: EdgeInsetsDirectional.fromSTEB(12, 8, 12, 8), + padding: const EdgeInsetsDirectional.fromSTEB(12, 8, 12, 8), child: Row( mainAxisSize: MainAxisSize.max, children: [ @@ -63,7 +69,7 @@ class _ModelItemWidgetState extends State { ), ), child: Align( - alignment: AlignmentDirectional(0, 0), + alignment: const AlignmentDirectional(0, 0), child: Text( 'A', textAlign: TextAlign.center, @@ -77,7 +83,7 @@ class _ModelItemWidgetState extends State { ), Expanded( child: Padding( - padding: EdgeInsetsDirectional.fromSTEB(12, 0, 8, 0), + padding: const EdgeInsetsDirectional.fromSTEB(12, 0, 8, 0), child: Column( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, @@ -92,7 +98,7 @@ class _ModelItemWidgetState extends State { ), ), Padding( - padding: EdgeInsetsDirectional.fromSTEB(0, 4, 0, 0), + padding: const EdgeInsetsDirectional.fromSTEB(0, 4, 0, 0), child: Text( 'name@domainname.com', style: @@ -111,12 +117,6 @@ class _ModelItemWidgetState extends State { ), ), ), - onEnter: ((event) async { - setState(() => _model.iuserHovered = true); - }), - onExit: ((event) async { - setState(() => _model.iuserHovered = false); - }), ); } } diff --git a/mobile/app/lib/components/NoChatsModel.dart b/mobile/app/lib/components/no_chats_model.dart similarity index 100% rename from mobile/app/lib/components/NoChatsModel.dart rename to mobile/app/lib/components/no_chats_model.dart diff --git a/mobile/app/lib/components/NoChatsWidget.dart b/mobile/app/lib/components/no_chats_widget.dart similarity index 86% rename from mobile/app/lib/components/NoChatsWidget.dart rename to mobile/app/lib/components/no_chats_widget.dart index b1274a3..3cf8561 100644 --- a/mobile/app/lib/components/NoChatsWidget.dart +++ b/mobile/app/lib/components/no_chats_widget.dart @@ -13,8 +13,8 @@ class NoChatsWidget extends StatefulWidget { this.icon, String? title, String? body, - }) : this.title = title ?? 'No Comments', - this.body = body ?? 'There are no comments associated with this post.'; + }) : title = title ?? 'No Comments', + body = body ?? 'There are no comments associated with this post.'; final Widget? icon; final String title; @@ -49,14 +49,14 @@ class _NoChatsWidgetState extends State { @override Widget build(BuildContext context) { return Align( - alignment: AlignmentDirectional(0, -1), + alignment: const AlignmentDirectional(0, -1), child: Column( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, children: [ widget.icon!, Padding( - padding: EdgeInsetsDirectional.fromSTEB(0, 16, 0, 0), + padding: const EdgeInsetsDirectional.fromSTEB(0, 16, 0, 0), child: Text( widget.title, textAlign: TextAlign.center, @@ -68,7 +68,7 @@ class _NoChatsWidgetState extends State { ), ), Padding( - padding: EdgeInsetsDirectional.fromSTEB(16, 8, 16, 0), + padding: const EdgeInsetsDirectional.fromSTEB(16, 8, 16, 0), child: Text( widget.body, textAlign: TextAlign.center, diff --git a/mobile/app/lib/components/OptionsDialogModel.dart b/mobile/app/lib/components/options_dialog/options_dialog_model.dart similarity index 100% rename from mobile/app/lib/components/OptionsDialogModel.dart rename to mobile/app/lib/components/options_dialog/options_dialog_model.dart diff --git a/mobile/app/lib/components/PopupWidget.dart b/mobile/app/lib/components/options_dialog/options_dialog_widget.dart similarity index 80% rename from mobile/app/lib/components/PopupWidget.dart rename to mobile/app/lib/components/options_dialog/options_dialog_widget.dart index a70dde1..c06d51b 100644 --- a/mobile/app/lib/components/PopupWidget.dart +++ b/mobile/app/lib/components/options_dialog/options_dialog_widget.dart @@ -6,8 +6,8 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; -import 'popup_model.dart'; -export 'popup_model.dart'; +import '/components/popup/popup_model.dart'; +export '/components/popup/popup_model.dart'; class PopupWidget extends StatefulWidget { const PopupWidget({super.key}); @@ -43,16 +43,16 @@ class _PopupWidgetState extends State { return Visibility( visible: true, child: Align( - alignment: AlignmentDirectional(0, 0), + alignment: const AlignmentDirectional(0, 0), child: Container( width: double.infinity, - constraints: BoxConstraints( + constraints: const BoxConstraints( maxWidth: 570, maxHeight: 375, ), decoration: BoxDecoration( color: Colors.white, - boxShadow: [ + boxShadow: const [ BoxShadow( blurRadius: 64, color: Color(0xB2000000), @@ -65,14 +65,14 @@ class _PopupWidgetState extends State { ], borderRadius: BorderRadius.circular(12), border: Border.all( - color: Color(0xFFE0E3E7), + color: const Color(0xFFE0E3E7), ), ), - alignment: AlignmentDirectional(0, 0), + alignment: const AlignmentDirectional(0, 0), child: Align( - alignment: AlignmentDirectional(0, 0), + alignment: const AlignmentDirectional(0, 0), child: Padding( - padding: EdgeInsets.all(24), + padding: const EdgeInsets.all(24), child: Column( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.end, @@ -83,14 +83,14 @@ class _PopupWidgetState extends State { children: [ Expanded( child: Padding( - padding: EdgeInsetsDirectional.fromSTEB(0, 0, 12, 0), + padding: const EdgeInsetsDirectional.fromSTEB(0, 0, 12, 0), child: Text( 'Refine the components modal.', style: FlutterFlowTheme.of(context) .headlineMedium .override( fontFamily: 'Outfit', - color: Color(0xFF14181B), + color: const Color(0xFF14181B), fontSize: 24, letterSpacing: 0, fontWeight: FontWeight.normal, @@ -99,11 +99,11 @@ class _PopupWidgetState extends State { ), ), FlutterFlowIconButton( - borderColor: Color(0xFFF1F4F8), + borderColor: const Color(0xFFF1F4F8), borderRadius: 30, borderWidth: 2, buttonSize: 44, - icon: Icon( + icon: const Icon( Icons.close_rounded, color: Color(0xFF57636C), size: 24, @@ -114,7 +114,7 @@ class _PopupWidgetState extends State { ), ], ), - Divider( + const Divider( height: 24, thickness: 2, color: Color(0xFFF1F4F8), @@ -123,14 +123,14 @@ class _PopupWidgetState extends State { 'FlutterFlow is a visual development platform that allows you to easily create beautiful and responsive user interfaces for your mobile and web applications. With its drag-and-drop interface and pre-built components, you can quickly prototype and build your app without writing any code. \nAdditionally, FlutterFlow\'s real-time preview feature allows you to see your changes in real-time and make adjustments on the fly.', style: FlutterFlowTheme.of(context).labelMedium.override( fontFamily: 'Readex Pro', - color: Color(0xFF57636C), + color: const Color(0xFF57636C), fontSize: 14, letterSpacing: 0, fontWeight: FontWeight.normal, ), ), Padding( - padding: EdgeInsetsDirectional.fromSTEB(0, 16, 0, 0), + padding: const EdgeInsetsDirectional.fromSTEB(0, 16, 0, 0), child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -143,26 +143,26 @@ class _PopupWidgetState extends State { options: FFButtonOptions( height: 40, padding: - EdgeInsetsDirectional.fromSTEB(24, 0, 24, 0), + const EdgeInsetsDirectional.fromSTEB(24, 0, 24, 0), iconPadding: - EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), + const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), color: Colors.white, textStyle: FlutterFlowTheme.of(context).bodySmall.override( fontFamily: 'Readex Pro', - color: Color(0xFF14181B), + color: const Color(0xFF14181B), fontSize: 12, letterSpacing: 0, fontWeight: FontWeight.normal, ), elevation: 0, - borderSide: BorderSide( + borderSide: const BorderSide( color: Color(0xFFF1F4F8), width: 2, ), borderRadius: BorderRadius.circular(8), - hoverColor: Color(0xFFF1F4F8), - hoverTextColor: Color(0xFF14181B), + hoverColor: const Color(0xFFF1F4F8), + hoverTextColor: const Color(0xFF14181B), ), ), FFButtonWidget( @@ -173,10 +173,10 @@ class _PopupWidgetState extends State { options: FFButtonOptions( width: 130, height: 40, - padding: EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), + padding: const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), iconPadding: - EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), - color: Color(0xFF4B39EF), + const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), + color: const Color(0xFF4B39EF), textStyle: FlutterFlowTheme.of(context) .titleSmall .override( @@ -187,12 +187,12 @@ class _PopupWidgetState extends State { fontWeight: FontWeight.w500, ), elevation: 3, - borderSide: BorderSide( + borderSide: const BorderSide( color: Colors.transparent, width: 1, ), borderRadius: BorderRadius.circular(8), - hoverColor: Color(0xFF2B16ED), + hoverColor: const Color(0xFF2B16ED), hoverTextColor: Colors.white, ), ), diff --git a/mobile/app/lib/components/PopupModel.dart b/mobile/app/lib/components/popup/popup_model.dart similarity index 100% rename from mobile/app/lib/components/PopupModel.dart rename to mobile/app/lib/components/popup/popup_model.dart diff --git a/mobile/app/lib/components/OptionsDialogWidget.dart b/mobile/app/lib/components/popup/popup_widget.dart similarity index 81% rename from mobile/app/lib/components/OptionsDialogWidget.dart rename to mobile/app/lib/components/popup/popup_widget.dart index a70dde1..d2c6420 100644 --- a/mobile/app/lib/components/OptionsDialogWidget.dart +++ b/mobile/app/lib/components/popup/popup_widget.dart @@ -43,16 +43,16 @@ class _PopupWidgetState extends State { return Visibility( visible: true, child: Align( - alignment: AlignmentDirectional(0, 0), + alignment: const AlignmentDirectional(0, 0), child: Container( width: double.infinity, - constraints: BoxConstraints( + constraints: const BoxConstraints( maxWidth: 570, maxHeight: 375, ), decoration: BoxDecoration( color: Colors.white, - boxShadow: [ + boxShadow: const [ BoxShadow( blurRadius: 64, color: Color(0xB2000000), @@ -65,14 +65,14 @@ class _PopupWidgetState extends State { ], borderRadius: BorderRadius.circular(12), border: Border.all( - color: Color(0xFFE0E3E7), + color: const Color(0xFFE0E3E7), ), ), - alignment: AlignmentDirectional(0, 0), + alignment: const AlignmentDirectional(0, 0), child: Align( - alignment: AlignmentDirectional(0, 0), + alignment: const AlignmentDirectional(0, 0), child: Padding( - padding: EdgeInsets.all(24), + padding: const EdgeInsets.all(24), child: Column( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.end, @@ -83,14 +83,14 @@ class _PopupWidgetState extends State { children: [ Expanded( child: Padding( - padding: EdgeInsetsDirectional.fromSTEB(0, 0, 12, 0), + padding: const EdgeInsetsDirectional.fromSTEB(0, 0, 12, 0), child: Text( 'Refine the components modal.', style: FlutterFlowTheme.of(context) .headlineMedium .override( fontFamily: 'Outfit', - color: Color(0xFF14181B), + color: const Color(0xFF14181B), fontSize: 24, letterSpacing: 0, fontWeight: FontWeight.normal, @@ -99,11 +99,11 @@ class _PopupWidgetState extends State { ), ), FlutterFlowIconButton( - borderColor: Color(0xFFF1F4F8), + borderColor: const Color(0xFFF1F4F8), borderRadius: 30, borderWidth: 2, buttonSize: 44, - icon: Icon( + icon: const Icon( Icons.close_rounded, color: Color(0xFF57636C), size: 24, @@ -114,7 +114,7 @@ class _PopupWidgetState extends State { ), ], ), - Divider( + const Divider( height: 24, thickness: 2, color: Color(0xFFF1F4F8), @@ -123,14 +123,14 @@ class _PopupWidgetState extends State { 'FlutterFlow is a visual development platform that allows you to easily create beautiful and responsive user interfaces for your mobile and web applications. With its drag-and-drop interface and pre-built components, you can quickly prototype and build your app without writing any code. \nAdditionally, FlutterFlow\'s real-time preview feature allows you to see your changes in real-time and make adjustments on the fly.', style: FlutterFlowTheme.of(context).labelMedium.override( fontFamily: 'Readex Pro', - color: Color(0xFF57636C), + color: const Color(0xFF57636C), fontSize: 14, letterSpacing: 0, fontWeight: FontWeight.normal, ), ), Padding( - padding: EdgeInsetsDirectional.fromSTEB(0, 16, 0, 0), + padding: const EdgeInsetsDirectional.fromSTEB(0, 16, 0, 0), child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -143,26 +143,26 @@ class _PopupWidgetState extends State { options: FFButtonOptions( height: 40, padding: - EdgeInsetsDirectional.fromSTEB(24, 0, 24, 0), + const EdgeInsetsDirectional.fromSTEB(24, 0, 24, 0), iconPadding: - EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), + const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), color: Colors.white, textStyle: FlutterFlowTheme.of(context).bodySmall.override( fontFamily: 'Readex Pro', - color: Color(0xFF14181B), + color: const Color(0xFF14181B), fontSize: 12, letterSpacing: 0, fontWeight: FontWeight.normal, ), elevation: 0, - borderSide: BorderSide( + borderSide: const BorderSide( color: Color(0xFFF1F4F8), width: 2, ), borderRadius: BorderRadius.circular(8), - hoverColor: Color(0xFFF1F4F8), - hoverTextColor: Color(0xFF14181B), + hoverColor: const Color(0xFFF1F4F8), + hoverTextColor: const Color(0xFF14181B), ), ), FFButtonWidget( @@ -173,10 +173,10 @@ class _PopupWidgetState extends State { options: FFButtonOptions( width: 130, height: 40, - padding: EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), + padding: const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), iconPadding: - EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), - color: Color(0xFF4B39EF), + const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), + color: const Color(0xFF4B39EF), textStyle: FlutterFlowTheme.of(context) .titleSmall .override( @@ -187,12 +187,12 @@ class _PopupWidgetState extends State { fontWeight: FontWeight.w500, ), elevation: 3, - borderSide: BorderSide( + borderSide: const BorderSide( color: Colors.transparent, width: 1, ), borderRadius: BorderRadius.circular(8), - hoverColor: Color(0xFF2B16ED), + hoverColor: const Color(0xFF2B16ED), hoverTextColor: Colors.white, ), ), diff --git a/mobile/app/lib/components/PromptBoxModel.dart b/mobile/app/lib/components/prompt_box/prompt_box_model.dart similarity index 100% rename from mobile/app/lib/components/PromptBoxModel.dart rename to mobile/app/lib/components/prompt_box/prompt_box_model.dart diff --git a/mobile/app/lib/components/PromptBoxWidget.dart b/mobile/app/lib/components/prompt_box/prompt_box_widget.dart similarity index 93% rename from mobile/app/lib/components/PromptBoxWidget.dart rename to mobile/app/lib/components/prompt_box/prompt_box_widget.dart index 7713f62..e53a61f 100644 --- a/mobile/app/lib/components/PromptBoxWidget.dart +++ b/mobile/app/lib/components/prompt_box/prompt_box_widget.dart @@ -50,7 +50,7 @@ class _PromptBoxWidgetState extends State { color: FlutterFlowTheme.of(context).secondaryBackground, borderRadius: BorderRadius.circular(10), ), - alignment: AlignmentDirectional(0, 0), + alignment: const AlignmentDirectional(0, 0), child: Column( mainAxisSize: MainAxisSize.max, children: [ @@ -61,7 +61,7 @@ class _PromptBoxWidgetState extends State { children: [ Expanded( child: Padding( - padding: EdgeInsets.all(14), + padding: const EdgeInsets.all(14), child: FlutterFlowIconButton( borderColor: Colors.transparent, borderRadius: 30, @@ -80,7 +80,7 @@ class _PromptBoxWidgetState extends State { ), Expanded( child: Padding( - padding: EdgeInsets.all(14), + padding: const EdgeInsets.all(14), child: FlutterFlowIconButton( borderColor: Colors.transparent, borderRadius: 30, @@ -99,7 +99,7 @@ class _PromptBoxWidgetState extends State { ), Expanded( child: Padding( - padding: EdgeInsets.all(14), + padding: const EdgeInsets.all(14), child: FlutterFlowIconButton( borderColor: Colors.transparent, borderRadius: 30, @@ -118,7 +118,7 @@ class _PromptBoxWidgetState extends State { ), Expanded( child: Padding( - padding: EdgeInsets.all(14), + padding: const EdgeInsets.all(14), child: FlutterFlowIconButton( borderColor: Colors.transparent, borderRadius: 30, @@ -137,7 +137,7 @@ class _PromptBoxWidgetState extends State { ), Expanded( child: Padding( - padding: EdgeInsets.all(14), + padding: const EdgeInsets.all(14), child: FlutterFlowIconButton( borderColor: Colors.transparent, borderRadius: 30, @@ -162,13 +162,13 @@ class _PromptBoxWidgetState extends State { Expanded( flex: 7, child: Padding( - padding: EdgeInsetsDirectional.fromSTEB(12, 0, 12, 0), + padding: const EdgeInsetsDirectional.fromSTEB(12, 0, 12, 0), child: TextFormField( controller: _model.textController, focusNode: _model.textFieldFocusNode, onChanged: (_) => EasyDebounce.debounce( '_model.textController', - Duration(milliseconds: 2000), + const Duration(milliseconds: 2000), () => setState(() {}), ), autofocus: true, @@ -192,21 +192,21 @@ class _PromptBoxWidgetState extends State { borderRadius: BorderRadius.circular(12), ), focusedBorder: OutlineInputBorder( - borderSide: BorderSide( + borderSide: const BorderSide( color: Color(0x00000000), width: 2, ), borderRadius: BorderRadius.circular(12), ), errorBorder: OutlineInputBorder( - borderSide: BorderSide( + borderSide: const BorderSide( color: Color(0x00000000), width: 2, ), borderRadius: BorderRadius.circular(12), ), focusedErrorBorder: OutlineInputBorder( - borderSide: BorderSide( + borderSide: const BorderSide( color: Color(0x00000000), width: 2, ), diff --git a/mobile/app/lib/pages/ConversationModel.dart b/mobile/app/lib/pages/conversation_model.dart similarity index 100% rename from mobile/app/lib/pages/ConversationModel.dart rename to mobile/app/lib/pages/conversation_model.dart diff --git a/mobile/app/lib/pages/ConversationWidget.dart b/mobile/app/lib/pages/conversation_widget.dart similarity index 96% rename from mobile/app/lib/pages/ConversationWidget.dart rename to mobile/app/lib/pages/conversation_widget.dart index 0954514..ec5f357 100644 --- a/mobile/app/lib/pages/ConversationWidget.dart +++ b/mobile/app/lib/pages/conversation_widget.dart @@ -66,7 +66,7 @@ class _ConversationWidgetState extends State { kTransitionInfoKey: TransitionInfo( hasTransition: true, transitionType: PageTransitionType.leftToRight, - duration: Duration(milliseconds: 250), + duration: const Duration(milliseconds: 250), ), }, ); @@ -79,7 +79,7 @@ class _ConversationWidgetState extends State { ), actions: [ Padding( - padding: EdgeInsetsDirectional.fromSTEB(0, 8, 16, 8), + padding: const EdgeInsetsDirectional.fromSTEB(0, 8, 16, 8), child: FlutterFlowIconButton( borderColor: FlutterFlowTheme.of(context).alternate, borderRadius: 12, diff --git a/mobile/app/lib/pages/ConversationsListModel.dart b/mobile/app/lib/pages/conversations_list_model.dart similarity index 100% rename from mobile/app/lib/pages/ConversationsListModel.dart rename to mobile/app/lib/pages/conversations_list_model.dart diff --git a/mobile/app/lib/pages/ConversationsListWidget.dart b/mobile/app/lib/pages/conversations_list_widget.dart similarity index 94% rename from mobile/app/lib/pages/ConversationsListWidget.dart rename to mobile/app/lib/pages/conversations_list_widget.dart index da3b2cb..6bac5ec 100644 --- a/mobile/app/lib/pages/ConversationsListWidget.dart +++ b/mobile/app/lib/pages/conversations_list_widget.dart @@ -93,7 +93,7 @@ class _ConversationsListWidgetState extends State { letterSpacing: 0, ), ), - actions: [], + actions: const [], centerTitle: false, elevation: 0, ), @@ -104,7 +104,7 @@ class _ConversationsListWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( - padding: EdgeInsetsDirectional.fromSTEB(16, 0, 0, 0), + padding: const EdgeInsetsDirectional.fromSTEB(16, 0, 0, 0), child: Text( 'AI conversations', style: FlutterFlowTheme.of(context).labelMedium.override( @@ -115,14 +115,14 @@ class _ConversationsListWidgetState extends State { ), Expanded( child: Align( - alignment: AlignmentDirectional(0, 0), + alignment: const AlignmentDirectional(0, 0), child: ListView( padding: EdgeInsets.zero, reverse: true, scrollDirection: Axis.vertical, children: [ Align( - alignment: AlignmentDirectional(0, 0), + alignment: const AlignmentDirectional(0, 0), child: InkWell( splashColor: Colors.transparent, focusColor: Colors.transparent, @@ -144,7 +144,7 @@ class _ConversationsListWidgetState extends State { .secondaryBackground, ), child: Align( - alignment: AlignmentDirectional(0, 0), + alignment: const AlignmentDirectional(0, 0), child: Icon( Icons.settings_outlined, color: FlutterFlowTheme.of(context) @@ -155,7 +155,7 @@ class _ConversationsListWidgetState extends State { ), Expanded( child: Align( - alignment: AlignmentDirectional(-1, 0), + alignment: const AlignmentDirectional(-1, 0), child: Container( width: 100, height: 100, @@ -177,7 +177,7 @@ class _ConversationsListWidgetState extends State { children: [ Align( alignment: - AlignmentDirectional(-1, 0), + const AlignmentDirectional(-1, 0), child: Row( mainAxisSize: MainAxisSize.max, children: [ @@ -196,7 +196,7 @@ class _ConversationsListWidgetState extends State { ), Align( alignment: - AlignmentDirectional(-1, 0), + const AlignmentDirectional(-1, 0), child: Row( mainAxisSize: MainAxisSize.max, children: [ diff --git a/mobile/app/lib/pages/ImageDetailsModel.dart b/mobile/app/lib/pages/image_details_model.dart similarity index 93% rename from mobile/app/lib/pages/ImageDetailsModel.dart rename to mobile/app/lib/pages/image_details_model.dart index 6398018..5d4863d 100644 --- a/mobile/app/lib/pages/ImageDetailsModel.dart +++ b/mobile/app/lib/pages/image_details_model.dart @@ -3,12 +3,10 @@ import '/flutter_flow/flutter_flow_icon_button.dart'; import '/flutter_flow/flutter_flow_theme.dart'; import '/flutter_flow/flutter_flow_util.dart'; import '/flutter_flow/flutter_flow_widgets.dart'; -import 'dart:math'; import 'image_details_widget.dart' show ImageDetailsWidget; import 'package:auto_size_text/auto_size_text.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/scheduler.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; diff --git a/mobile/app/lib/pages/ImageDetailsWidget.dart b/mobile/app/lib/pages/image_details_widget.dart similarity index 87% rename from mobile/app/lib/pages/ImageDetailsWidget.dart rename to mobile/app/lib/pages/image_details_widget.dart index e5d5c74..6b12719 100644 --- a/mobile/app/lib/pages/ImageDetailsWidget.dart +++ b/mobile/app/lib/pages/image_details_widget.dart @@ -3,11 +3,9 @@ import '/flutter_flow/flutter_flow_icon_button.dart'; import '/flutter_flow/flutter_flow_theme.dart'; import '/flutter_flow/flutter_flow_util.dart'; import '/flutter_flow/flutter_flow_widgets.dart'; -import 'dart:math'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/scheduler.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; @@ -51,15 +49,15 @@ class _ImageDetailsWidgetState extends State curve: Curves.easeInOut, delay: 0.0.ms, duration: 300.0.ms, - begin: Offset(0.0, 30.0), - end: Offset(0.0, 0.0), + begin: const Offset(0.0, 30.0), + end: const Offset(0.0, 0.0), ), ScaleEffect( curve: Curves.easeInOut, delay: 0.0.ms, duration: 300.0.ms, - begin: Offset(0.7, 0.7), - end: Offset(1.0, 1.0), + begin: const Offset(0.7, 0.7), + end: const Offset(1.0, 1.0), ), ], ), @@ -78,15 +76,15 @@ class _ImageDetailsWidgetState extends State curve: Curves.easeInOut, delay: 0.0.ms, duration: 300.0.ms, - begin: Offset(0.0, 30.0), - end: Offset(0.0, 0.0), + begin: const Offset(0.0, 30.0), + end: const Offset(0.0, 0.0), ), TiltEffect( curve: Curves.easeInOut, delay: 0.0.ms, duration: 300.0.ms, - begin: Offset(-0.698, 0), - end: Offset(0, 0), + begin: const Offset(-0.698, 0), + end: const Offset(0, 0), ), ], ), @@ -126,7 +124,7 @@ class _ImageDetailsWidgetState extends State context.pop(); }, ), - actions: [], + actions: const [], centerTitle: false, elevation: 0, ), @@ -137,15 +135,15 @@ class _ImageDetailsWidgetState extends State children: [ Expanded( child: Padding( - padding: EdgeInsets.all(12), + padding: const EdgeInsets.all(12), child: Hero( tag: 'hero', transitionOnUserGestures: true, child: ClipRRect( borderRadius: BorderRadius.circular(8), child: CachedNetworkImage( - fadeInDuration: Duration(milliseconds: 500), - fadeOutDuration: Duration(milliseconds: 500), + fadeInDuration: const Duration(milliseconds: 500), + fadeOutDuration: const Duration(milliseconds: 500), imageUrl: 'https://picsum.photos/seed/150/600', width: double.infinity, fit: BoxFit.fitWidth, @@ -158,13 +156,13 @@ class _ImageDetailsWidgetState extends State ), ), Padding( - padding: EdgeInsetsDirectional.fromSTEB(12, 12, 12, 32), + padding: const EdgeInsetsDirectional.fromSTEB(12, 12, 12, 32), child: Row( mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( - padding: EdgeInsetsDirectional.fromSTEB(0, 4, 8, 16), + padding: const EdgeInsetsDirectional.fromSTEB(0, 4, 8, 16), child: Container( width: 36, height: 36, @@ -178,12 +176,12 @@ class _ImageDetailsWidgetState extends State ), ), child: Padding( - padding: EdgeInsets.all(2), + padding: const EdgeInsets.all(2), child: ClipRRect( borderRadius: BorderRadius.circular(8), child: CachedNetworkImage( - fadeInDuration: Duration(milliseconds: 200), - fadeOutDuration: Duration(milliseconds: 200), + fadeInDuration: const Duration(milliseconds: 200), + fadeOutDuration: const Duration(milliseconds: 200), imageUrl: 'https://images.unsplash.com/photo-1521572267360-ee0c2909d518?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w0NTYyMDF8MHwxfHNlYXJjaHwyMHx8cHJvZmlsZXxlbnwwfHx8fDE2OTU4MjQ5OTN8MA&ixlib=rb-4.0.3&q=80&w=1080', width: 44, @@ -196,7 +194,7 @@ class _ImageDetailsWidgetState extends State ), Flexible( child: Padding( - padding: EdgeInsetsDirectional.fromSTEB(0, 0, 0, 8), + padding: const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 8), child: Column( mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.start, @@ -218,7 +216,7 @@ class _ImageDetailsWidgetState extends State ), )), Padding( - padding: EdgeInsetsDirectional.fromSTEB( + padding: const EdgeInsetsDirectional.fromSTEB( 0, 4, 0, 0), child: Text( 'Just Now', @@ -230,11 +228,11 @@ class _ImageDetailsWidgetState extends State ), ), ), - ].divide(SizedBox(width: 4)), + ].divide(const SizedBox(width: 4)), ), Padding( padding: - EdgeInsetsDirectional.fromSTEB(0, 4, 0, 0), + const EdgeInsetsDirectional.fromSTEB(0, 4, 0, 0), child: SelectionArea( child: AutoSizeText( 'Thanks so much! We really do appreciate it!', diff --git a/mobile/app/lib/pages/SettingsModel.dart b/mobile/app/lib/pages/settings_model.dart similarity index 91% rename from mobile/app/lib/pages/SettingsModel.dart rename to mobile/app/lib/pages/settings_model.dart index 19e74a3..c5f34a0 100644 --- a/mobile/app/lib/pages/SettingsModel.dart +++ b/mobile/app/lib/pages/settings_model.dart @@ -3,11 +3,9 @@ import '/flutter_flow/flutter_flow_animations.dart'; import '/flutter_flow/flutter_flow_theme.dart'; import '/flutter_flow/flutter_flow_util.dart'; import '/flutter_flow/flutter_flow_widgets.dart'; -import 'dart:math'; import 'settings_widget.dart' show SettingsWidget; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/scheduler.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; diff --git a/mobile/app/lib/pages/SettingsWidget.dart b/mobile/app/lib/pages/settings_widget.dart similarity index 93% rename from mobile/app/lib/pages/SettingsWidget.dart rename to mobile/app/lib/pages/settings_widget.dart index 7905835..d1f5e44 100644 --- a/mobile/app/lib/pages/SettingsWidget.dart +++ b/mobile/app/lib/pages/settings_widget.dart @@ -3,10 +3,8 @@ import '/flutter_flow/flutter_flow_animations.dart'; import '/flutter_flow/flutter_flow_theme.dart'; import '/flutter_flow/flutter_flow_util.dart'; import '/flutter_flow/flutter_flow_widgets.dart'; -import 'dart:math'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/scheduler.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; @@ -49,8 +47,8 @@ class _SettingsWidgetState extends State curve: Curves.easeInOut, delay: 0.0.ms, duration: 600.0.ms, - begin: Offset(0.0, 90.0), - end: Offset(0.0, 0.0), + begin: const Offset(0.0, 90.0), + end: const Offset(0.0, 0.0), ), ], ), @@ -68,8 +66,8 @@ class _SettingsWidgetState extends State curve: Curves.easeInOut, delay: 0.0.ms, duration: 600.0.ms, - begin: Offset(0.0, 90.0), - end: Offset(0.0, 0.0), + begin: const Offset(0.0, 90.0), + end: const Offset(0.0, 0.0), ), ], ), @@ -105,7 +103,7 @@ class _SettingsWidgetState extends State letterSpacing: 0, ), ), - actions: [], + actions: const [], centerTitle: false, elevation: 0, ) @@ -134,14 +132,14 @@ class _SettingsWidgetState extends State ), ), child: Padding( - padding: EdgeInsetsDirectional.fromSTEB(0, 24, 0, 16), + padding: const EdgeInsetsDirectional.fromSTEB(0, 24, 0, 16), child: Column( mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: - EdgeInsetsDirectional.fromSTEB(16, 0, 16, 12), + const EdgeInsetsDirectional.fromSTEB(16, 0, 16, 12), child: Row( mainAxisSize: MainAxisSize.max, children: [ @@ -151,7 +149,7 @@ class _SettingsWidgetState extends State size: 32, ), Padding( - padding: EdgeInsetsDirectional.fromSTEB( + padding: const EdgeInsetsDirectional.fromSTEB( 12, 0, 0, 0), child: Text( 'check.io', @@ -177,7 +175,7 @@ class _SettingsWidgetState extends State crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( - padding: EdgeInsetsDirectional.fromSTEB( + padding: const EdgeInsetsDirectional.fromSTEB( 16, 12, 0, 0), child: Text( 'Platform Navigation', @@ -190,10 +188,10 @@ class _SettingsWidgetState extends State ), ), Padding( - padding: EdgeInsetsDirectional.fromSTEB( + padding: const EdgeInsetsDirectional.fromSTEB( 16, 0, 16, 0), child: AnimatedContainer( - duration: Duration(milliseconds: 200), + duration: const Duration(milliseconds: 200), curve: Curves.easeInOut, width: double.infinity, height: 44, @@ -204,7 +202,7 @@ class _SettingsWidgetState extends State shape: BoxShape.rectangle, ), child: Padding( - padding: EdgeInsetsDirectional.fromSTEB( + padding: const EdgeInsetsDirectional.fromSTEB( 8, 0, 6, 0), child: Row( mainAxisSize: MainAxisSize.max, @@ -217,7 +215,7 @@ class _SettingsWidgetState extends State ), Padding( padding: - EdgeInsetsDirectional.fromSTEB( + const EdgeInsetsDirectional.fromSTEB( 12, 0, 0, 0), child: Text( 'Dashboard', @@ -237,10 +235,10 @@ class _SettingsWidgetState extends State ), ), Padding( - padding: EdgeInsetsDirectional.fromSTEB( + padding: const EdgeInsetsDirectional.fromSTEB( 16, 0, 16, 0), child: AnimatedContainer( - duration: Duration(milliseconds: 200), + duration: const Duration(milliseconds: 200), curve: Curves.easeInOut, width: double.infinity, height: 44, @@ -251,7 +249,7 @@ class _SettingsWidgetState extends State shape: BoxShape.rectangle, ), child: Padding( - padding: EdgeInsetsDirectional.fromSTEB( + padding: const EdgeInsetsDirectional.fromSTEB( 8, 0, 6, 0), child: Row( mainAxisSize: MainAxisSize.max, @@ -264,7 +262,7 @@ class _SettingsWidgetState extends State ), Padding( padding: - EdgeInsetsDirectional.fromSTEB( + const EdgeInsetsDirectional.fromSTEB( 12, 0, 0, 0), child: Text( 'Chats', @@ -284,10 +282,10 @@ class _SettingsWidgetState extends State ), ), Padding( - padding: EdgeInsetsDirectional.fromSTEB( + padding: const EdgeInsetsDirectional.fromSTEB( 16, 0, 16, 0), child: AnimatedContainer( - duration: Duration(milliseconds: 200), + duration: const Duration(milliseconds: 200), curve: Curves.easeInOut, width: double.infinity, height: 44, @@ -298,7 +296,7 @@ class _SettingsWidgetState extends State shape: BoxShape.rectangle, ), child: Padding( - padding: EdgeInsetsDirectional.fromSTEB( + padding: const EdgeInsetsDirectional.fromSTEB( 8, 0, 6, 0), child: Row( mainAxisSize: MainAxisSize.max, @@ -311,7 +309,7 @@ class _SettingsWidgetState extends State ), Padding( padding: - EdgeInsetsDirectional.fromSTEB( + const EdgeInsetsDirectional.fromSTEB( 12, 0, 0, 0), child: Text( 'Projects', @@ -331,10 +329,10 @@ class _SettingsWidgetState extends State ), ), Padding( - padding: EdgeInsetsDirectional.fromSTEB( + padding: const EdgeInsetsDirectional.fromSTEB( 16, 0, 16, 0), child: AnimatedContainer( - duration: Duration(milliseconds: 200), + duration: const Duration(milliseconds: 200), curve: Curves.easeInOut, width: double.infinity, height: 44, @@ -345,7 +343,7 @@ class _SettingsWidgetState extends State shape: BoxShape.rectangle, ), child: Padding( - padding: EdgeInsetsDirectional.fromSTEB( + padding: const EdgeInsetsDirectional.fromSTEB( 8, 0, 6, 0), child: Row( mainAxisSize: MainAxisSize.max, @@ -358,7 +356,7 @@ class _SettingsWidgetState extends State ), Expanded( child: Padding( - padding: EdgeInsetsDirectional + padding: const EdgeInsetsDirectional .fromSTEB(12, 0, 0, 0), child: Text( 'Recent Orders', @@ -384,9 +382,9 @@ class _SettingsWidgetState extends State ), child: Align( alignment: - AlignmentDirectional(0, 0), + const AlignmentDirectional(0, 0), child: Padding( - padding: EdgeInsetsDirectional + padding: const EdgeInsetsDirectional .fromSTEB(8, 4, 8, 4), child: Text( '12', @@ -412,7 +410,7 @@ class _SettingsWidgetState extends State ), ), Padding( - padding: EdgeInsetsDirectional.fromSTEB( + padding: const EdgeInsetsDirectional.fromSTEB( 16, 0, 0, 0), child: Text( 'Settings', @@ -425,10 +423,10 @@ class _SettingsWidgetState extends State ), ), Padding( - padding: EdgeInsetsDirectional.fromSTEB( + padding: const EdgeInsetsDirectional.fromSTEB( 16, 0, 16, 0), child: AnimatedContainer( - duration: Duration(milliseconds: 200), + duration: const Duration(milliseconds: 200), curve: Curves.easeInOut, width: double.infinity, height: 44, @@ -439,7 +437,7 @@ class _SettingsWidgetState extends State shape: BoxShape.rectangle, ), child: Padding( - padding: EdgeInsetsDirectional.fromSTEB( + padding: const EdgeInsetsDirectional.fromSTEB( 8, 0, 6, 0), child: Row( mainAxisSize: MainAxisSize.max, @@ -452,7 +450,7 @@ class _SettingsWidgetState extends State ), Padding( padding: - EdgeInsetsDirectional.fromSTEB( + const EdgeInsetsDirectional.fromSTEB( 12, 0, 0, 0), child: Text( 'Billing', @@ -472,10 +470,10 @@ class _SettingsWidgetState extends State ), ), Padding( - padding: EdgeInsetsDirectional.fromSTEB( + padding: const EdgeInsetsDirectional.fromSTEB( 16, 0, 16, 0), child: AnimatedContainer( - duration: Duration(milliseconds: 200), + duration: const Duration(milliseconds: 200), curve: Curves.easeInOut, width: double.infinity, height: 44, @@ -486,7 +484,7 @@ class _SettingsWidgetState extends State shape: BoxShape.rectangle, ), child: Padding( - padding: EdgeInsetsDirectional.fromSTEB( + padding: const EdgeInsetsDirectional.fromSTEB( 8, 0, 6, 0), child: Row( mainAxisSize: MainAxisSize.max, @@ -499,7 +497,7 @@ class _SettingsWidgetState extends State ), Padding( padding: - EdgeInsetsDirectional.fromSTEB( + const EdgeInsetsDirectional.fromSTEB( 12, 0, 0, 0), child: Text( 'Explore', @@ -518,14 +516,14 @@ class _SettingsWidgetState extends State ), ), ), - ].divide(SizedBox(height: 12)), + ].divide(const SizedBox(height: 12)), ), ), Align( - alignment: AlignmentDirectional(0, -1), + alignment: const AlignmentDirectional(0, -1), child: Padding( padding: - EdgeInsetsDirectional.fromSTEB(0, 8, 0, 16), + const EdgeInsetsDirectional.fromSTEB(0, 8, 0, 16), child: Container( width: 250, height: 50, @@ -540,7 +538,7 @@ class _SettingsWidgetState extends State ), ), child: Padding( - padding: EdgeInsets.all(4), + padding: const EdgeInsets.all(4), child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.start, @@ -604,7 +602,7 @@ class _SettingsWidgetState extends State size: 16, ), Padding( - padding: EdgeInsetsDirectional + padding: const EdgeInsetsDirectional .fromSTEB(4, 0, 0, 0), child: Text( 'Light Mode', @@ -693,7 +691,7 @@ class _SettingsWidgetState extends State size: 16, ), Padding( - padding: EdgeInsetsDirectional + padding: const EdgeInsetsDirectional .fromSTEB(4, 0, 0, 0), child: Text( 'Dark Mode', @@ -736,7 +734,7 @@ class _SettingsWidgetState extends State ), Padding( padding: - EdgeInsetsDirectional.fromSTEB(16, 12, 16, 12), + const EdgeInsetsDirectional.fromSTEB(16, 12, 16, 12), child: Row( mainAxisSize: MainAxisSize.max, children: [ @@ -753,14 +751,14 @@ class _SettingsWidgetState extends State ), ), child: Padding( - padding: EdgeInsets.all(2), + padding: const EdgeInsets.all(2), child: ClipRRect( borderRadius: BorderRadius.circular(8), child: CachedNetworkImage( fadeInDuration: - Duration(milliseconds: 500), + const Duration(milliseconds: 500), fadeOutDuration: - Duration(milliseconds: 500), + const Duration(milliseconds: 500), imageUrl: 'https://images.unsplash.com/photo-1624561172888-ac93c696e10c?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8NjJ8fHVzZXJzfGVufDB8fDB8fA%3D%3D&auto=format&fit=crop&w=900&q=60', width: 44, @@ -772,7 +770,7 @@ class _SettingsWidgetState extends State ), Expanded( child: Padding( - padding: EdgeInsetsDirectional.fromSTEB( + padding: const EdgeInsetsDirectional.fromSTEB( 12, 0, 0, 0), child: Column( mainAxisSize: MainAxisSize.max, @@ -806,7 +804,7 @@ class _SettingsWidgetState extends State ), Padding( padding: - EdgeInsetsDirectional.fromSTEB(16, 0, 16, 0), + const EdgeInsetsDirectional.fromSTEB(16, 0, 16, 0), child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.end, @@ -826,7 +824,7 @@ class _SettingsWidgetState extends State ), Expanded( child: Align( - alignment: AlignmentDirectional(0, 0), + alignment: const AlignmentDirectional(0, 0), child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.max, @@ -839,13 +837,13 @@ class _SettingsWidgetState extends State Container( width: double.infinity, height: 24, - decoration: BoxDecoration(), + decoration: const BoxDecoration(), ), Align( - alignment: AlignmentDirectional(0, -1), + alignment: const AlignmentDirectional(0, -1), child: Padding( padding: - EdgeInsetsDirectional.fromSTEB(16, 16, 16, 0), + const EdgeInsetsDirectional.fromSTEB(16, 16, 16, 0), child: Wrap( spacing: 16, runSpacing: 16, @@ -858,7 +856,7 @@ class _SettingsWidgetState extends State children: [ Container( width: double.infinity, - constraints: BoxConstraints( + constraints: const BoxConstraints( maxWidth: 390, ), decoration: BoxDecoration( @@ -875,7 +873,7 @@ class _SettingsWidgetState extends State mainAxisSize: MainAxisSize.max, children: [ Padding( - padding: EdgeInsets.all(16), + padding: const EdgeInsets.all(16), child: Row( mainAxisSize: MainAxisSize.max, children: [ @@ -896,15 +894,15 @@ class _SettingsWidgetState extends State ), ), child: Padding( - padding: EdgeInsets.all(2), + padding: const EdgeInsets.all(2), child: ClipRRect( borderRadius: BorderRadius.circular( 8), child: CachedNetworkImage( - fadeInDuration: Duration( + fadeInDuration: const Duration( milliseconds: 500), - fadeOutDuration: Duration( + fadeOutDuration: const Duration( milliseconds: 500), imageUrl: 'https://images.unsplash.com/photo-1624561172888-ac93c696e10c?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8NjJ8fHVzZXJzfGVufDB8fDB8fA%3D%3D&auto=format&fit=crop&w=900&q=60', @@ -917,7 +915,7 @@ class _SettingsWidgetState extends State ), Expanded( child: Padding( - padding: EdgeInsetsDirectional + padding: const EdgeInsetsDirectional .fromSTEB(16, 4, 0, 4), child: Column( mainAxisSize: @@ -939,7 +937,7 @@ class _SettingsWidgetState extends State ), Padding( padding: - EdgeInsetsDirectional + const EdgeInsetsDirectional .fromSTEB( 0, 4, 0, 0), child: Text( @@ -973,7 +971,7 @@ class _SettingsWidgetState extends State children: [ Expanded( child: Padding( - padding: EdgeInsetsDirectional + padding: const EdgeInsetsDirectional .fromSTEB(0, 0, 0, 12), child: Container( width: 200, @@ -987,7 +985,7 @@ class _SettingsWidgetState extends State ), child: Padding( padding: - EdgeInsetsDirectional + const EdgeInsetsDirectional .fromSTEB( 12, 12, 12, 0), child: Column( @@ -1006,7 +1004,7 @@ class _SettingsWidgetState extends State ), Padding( padding: - EdgeInsetsDirectional + const EdgeInsetsDirectional .fromSTEB(0, 8, 0, 4), child: Text( @@ -1047,7 +1045,7 @@ class _SettingsWidgetState extends State ), Expanded( child: Padding( - padding: EdgeInsetsDirectional + padding: const EdgeInsetsDirectional .fromSTEB(0, 0, 0, 12), child: Container( width: 200, @@ -1061,7 +1059,7 @@ class _SettingsWidgetState extends State ), child: Padding( padding: - EdgeInsetsDirectional + const EdgeInsetsDirectional .fromSTEB( 12, 12, 12, 0), child: Column( @@ -1081,7 +1079,7 @@ class _SettingsWidgetState extends State ), Padding( padding: - EdgeInsetsDirectional + const EdgeInsetsDirectional .fromSTEB(0, 8, 0, 4), child: Text( @@ -1127,10 +1125,10 @@ class _SettingsWidgetState extends State ), Container( width: double.infinity, - constraints: BoxConstraints( + constraints: const BoxConstraints( maxWidth: 470, ), - decoration: BoxDecoration(), + decoration: const BoxDecoration(), child: Column( mainAxisSize: MainAxisSize.max, crossAxisAlignment: @@ -1151,7 +1149,7 @@ class _SettingsWidgetState extends State ), child: Padding( padding: - EdgeInsetsDirectional.fromSTEB( + const EdgeInsetsDirectional.fromSTEB( 16, 12, 0, 12), child: Text( 'My Account Information', @@ -1168,7 +1166,7 @@ class _SettingsWidgetState extends State ), Padding( padding: - EdgeInsetsDirectional.fromSTEB( + const EdgeInsetsDirectional.fromSTEB( 0, 0, 0, 1), child: Container( width: double.infinity, @@ -1183,7 +1181,7 @@ class _SettingsWidgetState extends State color: FlutterFlowTheme.of( context) .alternate, - offset: Offset( + offset: const Offset( 0, 1, ), @@ -1192,7 +1190,7 @@ class _SettingsWidgetState extends State shape: BoxShape.rectangle, ), child: Padding( - padding: EdgeInsetsDirectional + padding: const EdgeInsetsDirectional .fromSTEB(16, 0, 16, 0), child: Column( mainAxisSize: MainAxisSize.max, @@ -1217,7 +1215,7 @@ class _SettingsWidgetState extends State Expanded( child: Align( alignment: - AlignmentDirectional( + const AlignmentDirectional( 1, 0), child: Icon( Icons @@ -1238,7 +1236,7 @@ class _SettingsWidgetState extends State ), Padding( padding: - EdgeInsetsDirectional.fromSTEB( + const EdgeInsetsDirectional.fromSTEB( 0, 0, 0, 1), child: Container( width: double.infinity, @@ -1253,7 +1251,7 @@ class _SettingsWidgetState extends State color: FlutterFlowTheme.of( context) .alternate, - offset: Offset( + offset: const Offset( 0, 1, ), @@ -1262,7 +1260,7 @@ class _SettingsWidgetState extends State shape: BoxShape.rectangle, ), child: Padding( - padding: EdgeInsetsDirectional + padding: const EdgeInsetsDirectional .fromSTEB(16, 0, 16, 0), child: Column( mainAxisSize: MainAxisSize.max, @@ -1292,7 +1290,7 @@ class _SettingsWidgetState extends State Expanded( child: Align( alignment: - AlignmentDirectional( + const AlignmentDirectional( 1, 0), child: Icon( Icons @@ -1313,7 +1311,7 @@ class _SettingsWidgetState extends State ), Padding( padding: - EdgeInsetsDirectional.fromSTEB( + const EdgeInsetsDirectional.fromSTEB( 0, 12, 0, 0), child: Container( width: double.infinity, @@ -1330,7 +1328,7 @@ class _SettingsWidgetState extends State ), ), child: Padding( - padding: EdgeInsetsDirectional + padding: const EdgeInsetsDirectional .fromSTEB(16, 12, 0, 12), child: Text( 'Support', @@ -1348,7 +1346,7 @@ class _SettingsWidgetState extends State ), Padding( padding: - EdgeInsetsDirectional.fromSTEB( + const EdgeInsetsDirectional.fromSTEB( 0, 0, 0, 1), child: InkWell( splashColor: Colors.transparent, @@ -1365,7 +1363,7 @@ class _SettingsWidgetState extends State transitionType: PageTransitionType .rightToLeft, - duration: Duration( + duration: const Duration( milliseconds: 250), ), }, @@ -1384,7 +1382,7 @@ class _SettingsWidgetState extends State color: FlutterFlowTheme.of( context) .alternate, - offset: Offset( + offset: const Offset( 0, 1, ), @@ -1393,7 +1391,7 @@ class _SettingsWidgetState extends State shape: BoxShape.rectangle, ), child: Padding( - padding: EdgeInsetsDirectional + padding: const EdgeInsetsDirectional .fromSTEB(16, 0, 16, 0), child: Column( mainAxisSize: @@ -1423,7 +1421,7 @@ class _SettingsWidgetState extends State Expanded( child: Align( alignment: - AlignmentDirectional( + const AlignmentDirectional( 1, 0), child: Icon( Icons @@ -1445,7 +1443,7 @@ class _SettingsWidgetState extends State ), Padding( padding: - EdgeInsetsDirectional.fromSTEB( + const EdgeInsetsDirectional.fromSTEB( 0, 0, 0, 1), child: InkWell( splashColor: Colors.transparent, @@ -1484,7 +1482,7 @@ class _SettingsWidgetState extends State color: FlutterFlowTheme.of( context) .alternate, - offset: Offset( + offset: const Offset( 0, 1, ), @@ -1493,7 +1491,7 @@ class _SettingsWidgetState extends State shape: BoxShape.rectangle, ), child: Padding( - padding: EdgeInsetsDirectional + padding: const EdgeInsetsDirectional .fromSTEB(16, 0, 16, 0), child: Column( mainAxisSize: @@ -1520,7 +1518,7 @@ class _SettingsWidgetState extends State Expanded( child: Align( alignment: - AlignmentDirectional( + const AlignmentDirectional( 1, 0), child: Icon( Icons @@ -1542,13 +1540,13 @@ class _SettingsWidgetState extends State ), Align( alignment: - AlignmentDirectional(0, -1), + const AlignmentDirectional(0, -1), child: Padding( - padding: EdgeInsets.all(16), + padding: const EdgeInsets.all(16), child: Container( width: double.infinity, height: 50, - constraints: BoxConstraints( + constraints: const BoxConstraints( maxWidth: 500, ), decoration: BoxDecoration( @@ -1565,7 +1563,7 @@ class _SettingsWidgetState extends State ), ), child: Padding( - padding: EdgeInsets.all(4), + padding: const EdgeInsets.all(4), child: Row( mainAxisSize: MainAxisSize.max, @@ -1652,7 +1650,7 @@ class _SettingsWidgetState extends State ), Padding( padding: - EdgeInsetsDirectional + const EdgeInsetsDirectional .fromSTEB( 4, 0, @@ -1753,7 +1751,7 @@ class _SettingsWidgetState extends State ), Padding( padding: - EdgeInsetsDirectional + const EdgeInsetsDirectional .fromSTEB( 4, 0, @@ -1794,7 +1792,7 @@ class _SettingsWidgetState extends State ), ), ), - ].addToEnd(SizedBox(height: 72)), + ].addToEnd(const SizedBox(height: 72)), ), ), ), diff --git a/mobile/app/lib/pages/SetupModel.dart b/mobile/app/lib/pages/setup_model.dart similarity index 91% rename from mobile/app/lib/pages/SetupModel.dart rename to mobile/app/lib/pages/setup_model.dart index 959f48a..5ce1f8c 100644 --- a/mobile/app/lib/pages/SetupModel.dart +++ b/mobile/app/lib/pages/setup_model.dart @@ -2,10 +2,8 @@ import '/flutter_flow/flutter_flow_animations.dart'; import '/flutter_flow/flutter_flow_theme.dart'; import '/flutter_flow/flutter_flow_util.dart'; import '/flutter_flow/flutter_flow_widgets.dart'; -import 'dart:math'; import 'setup_widget.dart' show SetupWidget; import 'package:flutter/material.dart'; -import 'package:flutter/scheduler.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; diff --git a/mobile/app/lib/pages/SetupWidget.dart b/mobile/app/lib/pages/setup_widget.dart similarity index 84% rename from mobile/app/lib/pages/SetupWidget.dart rename to mobile/app/lib/pages/setup_widget.dart index 13d9f34..ae4feed 100644 --- a/mobile/app/lib/pages/SetupWidget.dart +++ b/mobile/app/lib/pages/setup_widget.dart @@ -2,9 +2,7 @@ import '/flutter_flow/flutter_flow_animations.dart'; import '/flutter_flow/flutter_flow_theme.dart'; import '/flutter_flow/flutter_flow_util.dart'; import '/flutter_flow/flutter_flow_widgets.dart'; -import 'dart:math'; import 'package:flutter/material.dart'; -import 'package:flutter/scheduler.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; @@ -48,8 +46,8 @@ class _SetupWidgetState extends State curve: Curves.easeInOut, delay: 0.0.ms, duration: 400.0.ms, - begin: Offset(3.0, 3.0), - end: Offset(1.0, 1.0), + begin: const Offset(3.0, 3.0), + end: const Offset(1.0, 1.0), ), ], ), @@ -68,8 +66,8 @@ class _SetupWidgetState extends State curve: Curves.bounceOut, delay: 300.0.ms, duration: 300.0.ms, - begin: Offset(0.6, 0.6), - end: Offset(1.0, 1.0), + begin: const Offset(0.6, 0.6), + end: const Offset(1.0, 1.0), ), ], ), @@ -88,8 +86,8 @@ class _SetupWidgetState extends State curve: Curves.easeInOut, delay: 350.0.ms, duration: 400.0.ms, - begin: Offset(0.0, 30.0), - end: Offset(0.0, 0.0), + begin: const Offset(0.0, 30.0), + end: const Offset(0.0, 0.0), ), ], ), @@ -108,8 +106,8 @@ class _SetupWidgetState extends State curve: Curves.easeInOut, delay: 400.0.ms, duration: 400.0.ms, - begin: Offset(0.0, 30.0), - end: Offset(0.0, 0.0), + begin: const Offset(0.0, 30.0), + end: const Offset(0.0, 0.0), ), ], ), @@ -128,8 +126,8 @@ class _SetupWidgetState extends State curve: Curves.bounceOut, delay: 300.0.ms, duration: 600.0.ms, - begin: Offset(0.6, 0.6), - end: Offset(1.0, 1.0), + begin: const Offset(0.6, 0.6), + end: const Offset(1.0, 1.0), ), ], ), @@ -167,9 +165,9 @@ class _SetupWidgetState extends State FlutterFlowTheme.of(context).error, FlutterFlowTheme.of(context).tertiary ], - stops: [0, 0.5, 1], - begin: AlignmentDirectional(-1, -1), - end: AlignmentDirectional(1, 1), + stops: const [0, 0.5, 1], + begin: const AlignmentDirectional(-1, -1), + end: const AlignmentDirectional(1, 1), ), ), child: Container( @@ -178,12 +176,12 @@ class _SetupWidgetState extends State decoration: BoxDecoration( gradient: LinearGradient( colors: [ - Color(0x00FFFFFF), + const Color(0x00FFFFFF), FlutterFlowTheme.of(context).secondaryBackground ], - stops: [0, 1], - begin: AlignmentDirectional(0, -1), - end: AlignmentDirectional(0, 1), + stops: const [0, 1], + begin: const AlignmentDirectional(0, -1), + end: const AlignmentDirectional(0, 1), ), ), child: Column( @@ -198,7 +196,7 @@ class _SetupWidgetState extends State shape: BoxShape.circle, ), child: Padding( - padding: EdgeInsets.all(8), + padding: const EdgeInsets.all(8), child: Image.network( 'https://storage.googleapis.com/flutterflow-io-6f20.appspot.com/projects/f-f-templates-q1-23-fbcr63/assets/ax4fvwjz7awx/@4xff_badgeDesign_dark_small.png', width: 100, @@ -209,7 +207,7 @@ class _SetupWidgetState extends State ).animateOnPageLoad( animationsMap['containerOnPageLoadAnimation2']!), Padding( - padding: EdgeInsetsDirectional.fromSTEB(0, 44, 0, 0), + padding: const EdgeInsetsDirectional.fromSTEB(0, 44, 0, 0), child: Text( 'Welcome!', style: FlutterFlowTheme.of(context) @@ -222,7 +220,7 @@ class _SetupWidgetState extends State animationsMap['textOnPageLoadAnimation1']!), ), Padding( - padding: EdgeInsetsDirectional.fromSTEB(44, 8, 44, 0), + padding: const EdgeInsetsDirectional.fromSTEB(44, 8, 44, 0), child: Text( 'Thanks for joining! Access or create your account below, and get started on your journey!', textAlign: TextAlign.center, @@ -241,15 +239,15 @@ class _SetupWidgetState extends State animationsMap['containerOnPageLoadAnimation1']!), ), Padding( - padding: EdgeInsetsDirectional.fromSTEB(16, 24, 16, 44), + padding: const EdgeInsetsDirectional.fromSTEB(16, 24, 16, 44), child: Row( mainAxisSize: MainAxisSize.max, children: [ Expanded( child: Align( - alignment: AlignmentDirectional(0, 0), + alignment: const AlignmentDirectional(0, 0), child: Padding( - padding: EdgeInsetsDirectional.fromSTEB(0, 0, 8, 16), + padding: const EdgeInsetsDirectional.fromSTEB(0, 0, 8, 16), child: FFButtonWidget( onPressed: () { print('Button pressed ...'); @@ -258,9 +256,9 @@ class _SetupWidgetState extends State options: FFButtonOptions( width: 230, height: 52, - padding: EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), + padding: const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), iconPadding: - EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), + const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), color: FlutterFlowTheme.of(context) .secondaryBackground, textStyle: @@ -281,9 +279,9 @@ class _SetupWidgetState extends State ), Expanded( child: Align( - alignment: AlignmentDirectional(0, 0), + alignment: const AlignmentDirectional(0, 0), child: Padding( - padding: EdgeInsetsDirectional.fromSTEB(8, 0, 0, 16), + padding: const EdgeInsetsDirectional.fromSTEB(8, 0, 0, 16), child: FFButtonWidget( onPressed: () { print('Button pressed ...'); @@ -292,9 +290,9 @@ class _SetupWidgetState extends State options: FFButtonOptions( width: 230, height: 52, - padding: EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), + padding: const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), iconPadding: - EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), + const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), color: FlutterFlowTheme.of(context).primary, textStyle: FlutterFlowTheme.of(context) .titleSmall @@ -304,7 +302,7 @@ class _SetupWidgetState extends State letterSpacing: 0, ), elevation: 3, - borderSide: BorderSide( + borderSide: const BorderSide( color: Colors.transparent, width: 1, ), diff --git a/mobile/app/lib/pages/SupportFormModel.dart b/mobile/app/lib/pages/support_form_model.dart similarity index 96% rename from mobile/app/lib/pages/SupportFormModel.dart rename to mobile/app/lib/pages/support_form_model.dart index 246d59e..547e54b 100644 --- a/mobile/app/lib/pages/SupportFormModel.dart +++ b/mobile/app/lib/pages/support_form_model.dart @@ -3,10 +3,8 @@ import '/flutter_flow/flutter_flow_icon_button.dart'; import '/flutter_flow/flutter_flow_theme.dart'; import '/flutter_flow/flutter_flow_util.dart'; import '/flutter_flow/flutter_flow_widgets.dart'; -import 'dart:math'; import 'support_form_widget.dart' show SupportFormWidget; import 'package:flutter/material.dart'; -import 'package:flutter/scheduler.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; diff --git a/mobile/app/lib/pages/SupportFormWidget.dart b/mobile/app/lib/pages/support_form_widget.dart similarity index 91% rename from mobile/app/lib/pages/SupportFormWidget.dart rename to mobile/app/lib/pages/support_form_widget.dart index cb6740c..960d4fe 100644 --- a/mobile/app/lib/pages/SupportFormWidget.dart +++ b/mobile/app/lib/pages/support_form_widget.dart @@ -3,9 +3,7 @@ import '/flutter_flow/flutter_flow_icon_button.dart'; import '/flutter_flow/flutter_flow_theme.dart'; import '/flutter_flow/flutter_flow_util.dart'; import '/flutter_flow/flutter_flow_widgets.dart'; -import 'dart:math'; import 'package:flutter/material.dart'; -import 'package:flutter/scheduler.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; @@ -57,8 +55,8 @@ class _SupportFormWidgetState extends State curve: Curves.easeInOut, delay: 0.0.ms, duration: 600.0.ms, - begin: Offset(0.0, 110.0), - end: Offset(0.0, 0.0), + begin: const Offset(0.0, 110.0), + end: const Offset(0.0, 0.0), ), ], ), @@ -76,8 +74,8 @@ class _SupportFormWidgetState extends State curve: Curves.easeInOut, delay: 0.0.ms, duration: 600.0.ms, - begin: Offset(0.0, 110.0), - end: Offset(0.0, 0.0), + begin: const Offset(0.0, 110.0), + end: const Offset(0.0, 0.0), ), ], ), @@ -95,8 +93,8 @@ class _SupportFormWidgetState extends State curve: Curves.easeInOut, delay: 0.0.ms, duration: 600.0.ms, - begin: Offset(0.0, 110.0), - end: Offset(0.0, 0.0), + begin: const Offset(0.0, 110.0), + end: const Offset(0.0, 0.0), ), ], ), @@ -149,16 +147,16 @@ class _SupportFormWidgetState extends State letterSpacing: 0, ), ), - actions: [], + actions: const [], centerTitle: false, elevation: 0, ), body: SafeArea( top: true, child: Align( - alignment: AlignmentDirectional(0, 1), + alignment: const AlignmentDirectional(0, 1), child: Padding( - padding: EdgeInsetsDirectional.fromSTEB(16, 12, 16, 0), + padding: const EdgeInsetsDirectional.fromSTEB(16, 12, 16, 0), child: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.max, @@ -172,7 +170,7 @@ class _SupportFormWidgetState extends State ), ), Padding( - padding: EdgeInsetsDirectional.fromSTEB(0, 4, 0, 0), + padding: const EdgeInsetsDirectional.fromSTEB(0, 4, 0, 0), child: Text( 'Submit a bug', style: FlutterFlowTheme.of(context) @@ -189,10 +187,10 @@ class _SupportFormWidgetState extends State Expanded( child: Padding( padding: - EdgeInsetsDirectional.fromSTEB(0, 16, 0, 0), + const EdgeInsetsDirectional.fromSTEB(0, 16, 0, 0), child: Container( width: 120, - constraints: BoxConstraints( + constraints: const BoxConstraints( maxWidth: 500, ), decoration: BoxDecoration( @@ -205,7 +203,7 @@ class _SupportFormWidgetState extends State ), ), child: Padding( - padding: EdgeInsetsDirectional.fromSTEB( + padding: const EdgeInsetsDirectional.fromSTEB( 8, 16, 8, 16), child: Column( mainAxisSize: MainAxisSize.max, @@ -219,7 +217,7 @@ class _SupportFormWidgetState extends State size: 36, ), Padding( - padding: EdgeInsetsDirectional.fromSTEB( + padding: const EdgeInsetsDirectional.fromSTEB( 0, 12, 0, 0), child: Text( 'Email Us', @@ -242,10 +240,10 @@ class _SupportFormWidgetState extends State Expanded( child: Padding( padding: - EdgeInsetsDirectional.fromSTEB(0, 16, 0, 0), + const EdgeInsetsDirectional.fromSTEB(0, 16, 0, 0), child: Container( width: 120, - constraints: BoxConstraints( + constraints: const BoxConstraints( maxWidth: 500, ), decoration: BoxDecoration( @@ -258,7 +256,7 @@ class _SupportFormWidgetState extends State ), ), child: Padding( - padding: EdgeInsetsDirectional.fromSTEB( + padding: const EdgeInsetsDirectional.fromSTEB( 8, 16, 8, 16), child: Column( mainAxisSize: MainAxisSize.max, @@ -272,7 +270,7 @@ class _SupportFormWidgetState extends State size: 36, ), Padding( - padding: EdgeInsetsDirectional.fromSTEB( + padding: const EdgeInsetsDirectional.fromSTEB( 0, 12, 0, 0), child: Text( 'Search FAQs', @@ -292,10 +290,10 @@ class _SupportFormWidgetState extends State 'containerOnPageLoadAnimation2']!), ), ), - ].divide(SizedBox(width: 12)), + ].divide(const SizedBox(width: 12)), ), Padding( - padding: EdgeInsetsDirectional.fromSTEB(0, 16, 0, 0), + padding: const EdgeInsetsDirectional.fromSTEB(0, 16, 0, 0), child: Column( mainAxisSize: MainAxisSize.max, children: [ @@ -346,7 +344,7 @@ class _SupportFormWidgetState extends State ), borderRadius: BorderRadius.circular(12), ), - contentPadding: EdgeInsetsDirectional.fromSTEB( + contentPadding: const EdgeInsetsDirectional.fromSTEB( 16, 12, 16, 12), ), style: FlutterFlowTheme.of(context) @@ -406,7 +404,7 @@ class _SupportFormWidgetState extends State ), borderRadius: BorderRadius.circular(12), ), - contentPadding: EdgeInsetsDirectional.fromSTEB( + contentPadding: const EdgeInsetsDirectional.fromSTEB( 16, 12, 16, 12), ), style: FlutterFlowTheme.of(context) @@ -467,7 +465,7 @@ class _SupportFormWidgetState extends State ), borderRadius: BorderRadius.circular(12), ), - contentPadding: EdgeInsetsDirectional.fromSTEB( + contentPadding: const EdgeInsetsDirectional.fromSTEB( 16, 24, 16, 12), ), style: FlutterFlowTheme.of(context) @@ -482,14 +480,14 @@ class _SupportFormWidgetState extends State validator: _model.textController3Validator .asValidator(context), ), - ].divide(SizedBox(height: 12)), + ].divide(const SizedBox(height: 12)), ), ), Padding( - padding: EdgeInsetsDirectional.fromSTEB(0, 16, 0, 0), + padding: const EdgeInsetsDirectional.fromSTEB(0, 16, 0, 0), child: Container( width: double.infinity, - constraints: BoxConstraints( + constraints: const BoxConstraints( maxWidth: 500, ), decoration: BoxDecoration( @@ -502,7 +500,7 @@ class _SupportFormWidgetState extends State ), ), child: Padding( - padding: EdgeInsets.all(8), + padding: const EdgeInsets.all(8), child: Row( mainAxisSize: MainAxisSize.max, children: [ @@ -513,7 +511,7 @@ class _SupportFormWidgetState extends State ), Padding( padding: - EdgeInsetsDirectional.fromSTEB(16, 0, 0, 0), + const EdgeInsetsDirectional.fromSTEB(16, 0, 0, 0), child: Text( 'Upload Screenshot', textAlign: TextAlign.center, @@ -532,22 +530,22 @@ class _SupportFormWidgetState extends State animationsMap['containerOnPageLoadAnimation3']!), ), Padding( - padding: EdgeInsetsDirectional.fromSTEB(0, 24, 0, 12), + padding: const EdgeInsetsDirectional.fromSTEB(0, 24, 0, 12), child: FFButtonWidget( onPressed: () { print('Button pressed ...'); }, text: 'Submit Ticket', - icon: Icon( + icon: const Icon( Icons.receipt_long, size: 15, ), options: FFButtonOptions( width: double.infinity, height: 48, - padding: EdgeInsets.all(0), + padding: const EdgeInsets.all(0), iconPadding: - EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), + const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), color: FlutterFlowTheme.of(context).primary, textStyle: FlutterFlowTheme.of(context).titleSmall.override( @@ -556,7 +554,7 @@ class _SupportFormWidgetState extends State letterSpacing: 0, ), elevation: 4, - borderSide: BorderSide( + borderSide: const BorderSide( color: Colors.transparent, width: 1, ), diff --git a/mobile/app/macos/Flutter/GeneratedPluginRegistrant.swift b/mobile/app/macos/Flutter/GeneratedPluginRegistrant.swift index cccf817..2bfe7e4 100644 --- a/mobile/app/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/mobile/app/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,10 @@ import FlutterMacOS import Foundation +import path_provider_foundation +import sqflite func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) + SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) } diff --git a/mobile/app/pubspec.lock b/mobile/app/pubspec.lock index ed4160b..3f7529b 100644 --- a/mobile/app/pubspec.lock +++ b/mobile/app/pubspec.lock @@ -9,6 +9,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.11.0" + auto_size_text: + dependency: "direct main" + description: + name: auto_size_text + sha256: "3f5261cd3fb5f2a9ab4e2fc3fba84fd9fcaac8821f20a1d4e71f557521b22599" + url: "https://pub.dev" + source: hosted + version: "3.0.0" boolean_selector: dependency: transitive description: @@ -17,6 +25,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + cached_network_image: + dependency: "direct main" + description: + name: cached_network_image + sha256: "28ea9690a8207179c319965c13cd8df184d5ee721ae2ce60f398ced1219cea1f" + url: "https://pub.dev" + source: hosted + version: "3.3.1" + cached_network_image_platform_interface: + dependency: transitive + description: + name: cached_network_image_platform_interface + sha256: "9e90e78ae72caa874a323d78fa6301b3fb8fa7ea76a8f96dc5b5bf79f283bf2f" + url: "https://pub.dev" + source: hosted + version: "4.0.0" + cached_network_image_web: + dependency: transitive + description: + name: cached_network_image_web + sha256: "205d6a9f1862de34b93184f22b9d2d94586b2f05c581d546695e3d8f6a805cd7" + url: "https://pub.dev" + source: hosted + version: "1.2.0" characters: dependency: transitive description: @@ -41,6 +73,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.18.0" + crypto: + dependency: transitive + description: + name: crypto + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + url: "https://pub.dev" + source: hosted + version: "3.0.3" cupertino_icons: dependency: "direct main" description: @@ -49,6 +89,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.8" + easy_debounce: + dependency: "direct main" + description: + name: easy_debounce + sha256: f082609cfb8f37defb9e37fc28bc978c6712dedf08d4c5a26f820fa10165a236 + url: "https://pub.dev" + source: hosted + version: "2.0.3" fake_async: dependency: transitive description: @@ -57,11 +105,51 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + file: + dependency: transitive + description: + name: file + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + url: "https://pub.dev" + source: hosted + version: "1.1.0" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" + flutter_animate: + dependency: "direct main" + description: + name: flutter_animate + sha256: "7c8a6594a9252dad30cc2ef16e33270b6248c4dedc3b3d06c86c4f3f4dc05ae5" + url: "https://pub.dev" + source: hosted + version: "4.5.0" + flutter_cache_manager: + dependency: transitive + description: + name: flutter_cache_manager + sha256: "395d6b7831f21f3b989ebedbb785545932adb9afe2622c1ffacf7f4b53a7e544" + url: "https://pub.dev" + source: hosted + version: "3.3.2" flutter_lints: dependency: "direct dev" description: @@ -70,11 +158,43 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.2" + flutter_shaders: + dependency: transitive + description: + name: flutter_shaders + sha256: "02750b545c01ff4d8e9bbe8f27a7731aa3778402506c67daa1de7f5fc3f4befe" + url: "https://pub.dev" + source: hosted + version: "0.1.2" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" + google_fonts: + dependency: "direct main" + description: + name: google_fonts + sha256: b1ac0fe2832c9cc95e5e88b57d627c5e68c223b9657f4b96e1487aa9098c7b82 + url: "https://pub.dev" + source: hosted + version: "6.2.1" + http: + dependency: transitive + description: + name: http + sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" leak_tracker: dependency: transitive description: @@ -131,6 +251,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.12.0" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + octo_image: + dependency: transitive + description: + name: octo_image + sha256: "45b40f99622f11901238e18d48f5f12ea36426d8eced9f4cbf58479c7aa2430d" + url: "https://pub.dev" + source: hosted + version: "2.0.0" path: dependency: transitive description: @@ -139,6 +275,86 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.0" + path_provider: + dependency: transitive + description: + name: path_provider + sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 + url: "https://pub.dev" + source: hosted + version: "2.1.3" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: bca87b0165ffd7cdb9cad8edd22d18d2201e886d9a9f19b4fb3452ea7df3a72a + url: "https://pub.dev" + source: hosted + version: "2.2.6" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 + url: "https://pub.dev" + source: hosted + version: "2.4.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: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" + url: "https://pub.dev" + source: hosted + version: "2.2.1" + platform: + dependency: transitive + description: + name: platform + sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" + url: "https://pub.dev" + source: hosted + version: "3.1.5" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + provider: + dependency: "direct main" + description: + name: provider + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c + url: "https://pub.dev" + source: hosted + version: "6.1.2" + rxdart: + dependency: transitive + description: + name: rxdart + sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" + url: "https://pub.dev" + source: hosted + version: "0.27.7" sky_engine: dependency: transitive description: flutter @@ -152,6 +368,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.0" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + sqflite: + dependency: transitive + description: + name: sqflite + sha256: a43e5a27235518c03ca238e7b4732cf35eabe863a369ceba6cbefa537a66f16d + url: "https://pub.dev" + source: hosted + version: "2.3.3+1" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + sha256: "3da423ce7baf868be70e2c0976c28a1bb2f73644268b7ffa7d2e08eab71f16a4" + url: "https://pub.dev" + source: hosted + version: "2.5.4" stack_trace: dependency: transitive description: @@ -176,6 +416,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558" + url: "https://pub.dev" + source: hosted + version: "3.1.0+1" term_glyph: dependency: transitive description: @@ -192,6 +440,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.0" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" + uuid: + dependency: transitive + description: + name: uuid + sha256: "814e9e88f21a176ae1359149021870e87f7cddaf633ab678a5d2b0bff7fd1ba8" + url: "https://pub.dev" + source: hosted + version: "4.4.0" vector_math: dependency: transitive description: @@ -208,6 +472,30 @@ packages: url: "https://pub.dev" source: hosted version: "14.2.1" + web: + dependency: transitive + description: + name: web + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + url: "https://pub.dev" + source: hosted + version: "0.5.1" + win32: + dependency: transitive + description: + name: win32 + sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4 + url: "https://pub.dev" + source: hosted + version: "5.5.1" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d + url: "https://pub.dev" + source: hosted + version: "1.0.4" sdks: dart: ">=3.4.3 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" + flutter: ">=3.22.0" diff --git a/mobile/app/pubspec.yaml b/mobile/app/pubspec.yaml index 11f607d..6dc73af 100644 --- a/mobile/app/pubspec.yaml +++ b/mobile/app/pubspec.yaml @@ -36,6 +36,12 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.6 + auto_size_text: any + cached_network_image: any + flutter_animate: any + google_fonts: any + provider: any + easy_debounce: any dev_dependencies: flutter_test: sdk: flutter From 12481487d9e9c1b25409298b809a608a17c8894e Mon Sep 17 00:00:00 2001 From: Ashton Date: Fri, 5 Jul 2024 23:07:55 -0600 Subject: [PATCH 04/24] refactoring theme updates (removing flutterflow theme). --- .../conversation_details_overlay_model.dart | 7 +- .../conversation_details_overlay_widget.dart | 35 +- .../conversation_options_model.dart | 3 +- .../conversation_options_widget.dart | 29 +- .../conversation_thread_model.dart | 6 +- .../conversation_thread_widget.dart | 16 +- .../model_item/model_item_model.dart | 3 +- .../model_item/model_item_widget.dart | 17 +- mobile/app/lib/components/no_chats_model.dart | 3 +- .../app/lib/components/no_chats_widget.dart | 9 +- .../options_dialog/options_dialog_model.dart | 3 +- .../options_dialog/options_dialog_widget.dart | 13 +- .../app/lib/components/popup/popup_model.dart | 5 +- .../lib/components/popup/popup_widget.dart | 13 +- .../prompt_box/prompt_box_model.dart | 4 +- .../prompt_box/prompt_box_widget.dart | 28 +- mobile/app/lib/main.dart | 114 +-- mobile/app/lib/pages/conversation_model.dart | 5 +- mobile/app/lib/pages/conversation_widget.dart | 19 +- .../lib/pages/conversations_list_model.dart | 4 +- .../lib/pages/conversations_list_widget.dart | 30 +- mobile/app/lib/pages/image_details_model.dart | 6 +- .../app/lib/pages/image_details_widget.dart | 22 +- mobile/app/lib/pages/settings_model.dart | 5 +- mobile/app/lib/pages/settings_widget.dart | 283 ++++---- mobile/app/lib/pages/setup_model.dart | 5 +- mobile/app/lib/pages/setup_widget.dart | 31 +- mobile/app/lib/pages/support_form_model.dart | 6 +- mobile/app/lib/pages/support_form_widget.dart | 224 ++---- mobile/app/lib/theme.dart | 26 + .../flutter/generated_plugin_registrant.cc | 4 + .../app/linux/flutter/generated_plugins.cmake | 1 + .../Flutter/GeneratedPluginRegistrant.swift | 6 + mobile/app/pubspec.lock | 677 ++++++++++++++++++ mobile/app/pubspec.yaml | 1 + .../flutter/generated_plugin_registrant.cc | 6 + .../windows/flutter/generated_plugins.cmake | 2 + 37 files changed, 1073 insertions(+), 598 deletions(-) create mode 100644 mobile/app/lib/theme.dart diff --git a/mobile/app/lib/components/conversation_details_overlay/conversation_details_overlay_model.dart b/mobile/app/lib/components/conversation_details_overlay/conversation_details_overlay_model.dart index ee7aede..00629a4 100644 --- a/mobile/app/lib/components/conversation_details_overlay/conversation_details_overlay_model.dart +++ b/mobile/app/lib/components/conversation_details_overlay/conversation_details_overlay_model.dart @@ -1,9 +1,6 @@ import '/components/options_dialog/options_dialog_widget.dart'; -import '/flutter_flow/flutter_flow_icon_button.dart'; -import '/flutter_flow/flutter_flow_theme.dart'; -import '/flutter_flow/flutter_flow_util.dart'; -import '/flutter_flow/flutter_flow_widgets.dart'; -import '../conversation_details_overlay_widget.dart' +import 'package:flutterflow_ui/flutterflow_ui.dart'; +import 'conversation_details_overlay_widget.dart' show ConversationDetailsOverlayWidget; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; diff --git a/mobile/app/lib/components/conversation_details_overlay/conversation_details_overlay_widget.dart b/mobile/app/lib/components/conversation_details_overlay/conversation_details_overlay_widget.dart index da46e5c..c8b9d15 100644 --- a/mobile/app/lib/components/conversation_details_overlay/conversation_details_overlay_widget.dart +++ b/mobile/app/lib/components/conversation_details_overlay/conversation_details_overlay_widget.dart @@ -1,8 +1,5 @@ import '/components/options_dialog/options_dialog_widget.dart'; -import '/flutter_flow/flutter_flow_icon_button.dart'; -import '/flutter_flow/flutter_flow_theme.dart'; -import '/flutter_flow/flutter_flow_util.dart'; -import '/flutter_flow/flutter_flow_widgets.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; @@ -80,7 +77,7 @@ class _ConversationDetailsOverlayWidgetState padding: const EdgeInsetsDirectional.fromSTEB(16, 0, 0, 0), child: Text( 'Chat Details', - style: FlutterFlowTheme.of(context) + style: AppAppAppAppAppAppAppAppAppTheme .headlineSmall .override( fontFamily: 'Outfit', @@ -91,14 +88,14 @@ class _ConversationDetailsOverlayWidgetState Padding( padding: const EdgeInsetsDirectional.fromSTEB(0, 0, 16, 0), child: FlutterFlowIconButton( - borderColor: FlutterFlowTheme.of(context).alternate, + borderColor: AppAppAppAppAppAppAppAppAppTheme.alternate, borderRadius: 12, borderWidth: 1, buttonSize: 40, - fillColor: FlutterFlowTheme.of(context).accent4, + fillColor: AppAppAppAppAppAppAppAppAppTheme.accent4, icon: Icon( Icons.close_rounded, - color: FlutterFlowTheme.of(context).primaryText, + color: AppAppAppAppAppAppAppAppAppTheme.primaryText, size: 24, ), onPressed: () async { @@ -121,13 +118,13 @@ class _ConversationDetailsOverlayWidgetState TextSpan( text: 'Hello World ', style: TextStyle( - color: FlutterFlowTheme.of(context).primary, + color: AppAppAppAppAppAppAppAppAppTheme.primary, fontWeight: FontWeight.bold, ), ) ], style: - FlutterFlowTheme.of(context).labelMedium.override( + AppAppAppAppAppAppAppAppAppTheme.labelMedium.override( fontFamily: 'Readex Pro', letterSpacing: 0, ), @@ -143,7 +140,7 @@ class _ConversationDetailsOverlayWidgetState children: [ Divider( thickness: 1, - color: FlutterFlowTheme.of(context).tertiary, + color: AppAppAppAppAppAppAppAppAppTheme.tertiary, ), ], ), @@ -157,11 +154,11 @@ class _ConversationDetailsOverlayWidgetState borderRadius: BorderRadius.circular(12), child: Container( decoration: BoxDecoration( - color: FlutterFlowTheme.of(context) + color: AppAppAppAppAppAppAppAppAppTheme .secondaryBackground, borderRadius: BorderRadius.circular(12), border: Border.all( - color: FlutterFlowTheme.of(context).alternate, + color: AppAppAppAppAppAppAppAppAppTheme.alternate, ), ), child: wrapWithModel( @@ -188,26 +185,26 @@ class _ConversationDetailsOverlayWidgetState iconPadding: const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), color: - FlutterFlowTheme.of(context).secondaryBackground, + AppAppAppAppAppAppAppAppAppTheme.secondaryBackground, textStyle: - FlutterFlowTheme.of(context).titleLarge.override( + AppAppAppAppAppAppAppAppAppTheme.titleLarge.override( fontFamily: 'Outfit', fontSize: 18, letterSpacing: 0, ), elevation: 0, borderSide: BorderSide( - color: FlutterFlowTheme.of(context).alternate, + color: AppAppAppAppAppAppAppAppAppTheme.alternate, width: 2, ), borderRadius: BorderRadius.circular(12), - hoverColor: FlutterFlowTheme.of(context).alternate, + hoverColor: AppAppAppAppAppAppAppAppAppTheme.alternate, hoverBorderSide: BorderSide( - color: FlutterFlowTheme.of(context).alternate, + color: AppAppAppAppAppAppAppAppAppTheme.alternate, width: 2, ), hoverTextColor: - FlutterFlowTheme.of(context).primaryText, + AppAppAppAppAppAppAppAppAppTheme.primaryText, hoverElevation: 3, ), ), diff --git a/mobile/app/lib/components/conversation_options/conversation_options_model.dart b/mobile/app/lib/components/conversation_options/conversation_options_model.dart index 42c8a86..6da6dfb 100644 --- a/mobile/app/lib/components/conversation_options/conversation_options_model.dart +++ b/mobile/app/lib/components/conversation_options/conversation_options_model.dart @@ -1,5 +1,4 @@ -import '/flutter_flow/flutter_flow_theme.dart'; -import '/flutter_flow/flutter_flow_util.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'conversation_options_widget.dart' show ConversationOptionsWidget; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; diff --git a/mobile/app/lib/components/conversation_options/conversation_options_widget.dart b/mobile/app/lib/components/conversation_options/conversation_options_widget.dart index 96306aa..37b279f 100644 --- a/mobile/app/lib/components/conversation_options/conversation_options_widget.dart +++ b/mobile/app/lib/components/conversation_options/conversation_options_widget.dart @@ -1,5 +1,4 @@ -import '/flutter_flow/flutter_flow_theme.dart'; -import '/flutter_flow/flutter_flow_util.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; @@ -42,7 +41,7 @@ class _ConversationOptionsWidgetState extends State { return Container( width: double.infinity, decoration: BoxDecoration( - color: FlutterFlowTheme.of(context).secondaryBackground, + color: AppAppAppAppAppAppAppAppAppTheme.secondaryBackground, ), child: Padding( padding: const EdgeInsetsDirectional.fromSTEB(0, 12, 0, 24), @@ -61,7 +60,7 @@ class _ConversationOptionsWidgetState extends State { children: [ Card( clipBehavior: Clip.antiAliasWithSaveLayer, - color: FlutterFlowTheme.of(context).primaryBackground, + color: AppAppAppAppAppAppAppAppAppTheme.primaryBackground, elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(40), @@ -70,7 +69,7 @@ class _ConversationOptionsWidgetState extends State { padding: const EdgeInsets.all(8), child: Icon( Icons.share_rounded, - color: FlutterFlowTheme.of(context).secondaryText, + color: AppAppAppAppAppAppAppAppAppTheme.secondaryText, size: 20, ), ), @@ -84,7 +83,7 @@ class _ConversationOptionsWidgetState extends State { children: [ Text( 'Share', - style: FlutterFlowTheme.of(context) + style: AppAppAppAppAppAppAppAppAppTheme .labelLarge .override( fontFamily: 'Readex Pro', @@ -110,7 +109,7 @@ class _ConversationOptionsWidgetState extends State { children: [ Card( clipBehavior: Clip.antiAliasWithSaveLayer, - color: FlutterFlowTheme.of(context).primaryBackground, + color: AppAppAppAppAppAppAppAppAppTheme.primaryBackground, elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(40), @@ -119,7 +118,7 @@ class _ConversationOptionsWidgetState extends State { padding: const EdgeInsets.all(8), child: Icon( Icons.insert_link, - color: FlutterFlowTheme.of(context).secondaryText, + color: AppAppAppAppAppAppAppAppAppTheme.secondaryText, size: 20, ), ), @@ -133,7 +132,7 @@ class _ConversationOptionsWidgetState extends State { children: [ Text( 'Get Link', - style: FlutterFlowTheme.of(context) + style: AppAppAppAppAppAppAppAppAppTheme .labelLarge .override( fontFamily: 'Readex Pro', @@ -159,7 +158,7 @@ class _ConversationOptionsWidgetState extends State { children: [ Card( clipBehavior: Clip.antiAliasWithSaveLayer, - color: FlutterFlowTheme.of(context).primaryBackground, + color: AppAppAppAppAppAppAppAppAppTheme.primaryBackground, elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(40), @@ -168,7 +167,7 @@ class _ConversationOptionsWidgetState extends State { padding: const EdgeInsets.all(8), child: Icon( Icons.mode_edit, - color: FlutterFlowTheme.of(context).secondaryText, + color: AppAppAppAppAppAppAppAppAppTheme.secondaryText, size: 20, ), ), @@ -182,7 +181,7 @@ class _ConversationOptionsWidgetState extends State { children: [ Text( 'Model selection', - style: FlutterFlowTheme.of(context) + style: AppAppAppAppAppAppAppAppAppTheme .labelLarge .override( fontFamily: 'Readex Pro', @@ -208,7 +207,7 @@ class _ConversationOptionsWidgetState extends State { children: [ Card( clipBehavior: Clip.antiAliasWithSaveLayer, - color: FlutterFlowTheme.of(context).primaryBackground, + color: AppAppAppAppAppAppAppAppAppTheme.primaryBackground, elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(40), @@ -217,7 +216,7 @@ class _ConversationOptionsWidgetState extends State { padding: const EdgeInsets.all(8), child: Icon( Icons.hide_source, - color: FlutterFlowTheme.of(context).secondaryText, + color: AppAppAppAppAppAppAppAppAppTheme.secondaryText, size: 20, ), ), @@ -231,7 +230,7 @@ class _ConversationOptionsWidgetState extends State { children: [ Text( 'Private', - style: FlutterFlowTheme.of(context) + style: AppAppAppAppAppAppAppAppAppTheme .labelLarge .override( fontFamily: 'Readex Pro', diff --git a/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart b/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart index 9bf6598..f2886ac 100644 --- a/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart +++ b/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart @@ -1,10 +1,6 @@ import '/components/conversation_bubbles/conversation_bubbles_widget.dart'; import '/components/prompt_box/prompt_box_widget.dart'; -import '/flutter_flow/flutter_flow_icon_button.dart'; -import '/flutter_flow/flutter_flow_media_display.dart'; -import '/flutter_flow/flutter_flow_theme.dart'; -import '/flutter_flow/flutter_flow_util.dart'; -import '/flutter_flow/flutter_flow_video_player.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'conversation_thread_widget.dart' show ConversationThreadWidget; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; diff --git a/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart b/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart index 51ceb3e..fb4678f 100644 --- a/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart +++ b/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart @@ -1,10 +1,6 @@ import '/components/conversation_bubbles/conversation_bubbles_widget.dart'; import '/components/prompt_box/prompt_box_widget.dart'; -import '/flutter_flow/flutter_flow_icon_button.dart'; -import '/flutter_flow/flutter_flow_media_display.dart'; -import '/flutter_flow/flutter_flow_theme.dart'; -import '/flutter_flow/flutter_flow_util.dart'; -import '/flutter_flow/flutter_flow_video_player.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; @@ -49,7 +45,7 @@ class _ConversationThreadWidgetState extends State { width: double.infinity, height: double.infinity, decoration: BoxDecoration( - color: FlutterFlowTheme.of(context).primaryBackground, + color: AppAppAppAppAppAppAppAppAppTheme.primaryBackground, ), child: Column( mainAxisSize: MainAxisSize.max, @@ -87,7 +83,7 @@ class _ConversationThreadWidgetState extends State { Container( width: double.infinity, decoration: BoxDecoration( - color: FlutterFlowTheme.of(context).secondaryBackground, + color: AppAppAppAppAppAppAppAppAppTheme.secondaryBackground, boxShadow: const [ BoxShadow( blurRadius: 3, @@ -143,15 +139,15 @@ class _ConversationThreadWidgetState extends State { alignment: const AlignmentDirectional(-1, -1), child: FlutterFlowIconButton( borderColor: - FlutterFlowTheme.of(context).error, + AppAppAppAppAppAppAppAppAppTheme.error, borderRadius: 20, borderWidth: 2, buttonSize: 40, - fillColor: FlutterFlowTheme.of(context) + fillColor: AppAppAppAppAppAppAppAppAppTheme .primaryBackground, icon: Icon( Icons.delete_outline_rounded, - color: FlutterFlowTheme.of(context).error, + color: AppAppAppAppAppAppAppAppAppTheme.error, size: 24, ), onPressed: () { diff --git a/mobile/app/lib/components/model_item/model_item_model.dart b/mobile/app/lib/components/model_item/model_item_model.dart index 2faa5ab..7b525eb 100644 --- a/mobile/app/lib/components/model_item/model_item_model.dart +++ b/mobile/app/lib/components/model_item/model_item_model.dart @@ -1,5 +1,4 @@ -import '/flutter_flow/flutter_flow_theme.dart'; -import '/flutter_flow/flutter_flow_util.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'model_item_widget.dart' show ModelItemWidget; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; diff --git a/mobile/app/lib/components/model_item/model_item_widget.dart b/mobile/app/lib/components/model_item/model_item_widget.dart index 90c244f..2d1eaeb 100644 --- a/mobile/app/lib/components/model_item/model_item_widget.dart +++ b/mobile/app/lib/components/model_item/model_item_widget.dart @@ -1,5 +1,4 @@ -import '/flutter_flow/flutter_flow_theme.dart'; -import '/flutter_flow/flutter_flow_util.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; @@ -49,7 +48,7 @@ class _ModelItemWidgetState extends State { }), child: Container( decoration: BoxDecoration( - color: FlutterFlowTheme.of(context).primaryBackground, + color: AppAppAppAppAppAppAppAppAppTheme.primaryBackground, borderRadius: BorderRadius.circular(0), ), child: Padding( @@ -61,10 +60,10 @@ class _ModelItemWidgetState extends State { width: 40, height: 40, decoration: BoxDecoration( - color: FlutterFlowTheme.of(context).accent1, + color: AppAppAppAppAppAppAppAppAppTheme.accent1, borderRadius: BorderRadius.circular(12), border: Border.all( - color: FlutterFlowTheme.of(context).primary, + color: AppAppAppAppAppAppAppAppAppTheme.primary, width: 2, ), ), @@ -73,7 +72,7 @@ class _ModelItemWidgetState extends State { child: Text( 'A', textAlign: TextAlign.center, - style: FlutterFlowTheme.of(context).bodyLarge.override( + style: AppAppAppAppAppAppAppAppAppTheme.bodyLarge.override( fontFamily: 'Readex Pro', letterSpacing: 0, fontWeight: FontWeight.bold, @@ -91,7 +90,7 @@ class _ModelItemWidgetState extends State { children: [ Text( 'Randy Peterson', - style: FlutterFlowTheme.of(context).bodyMedium.override( + style: AppAppAppAppAppAppAppAppAppTheme.bodyMedium.override( fontFamily: 'Readex Pro', letterSpacing: 0, fontWeight: FontWeight.bold, @@ -102,9 +101,9 @@ class _ModelItemWidgetState extends State { child: Text( 'name@domainname.com', style: - FlutterFlowTheme.of(context).bodySmall.override( + AppAppAppAppAppAppAppAppAppTheme.bodySmall.override( fontFamily: 'Readex Pro', - color: FlutterFlowTheme.of(context).primary, + color: AppAppAppAppAppAppAppAppAppTheme.primary, letterSpacing: 0, ), ), diff --git a/mobile/app/lib/components/no_chats_model.dart b/mobile/app/lib/components/no_chats_model.dart index 24e70a3..d50872d 100644 --- a/mobile/app/lib/components/no_chats_model.dart +++ b/mobile/app/lib/components/no_chats_model.dart @@ -1,5 +1,4 @@ -import '/flutter_flow/flutter_flow_theme.dart'; -import '/flutter_flow/flutter_flow_util.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'no_chats_widget.dart' show NoChatsWidget; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; diff --git a/mobile/app/lib/components/no_chats_widget.dart b/mobile/app/lib/components/no_chats_widget.dart index 3cf8561..5ce3259 100644 --- a/mobile/app/lib/components/no_chats_widget.dart +++ b/mobile/app/lib/components/no_chats_widget.dart @@ -1,5 +1,4 @@ -import '/flutter_flow/flutter_flow_theme.dart'; -import '/flutter_flow/flutter_flow_util.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; @@ -60,9 +59,9 @@ class _NoChatsWidgetState extends State { child: Text( widget.title, textAlign: TextAlign.center, - style: FlutterFlowTheme.of(context).headlineSmall.override( + style: AppAppAppAppAppAppAppAppAppTheme.headlineSmall.override( fontFamily: 'Outfit', - color: FlutterFlowTheme.of(context).primaryText, + color: AppAppAppAppAppAppAppAppAppTheme.primaryText, letterSpacing: 0, ), ), @@ -72,7 +71,7 @@ class _NoChatsWidgetState extends State { child: Text( widget.body, textAlign: TextAlign.center, - style: FlutterFlowTheme.of(context).labelMedium.override( + style: AppAppAppAppAppAppAppAppAppTheme.labelMedium.override( fontFamily: 'Readex Pro', letterSpacing: 0, ), diff --git a/mobile/app/lib/components/options_dialog/options_dialog_model.dart b/mobile/app/lib/components/options_dialog/options_dialog_model.dart index 8910477..d178ffe 100644 --- a/mobile/app/lib/components/options_dialog/options_dialog_model.dart +++ b/mobile/app/lib/components/options_dialog/options_dialog_model.dart @@ -1,5 +1,4 @@ -import '/flutter_flow/flutter_flow_theme.dart'; -import '/flutter_flow/flutter_flow_util.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'options_dialog_widget.dart' show OptionsDialogWidget; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; diff --git a/mobile/app/lib/components/options_dialog/options_dialog_widget.dart b/mobile/app/lib/components/options_dialog/options_dialog_widget.dart index c06d51b..c645d80 100644 --- a/mobile/app/lib/components/options_dialog/options_dialog_widget.dart +++ b/mobile/app/lib/components/options_dialog/options_dialog_widget.dart @@ -1,7 +1,4 @@ -import '/flutter_flow/flutter_flow_icon_button.dart'; -import '/flutter_flow/flutter_flow_theme.dart'; -import '/flutter_flow/flutter_flow_util.dart'; -import '/flutter_flow/flutter_flow_widgets.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; @@ -86,7 +83,7 @@ class _PopupWidgetState extends State { padding: const EdgeInsetsDirectional.fromSTEB(0, 0, 12, 0), child: Text( 'Refine the components modal.', - style: FlutterFlowTheme.of(context) + style: AppAppAppAppAppAppAppAppAppTheme .headlineMedium .override( fontFamily: 'Outfit', @@ -121,7 +118,7 @@ class _PopupWidgetState extends State { ), Text( 'FlutterFlow is a visual development platform that allows you to easily create beautiful and responsive user interfaces for your mobile and web applications. With its drag-and-drop interface and pre-built components, you can quickly prototype and build your app without writing any code. \nAdditionally, FlutterFlow\'s real-time preview feature allows you to see your changes in real-time and make adjustments on the fly.', - style: FlutterFlowTheme.of(context).labelMedium.override( + style: AppAppAppAppAppAppAppAppAppTheme.labelMedium.override( fontFamily: 'Readex Pro', color: const Color(0xFF57636C), fontSize: 14, @@ -148,7 +145,7 @@ class _PopupWidgetState extends State { const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), color: Colors.white, textStyle: - FlutterFlowTheme.of(context).bodySmall.override( + AppAppAppAppAppAppAppAppAppTheme.bodySmall.override( fontFamily: 'Readex Pro', color: const Color(0xFF14181B), fontSize: 12, @@ -177,7 +174,7 @@ class _PopupWidgetState extends State { iconPadding: const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), color: const Color(0xFF4B39EF), - textStyle: FlutterFlowTheme.of(context) + textStyle: AppAppAppAppAppAppAppAppAppTheme .titleSmall .override( fontFamily: 'Readex Pro', diff --git a/mobile/app/lib/components/popup/popup_model.dart b/mobile/app/lib/components/popup/popup_model.dart index 4950211..ca192a9 100644 --- a/mobile/app/lib/components/popup/popup_model.dart +++ b/mobile/app/lib/components/popup/popup_model.dart @@ -1,7 +1,4 @@ -import '/flutter_flow/flutter_flow_icon_button.dart'; -import '/flutter_flow/flutter_flow_theme.dart'; -import '/flutter_flow/flutter_flow_util.dart'; -import '/flutter_flow/flutter_flow_widgets.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'popup_widget.dart' show PopupWidget; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; diff --git a/mobile/app/lib/components/popup/popup_widget.dart b/mobile/app/lib/components/popup/popup_widget.dart index d2c6420..32175e6 100644 --- a/mobile/app/lib/components/popup/popup_widget.dart +++ b/mobile/app/lib/components/popup/popup_widget.dart @@ -1,7 +1,4 @@ -import '/flutter_flow/flutter_flow_icon_button.dart'; -import '/flutter_flow/flutter_flow_theme.dart'; -import '/flutter_flow/flutter_flow_util.dart'; -import '/flutter_flow/flutter_flow_widgets.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; @@ -86,7 +83,7 @@ class _PopupWidgetState extends State { padding: const EdgeInsetsDirectional.fromSTEB(0, 0, 12, 0), child: Text( 'Refine the components modal.', - style: FlutterFlowTheme.of(context) + style: AppAppAppAppAppAppAppAppAppTheme .headlineMedium .override( fontFamily: 'Outfit', @@ -121,7 +118,7 @@ class _PopupWidgetState extends State { ), Text( 'FlutterFlow is a visual development platform that allows you to easily create beautiful and responsive user interfaces for your mobile and web applications. With its drag-and-drop interface and pre-built components, you can quickly prototype and build your app without writing any code. \nAdditionally, FlutterFlow\'s real-time preview feature allows you to see your changes in real-time and make adjustments on the fly.', - style: FlutterFlowTheme.of(context).labelMedium.override( + style: AppAppAppAppAppAppAppAppAppTheme.labelMedium.override( fontFamily: 'Readex Pro', color: const Color(0xFF57636C), fontSize: 14, @@ -148,7 +145,7 @@ class _PopupWidgetState extends State { const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), color: Colors.white, textStyle: - FlutterFlowTheme.of(context).bodySmall.override( + AppAppAppAppAppAppAppAppAppTheme.bodySmall.override( fontFamily: 'Readex Pro', color: const Color(0xFF14181B), fontSize: 12, @@ -177,7 +174,7 @@ class _PopupWidgetState extends State { iconPadding: const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), color: const Color(0xFF4B39EF), - textStyle: FlutterFlowTheme.of(context) + textStyle: AppAppAppAppAppAppAppAppAppTheme .titleSmall .override( fontFamily: 'Readex Pro', diff --git a/mobile/app/lib/components/prompt_box/prompt_box_model.dart b/mobile/app/lib/components/prompt_box/prompt_box_model.dart index bf0906b..ab11559 100644 --- a/mobile/app/lib/components/prompt_box/prompt_box_model.dart +++ b/mobile/app/lib/components/prompt_box/prompt_box_model.dart @@ -1,6 +1,4 @@ -import '/flutter_flow/flutter_flow_icon_button.dart'; -import '/flutter_flow/flutter_flow_theme.dart'; -import '/flutter_flow/flutter_flow_util.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'prompt_box_widget.dart' show PromptBoxWidget; import 'package:easy_debounce/easy_debounce.dart'; import 'package:flutter/material.dart'; diff --git a/mobile/app/lib/components/prompt_box/prompt_box_widget.dart b/mobile/app/lib/components/prompt_box/prompt_box_widget.dart index e53a61f..77a7119 100644 --- a/mobile/app/lib/components/prompt_box/prompt_box_widget.dart +++ b/mobile/app/lib/components/prompt_box/prompt_box_widget.dart @@ -1,6 +1,4 @@ -import '/flutter_flow/flutter_flow_icon_button.dart'; -import '/flutter_flow/flutter_flow_theme.dart'; -import '/flutter_flow/flutter_flow_util.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:easy_debounce/easy_debounce.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; @@ -47,7 +45,7 @@ class _PromptBoxWidgetState extends State { child: Container( width: double.infinity, decoration: BoxDecoration( - color: FlutterFlowTheme.of(context).secondaryBackground, + color: AppAppAppAppAppAppAppAppAppTheme.secondaryBackground, borderRadius: BorderRadius.circular(10), ), alignment: const AlignmentDirectional(0, 0), @@ -69,7 +67,7 @@ class _PromptBoxWidgetState extends State { buttonSize: 40, icon: Icon( Icons.file_upload_outlined, - color: FlutterFlowTheme.of(context).secondaryText, + color: AppAppAppAppAppAppAppAppAppTheme.secondaryText, size: 30, ), onPressed: () { @@ -88,7 +86,7 @@ class _PromptBoxWidgetState extends State { buttonSize: 40, icon: Icon( Icons.photo_outlined, - color: FlutterFlowTheme.of(context).secondaryText, + color: AppAppAppAppAppAppAppAppAppTheme.secondaryText, size: 30, ), onPressed: () { @@ -107,7 +105,7 @@ class _PromptBoxWidgetState extends State { buttonSize: 40, icon: Icon( Icons.mic_none, - color: FlutterFlowTheme.of(context).secondaryText, + color: AppAppAppAppAppAppAppAppAppTheme.secondaryText, size: 30, ), onPressed: () { @@ -126,7 +124,7 @@ class _PromptBoxWidgetState extends State { buttonSize: 40, icon: Icon( Icons.videocam_outlined, - color: FlutterFlowTheme.of(context).secondaryText, + color: AppAppAppAppAppAppAppAppAppTheme.secondaryText, size: 30, ), onPressed: () { @@ -145,7 +143,7 @@ class _PromptBoxWidgetState extends State { buttonSize: 40, icon: Icon( Icons.camera_alt_outlined, - color: FlutterFlowTheme.of(context).secondaryText, + color: AppAppAppAppAppAppAppAppAppTheme.secondaryText, size: 30, ), onPressed: () { @@ -177,16 +175,16 @@ class _PromptBoxWidgetState extends State { obscureText: false, decoration: InputDecoration( isDense: false, - counterStyle: FlutterFlowTheme.of(context) + counterStyle: AppAppAppAppAppAppAppAppAppTheme .bodyMedium .override( fontFamily: 'Readex Pro', - color: FlutterFlowTheme.of(context).secondaryText, + color: AppAppAppAppAppAppAppAppAppTheme.secondaryText, letterSpacing: 0, ), enabledBorder: OutlineInputBorder( borderSide: BorderSide( - color: FlutterFlowTheme.of(context).secondaryText, + color: AppAppAppAppAppAppAppAppAppTheme.secondaryText, width: 2, ), borderRadius: BorderRadius.circular(12), @@ -213,14 +211,14 @@ class _PromptBoxWidgetState extends State { borderRadius: BorderRadius.circular(12), ), filled: true, - fillColor: FlutterFlowTheme.of(context).alternate, + fillColor: AppAppAppAppAppAppAppAppAppTheme.alternate, suffixIcon: Icon( Icons.send, - color: FlutterFlowTheme.of(context).primary, + color: AppAppAppAppAppAppAppAppAppTheme.primary, size: 30, ), ), - style: FlutterFlowTheme.of(context).bodyMedium.override( + style: AppAppAppAppAppAppAppAppAppTheme.bodyMedium.override( fontFamily: 'Lato', fontSize: 14, letterSpacing: 0, diff --git a/mobile/app/lib/main.dart b/mobile/app/lib/main.dart index 8e94089..501f923 100644 --- a/mobile/app/lib/main.dart +++ b/mobile/app/lib/main.dart @@ -1,4 +1,6 @@ +import 'package:app/pages/settings_widget.dart'; import 'package:flutter/material.dart'; +import 'theme.dart'; void main() { runApp(const MyApp()); @@ -10,116 +12,10 @@ class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { - return MaterialApp( + + return const MaterialApp( title: 'Flutter Demo', - theme: ThemeData( - // This is the theme of your application. - // - // TRY THIS: Try running your application with "flutter run". You'll see - // the application has a purple toolbar. Then, without quitting the app, - // try changing the seedColor in the colorScheme below to Colors.green - // and then invoke "hot reload" (save your changes or press the "hot - // reload" button in a Flutter-supported IDE, or press "r" if you used - // the command line to start the app). - // - // Notice that the counter didn't reset back to zero; the application - // state is not lost during the reload. To reset the state, use hot - // restart instead. - // - // This works for code too, not just values: Most code changes can be - // tested with just a hot reload. - colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), - useMaterial3: true, - ), - home: const MyHomePage(title: 'Flutter Demo Home Page'), - ); - } -} - -class MyHomePage extends StatefulWidget { - const MyHomePage({super.key, required this.title}); - - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. - - // This class is the configuration for the state. It holds the values (in this - // case the title) provided by the parent (in this case the App widget) and - // used by the build method of the State. Fields in a Widget subclass are - // always marked "final". - - final String title; - - @override - State createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - // This call to setState tells the Flutter framework that something has - // changed in this State, which causes it to rerun the build method below - // so that the display can reflect the updated values. If we changed - // _counter without calling setState(), then the build method would not be - // called again, and so nothing would appear to happen. - _counter++; - }); - } - - @override - Widget build(BuildContext context) { - // This method is rerun every time setState is called, for instance as done - // by the _incrementCounter method above. - // - // The Flutter framework has been optimized to make rerunning build methods - // fast, so that you can just rebuild anything that needs updating rather - // than having to individually change instances of widgets. - return Scaffold( - appBar: AppBar( - // TRY THIS: Try changing the color here to a specific color (to - // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar - // change color while the other colors stay the same. - backgroundColor: Theme.of(context).colorScheme.inversePrimary, - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. - title: Text(widget.title), - ), - body: Center( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. - child: Column( - // Column is also a layout widget. It takes a list of children and - // arranges them vertically. By default, it sizes itself to fit its - // children horizontally, and tries to be as tall as its parent. - // - // Column has various properties to control how it sizes itself and - // how it positions its children. Here we use mainAxisAlignment to - // center the children vertically; the main axis here is the vertical - // axis because Columns are vertical (the cross axis would be - // horizontal). - // - // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint" - // action in the IDE, or press "p" in the console), to see the - // wireframe for each widget. - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text( - 'You have pushed the button this many times:', - ), - Text( - '$_counter', - style: Theme.of(context).textTheme.headlineMedium, - ), - ], - ), - ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: const Icon(Icons.add), - ), // This trailing comma makes auto-formatting nicer for build methods. + home: SettingsWidget(), ); } } diff --git a/mobile/app/lib/pages/conversation_model.dart b/mobile/app/lib/pages/conversation_model.dart index 36fcfb1..8273bd9 100644 --- a/mobile/app/lib/pages/conversation_model.dart +++ b/mobile/app/lib/pages/conversation_model.dart @@ -1,10 +1,7 @@ import '/components/conversation_details_overlay/conversation_details_overlay_widget.dart'; import '/components/conversation_thread/conversation_thread_widget.dart'; import '/components/model_item/model_item_widget.dart'; -import '/flutter_flow/flutter_flow_icon_button.dart'; -import '/flutter_flow/flutter_flow_theme.dart'; -import '/flutter_flow/flutter_flow_util.dart'; -import '/flutter_flow/flutter_flow_widgets.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'conversation_widget.dart' show ConversationWidget; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; diff --git a/mobile/app/lib/pages/conversation_widget.dart b/mobile/app/lib/pages/conversation_widget.dart index ec5f357..76aa4cb 100644 --- a/mobile/app/lib/pages/conversation_widget.dart +++ b/mobile/app/lib/pages/conversation_widget.dart @@ -1,14 +1,13 @@ import '/components/conversation_details_overlay/conversation_details_overlay_widget.dart'; import '/components/conversation_thread/conversation_thread_widget.dart'; import '/components/model_item/model_item_widget.dart'; -import '/flutter_flow/flutter_flow_icon_button.dart'; -import '/flutter_flow/flutter_flow_theme.dart'; -import '/flutter_flow/flutter_flow_util.dart'; -import '/flutter_flow/flutter_flow_widgets.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; +import '/theme.dart'; + import 'conversation_model.dart'; export 'conversation_model.dart'; @@ -45,9 +44,9 @@ class _ConversationWidgetState extends State { : FocusScope.of(context).unfocus(), child: Scaffold( key: scaffoldKey, - backgroundColor: FlutterFlowTheme.of(context).secondaryBackground, + backgroundColor: myAppTheme.of(context).secondaryBackground, appBar: AppBar( - backgroundColor: FlutterFlowTheme.of(context).primaryBackground, + backgroundColor: myAppTheme.of(context).primaryBackground, automaticallyImplyLeading: false, leading: FlutterFlowIconButton( borderColor: Colors.transparent, @@ -56,7 +55,7 @@ class _ConversationWidgetState extends State { buttonSize: 60, icon: Icon( Icons.arrow_back_rounded, - color: FlutterFlowTheme.of(context).primaryText, + color: AppTheme.primaryText, size: 30, ), onPressed: () async { @@ -81,14 +80,14 @@ class _ConversationWidgetState extends State { Padding( padding: const EdgeInsetsDirectional.fromSTEB(0, 8, 16, 8), child: FlutterFlowIconButton( - borderColor: FlutterFlowTheme.of(context).alternate, + borderColor: AppTheme.alternate, borderRadius: 12, borderWidth: 2, buttonSize: 40, - fillColor: FlutterFlowTheme.of(context).primaryBackground, + fillColor: AppTheme.primaryBackground, icon: Icon( Icons.more_vert, - color: FlutterFlowTheme.of(context).primaryText, + color: AppTheme.primaryText, size: 24, ), onPressed: () async { diff --git a/mobile/app/lib/pages/conversations_list_model.dart b/mobile/app/lib/pages/conversations_list_model.dart index b5339a6..96baaea 100644 --- a/mobile/app/lib/pages/conversations_list_model.dart +++ b/mobile/app/lib/pages/conversations_list_model.dart @@ -1,7 +1,5 @@ import '/components/conversation_options/conversation_options_widget.dart'; -import '/flutter_flow/flutter_flow_theme.dart'; -import '/flutter_flow/flutter_flow_util.dart'; -import '/flutter_flow/flutter_flow_widgets.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'conversations_list_widget.dart' show ConversationsListWidget; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; diff --git a/mobile/app/lib/pages/conversations_list_widget.dart b/mobile/app/lib/pages/conversations_list_widget.dart index 6bac5ec..1a2eff3 100644 --- a/mobile/app/lib/pages/conversations_list_widget.dart +++ b/mobile/app/lib/pages/conversations_list_widget.dart @@ -1,7 +1,5 @@ import '/components/conversation_options/conversation_options_widget.dart'; -import '/flutter_flow/flutter_flow_theme.dart'; -import '/flutter_flow/flutter_flow_util.dart'; -import '/flutter_flow/flutter_flow_widgets.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; @@ -43,7 +41,7 @@ class _ConversationsListWidgetState extends State { : FocusScope.of(context).unfocus(), child: Scaffold( key: scaffoldKey, - backgroundColor: FlutterFlowTheme.of(context).secondaryBackground, + backgroundColor: AppTheme.secondaryBackground, floatingActionButton: InkWell( splashColor: Colors.transparent, focusColor: Colors.transparent, @@ -74,21 +72,21 @@ class _ConversationsListWidgetState extends State { onPressed: () async { context.pushNamed('Conversation'); }, - backgroundColor: FlutterFlowTheme.of(context).primary, + backgroundColor: AppTheme.primary, elevation: 8, child: Icon( Icons.add, - color: FlutterFlowTheme.of(context).info, + color: AppTheme.info, size: 24, ), ), ), appBar: AppBar( - backgroundColor: FlutterFlowTheme.of(context).secondaryBackground, + backgroundColor: AppTheme.secondaryBackground, automaticallyImplyLeading: false, title: Text( 'My Chats', - style: FlutterFlowTheme.of(context).headlineLarge.override( + style: AppTheme.headlineLarge.override( fontFamily: 'Outfit', letterSpacing: 0, ), @@ -107,7 +105,7 @@ class _ConversationsListWidgetState extends State { padding: const EdgeInsetsDirectional.fromSTEB(16, 0, 0, 0), child: Text( 'AI conversations', - style: FlutterFlowTheme.of(context).labelMedium.override( + style: AppTheme.labelMedium.override( fontFamily: 'Readex Pro', letterSpacing: 0, ), @@ -140,14 +138,14 @@ class _ConversationsListWidgetState extends State { width: 100, height: 100, decoration: BoxDecoration( - color: FlutterFlowTheme.of(context) + color: AppTheme .secondaryBackground, ), child: Align( alignment: const AlignmentDirectional(0, 0), child: Icon( Icons.settings_outlined, - color: FlutterFlowTheme.of(context) + color: AppTheme .secondaryText, size: 48, ), @@ -164,7 +162,7 @@ class _ConversationsListWidgetState extends State { MediaQuery.sizeOf(context).width, ), decoration: BoxDecoration( - color: FlutterFlowTheme.of(context) + color: AppTheme .secondaryBackground, shape: BoxShape.rectangle, ), @@ -184,7 +182,7 @@ class _ConversationsListWidgetState extends State { Text( 'Hello World', style: - FlutterFlowTheme.of(context) + AppTheme .headlineLarge .override( fontFamily: 'Outfit', @@ -203,7 +201,7 @@ class _ConversationsListWidgetState extends State { Expanded( child: Text( 'Hello World', - style: FlutterFlowTheme.of( + style: FlutterFlowAppTheme.of( context) .bodyMedium .override( @@ -225,12 +223,12 @@ class _ConversationsListWidgetState extends State { width: 100, height: 100, decoration: BoxDecoration( - color: FlutterFlowTheme.of(context) + color: AppTheme .secondaryBackground, ), child: Icon( Icons.chevron_right_rounded, - color: FlutterFlowTheme.of(context) + color: AppTheme .secondaryText, size: 48, ), diff --git a/mobile/app/lib/pages/image_details_model.dart b/mobile/app/lib/pages/image_details_model.dart index 5d4863d..9691f5b 100644 --- a/mobile/app/lib/pages/image_details_model.dart +++ b/mobile/app/lib/pages/image_details_model.dart @@ -1,8 +1,4 @@ -import '/flutter_flow/flutter_flow_animations.dart'; -import '/flutter_flow/flutter_flow_icon_button.dart'; -import '/flutter_flow/flutter_flow_theme.dart'; -import '/flutter_flow/flutter_flow_util.dart'; -import '/flutter_flow/flutter_flow_widgets.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'image_details_widget.dart' show ImageDetailsWidget; import 'package:auto_size_text/auto_size_text.dart'; import 'package:cached_network_image/cached_network_image.dart'; diff --git a/mobile/app/lib/pages/image_details_widget.dart b/mobile/app/lib/pages/image_details_widget.dart index 6b12719..2694247 100644 --- a/mobile/app/lib/pages/image_details_widget.dart +++ b/mobile/app/lib/pages/image_details_widget.dart @@ -1,8 +1,4 @@ -import '/flutter_flow/flutter_flow_animations.dart'; -import '/flutter_flow/flutter_flow_icon_button.dart'; -import '/flutter_flow/flutter_flow_theme.dart'; -import '/flutter_flow/flutter_flow_util.dart'; -import '/flutter_flow/flutter_flow_widgets.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; @@ -106,9 +102,9 @@ class _ImageDetailsWidgetState extends State : FocusScope.of(context).unfocus(), child: Scaffold( key: scaffoldKey, - backgroundColor: FlutterFlowTheme.of(context).secondaryBackground, + backgroundColor: AppTheme.secondaryBackground, appBar: AppBar( - backgroundColor: FlutterFlowTheme.of(context).secondaryBackground, + backgroundColor: AppTheme.secondaryBackground, automaticallyImplyLeading: false, leading: FlutterFlowIconButton( borderColor: Colors.transparent, @@ -117,7 +113,7 @@ class _ImageDetailsWidgetState extends State buttonSize: 60, icon: Icon( Icons.arrow_back_rounded, - color: FlutterFlowTheme.of(context).primaryText, + color: AppTheme.primaryText, size: 30, ), onPressed: () async { @@ -167,11 +163,11 @@ class _ImageDetailsWidgetState extends State width: 36, height: 36, decoration: BoxDecoration( - color: FlutterFlowTheme.of(context).accent1, + color: AppTheme.accent1, borderRadius: BorderRadius.circular(12), shape: BoxShape.rectangle, border: Border.all( - color: FlutterFlowTheme.of(context).primary, + color: AppTheme.primary, width: 2, ), ), @@ -207,7 +203,7 @@ class _ImageDetailsWidgetState extends State child: AutoSizeText( 'Thanks so much! We really do appreciate it!', textAlign: TextAlign.start, - style: FlutterFlowTheme.of(context) + style: AppTheme .bodyMedium .override( fontFamily: 'Readex Pro', @@ -220,7 +216,7 @@ class _ImageDetailsWidgetState extends State 0, 4, 0, 0), child: Text( 'Just Now', - style: FlutterFlowTheme.of(context) + style: AppTheme .labelSmall .override( fontFamily: 'Readex Pro', @@ -237,7 +233,7 @@ class _ImageDetailsWidgetState extends State child: AutoSizeText( 'Thanks so much! We really do appreciate it!', textAlign: TextAlign.start, - style: FlutterFlowTheme.of(context) + style: AppTheme .labelLarge .override( fontFamily: 'Readex Pro', diff --git a/mobile/app/lib/pages/settings_model.dart b/mobile/app/lib/pages/settings_model.dart index c5f34a0..39df4a1 100644 --- a/mobile/app/lib/pages/settings_model.dart +++ b/mobile/app/lib/pages/settings_model.dart @@ -1,8 +1,5 @@ import '/components/popup/popup_widget.dart'; -import '/flutter_flow/flutter_flow_animations.dart'; -import '/flutter_flow/flutter_flow_theme.dart'; -import '/flutter_flow/flutter_flow_util.dart'; -import '/flutter_flow/flutter_flow_widgets.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'settings_widget.dart' show SettingsWidget; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; diff --git a/mobile/app/lib/pages/settings_widget.dart b/mobile/app/lib/pages/settings_widget.dart index d1f5e44..2558ae0 100644 --- a/mobile/app/lib/pages/settings_widget.dart +++ b/mobile/app/lib/pages/settings_widget.dart @@ -1,8 +1,5 @@ import '/components/popup/popup_widget.dart'; -import '/flutter_flow/flutter_flow_animations.dart'; -import '/flutter_flow/flutter_flow_theme.dart'; -import '/flutter_flow/flutter_flow_util.dart'; -import '/flutter_flow/flutter_flow_widgets.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_animate/flutter_animate.dart'; @@ -91,14 +88,14 @@ class _SettingsWidgetState extends State Widget build(BuildContext context) { return Scaffold( key: scaffoldKey, - backgroundColor: FlutterFlowTheme.of(context).secondaryBackground, + backgroundColor: AppTheme.secondaryBackground, appBar: MediaQuery.sizeOf(context).width <= 991.0 ? AppBar( - backgroundColor: FlutterFlowTheme.of(context).secondaryBackground, + backgroundColor: AppTheme.secondaryBackground, automaticallyImplyLeading: false, title: Text( 'My Profile', - style: FlutterFlowTheme.of(context).headlineMedium.override( + style: AppTheme.headlineMedium.override( fontFamily: 'Outfit', letterSpacing: 0, ), @@ -124,10 +121,10 @@ class _SettingsWidgetState extends State width: 270, height: double.infinity, decoration: BoxDecoration( - color: FlutterFlowTheme.of(context).primaryBackground, + color: AppTheme.primaryBackground, borderRadius: BorderRadius.circular(0), border: Border.all( - color: FlutterFlowTheme.of(context).alternate, + color: AppTheme.alternate, width: 1, ), ), @@ -145,7 +142,7 @@ class _SettingsWidgetState extends State children: [ Icon( Icons.add_task_rounded, - color: FlutterFlowTheme.of(context).primary, + color: AppTheme.primary, size: 32, ), Padding( @@ -153,7 +150,7 @@ class _SettingsWidgetState extends State 12, 0, 0, 0), child: Text( 'check.io', - style: FlutterFlowTheme.of(context) + style: AppTheme .headlineMedium .override( fontFamily: 'Outfit', @@ -167,7 +164,7 @@ class _SettingsWidgetState extends State Divider( height: 12, thickness: 2, - color: FlutterFlowTheme.of(context).alternate, + color: AppTheme.alternate, ), Expanded( child: Column( @@ -179,7 +176,7 @@ class _SettingsWidgetState extends State 16, 12, 0, 0), child: Text( 'Platform Navigation', - style: FlutterFlowTheme.of(context) + style: AppTheme .labelMedium .override( fontFamily: 'Readex Pro', @@ -196,7 +193,7 @@ class _SettingsWidgetState extends State width: double.infinity, height: 44, decoration: BoxDecoration( - color: FlutterFlowTheme.of(context) + color: AppTheme .primaryBackground, borderRadius: BorderRadius.circular(12), shape: BoxShape.rectangle, @@ -209,7 +206,7 @@ class _SettingsWidgetState extends State children: [ Icon( Icons.space_dashboard, - color: FlutterFlowTheme.of(context) + color: AppTheme .primaryText, size: 24, ), @@ -220,7 +217,7 @@ class _SettingsWidgetState extends State child: Text( 'Dashboard', style: - FlutterFlowTheme.of(context) + AppTheme .bodyMedium .override( fontFamily: @@ -243,7 +240,7 @@ class _SettingsWidgetState extends State width: double.infinity, height: 44, decoration: BoxDecoration( - color: FlutterFlowTheme.of(context) + color: AppTheme .primaryBackground, borderRadius: BorderRadius.circular(12), shape: BoxShape.rectangle, @@ -256,7 +253,7 @@ class _SettingsWidgetState extends State children: [ Icon( Icons.forum_rounded, - color: FlutterFlowTheme.of(context) + color: AppTheme .primaryText, size: 24, ), @@ -267,7 +264,7 @@ class _SettingsWidgetState extends State child: Text( 'Chats', style: - FlutterFlowTheme.of(context) + AppTheme .bodyMedium .override( fontFamily: @@ -290,7 +287,7 @@ class _SettingsWidgetState extends State width: double.infinity, height: 44, decoration: BoxDecoration( - color: FlutterFlowTheme.of(context) + color: AppTheme .primaryBackground, borderRadius: BorderRadius.circular(12), shape: BoxShape.rectangle, @@ -303,7 +300,7 @@ class _SettingsWidgetState extends State children: [ Icon( Icons.work, - color: FlutterFlowTheme.of(context) + color: AppTheme .primaryText, size: 24, ), @@ -314,7 +311,7 @@ class _SettingsWidgetState extends State child: Text( 'Projects', style: - FlutterFlowTheme.of(context) + AppTheme .bodyMedium .override( fontFamily: @@ -338,7 +335,7 @@ class _SettingsWidgetState extends State height: 44, decoration: BoxDecoration( color: - FlutterFlowTheme.of(context).accent1, + AppTheme.accent1, borderRadius: BorderRadius.circular(12), shape: BoxShape.rectangle, ), @@ -350,7 +347,7 @@ class _SettingsWidgetState extends State children: [ Icon( Icons.receipt_rounded, - color: FlutterFlowTheme.of(context) + color: AppTheme .primary, size: 24, ), @@ -361,7 +358,7 @@ class _SettingsWidgetState extends State child: Text( 'Recent Orders', style: - FlutterFlowTheme.of(context) + AppTheme .bodyMedium .override( fontFamily: @@ -375,7 +372,7 @@ class _SettingsWidgetState extends State height: 32, decoration: BoxDecoration( color: - FlutterFlowTheme.of(context) + AppTheme .primary, borderRadius: BorderRadius.circular(8), @@ -388,14 +385,14 @@ class _SettingsWidgetState extends State .fromSTEB(8, 4, 8, 4), child: Text( '12', - style: FlutterFlowTheme.of( + style: FlutterFlowAppTheme.of( context) .bodyMedium .override( fontFamily: 'Readex Pro', color: - FlutterFlowTheme.of( + FlutterFlowAppTheme.of( context) .info, letterSpacing: 0, @@ -414,7 +411,7 @@ class _SettingsWidgetState extends State 16, 0, 0, 0), child: Text( 'Settings', - style: FlutterFlowTheme.of(context) + style: AppTheme .labelMedium .override( fontFamily: 'Readex Pro', @@ -431,7 +428,7 @@ class _SettingsWidgetState extends State width: double.infinity, height: 44, decoration: BoxDecoration( - color: FlutterFlowTheme.of(context) + color: AppTheme .primaryBackground, borderRadius: BorderRadius.circular(12), shape: BoxShape.rectangle, @@ -444,7 +441,7 @@ class _SettingsWidgetState extends State children: [ Icon( Icons.attach_money_rounded, - color: FlutterFlowTheme.of(context) + color: AppTheme .primaryText, size: 24, ), @@ -455,7 +452,7 @@ class _SettingsWidgetState extends State child: Text( 'Billing', style: - FlutterFlowTheme.of(context) + AppTheme .bodyMedium .override( fontFamily: @@ -478,7 +475,7 @@ class _SettingsWidgetState extends State width: double.infinity, height: 44, decoration: BoxDecoration( - color: FlutterFlowTheme.of(context) + color: AppTheme .primaryBackground, borderRadius: BorderRadius.circular(12), shape: BoxShape.rectangle, @@ -491,7 +488,7 @@ class _SettingsWidgetState extends State children: [ Icon( Icons.wifi_tethering_rounded, - color: FlutterFlowTheme.of(context) + color: AppTheme .primaryText, size: 24, ), @@ -502,7 +499,7 @@ class _SettingsWidgetState extends State child: Text( 'Explore', style: - FlutterFlowTheme.of(context) + AppTheme .bodyMedium .override( fontFamily: @@ -528,12 +525,12 @@ class _SettingsWidgetState extends State width: 250, height: 50, decoration: BoxDecoration( - color: FlutterFlowTheme.of(context) + color: AppTheme .primaryBackground, borderRadius: BorderRadius.circular(12), border: Border.all( color: - FlutterFlowTheme.of(context).alternate, + AppTheme.alternate, width: 1, ), ), @@ -551,33 +548,33 @@ class _SettingsWidgetState extends State highlightColor: Colors.transparent, onTap: () async { setDarkModeSetting( - context, ThemeMode.light); + context, AppThemeMode.light); }, child: Container( width: 115, height: 100, decoration: BoxDecoration( - color: Theme.of(context) + color: AppTheme.of(context) .brightness == Brightness.light - ? FlutterFlowTheme.of(context) + ? AppTheme .secondaryBackground - : FlutterFlowTheme.of(context) + : AppTheme .primaryBackground, borderRadius: BorderRadius.circular(10), border: Border.all( color: valueOrDefault( - Theme.of(context) + AppTheme.of(context) .brightness == Brightness.light - ? FlutterFlowTheme.of( + ? FlutterFlowAppTheme.of( context) .alternate - : FlutterFlowTheme.of( + : FlutterFlowAppTheme.of( context) .primaryBackground, - FlutterFlowTheme.of(context) + AppTheme .alternate, ), width: 1, @@ -590,13 +587,13 @@ class _SettingsWidgetState extends State children: [ Icon( Icons.wb_sunny_rounded, - color: Theme.of(context) + color: AppTheme.of(context) .brightness == Brightness.light - ? FlutterFlowTheme.of( + ? FlutterFlowAppTheme.of( context) .primaryText - : FlutterFlowTheme.of( + : FlutterFlowAppTheme.of( context) .secondaryText, size: 16, @@ -607,20 +604,20 @@ class _SettingsWidgetState extends State child: Text( 'Light Mode', style: - FlutterFlowTheme.of( + FlutterFlowAppTheme.of( context) .bodyMedium .override( fontFamily: 'Readex Pro', - color: Theme.of(context) + color: AppTheme.of(context) .brightness == Brightness .light - ? FlutterFlowTheme.of( + ? FlutterFlowAppTheme.of( context) .primaryText - : FlutterFlowTheme.of( + : FlutterFlowAppTheme.of( context) .secondaryText, letterSpacing: 0, @@ -640,33 +637,33 @@ class _SettingsWidgetState extends State highlightColor: Colors.transparent, onTap: () async { setDarkModeSetting( - context, ThemeMode.dark); + context, AppThemeMode.dark); }, child: Container( width: 115, height: 100, decoration: BoxDecoration( - color: Theme.of(context) + color: AppTheme.of(context) .brightness == Brightness.dark - ? FlutterFlowTheme.of(context) + ? AppTheme .secondaryBackground - : FlutterFlowTheme.of(context) + : AppTheme .primaryBackground, borderRadius: BorderRadius.circular(10), border: Border.all( color: valueOrDefault( - Theme.of(context) + AppTheme.of(context) .brightness == Brightness.dark - ? FlutterFlowTheme.of( + ? FlutterFlowAppTheme.of( context) .alternate - : FlutterFlowTheme.of( + : FlutterFlowAppTheme.of( context) .primaryBackground, - FlutterFlowTheme.of(context) + AppTheme .primaryBackground, ), width: 1, @@ -679,13 +676,13 @@ class _SettingsWidgetState extends State children: [ Icon( Icons.nightlight_round, - color: Theme.of(context) + color: AppTheme.of(context) .brightness == Brightness.dark - ? FlutterFlowTheme.of( + ? FlutterFlowAppTheme.of( context) .primaryText - : FlutterFlowTheme.of( + : FlutterFlowAppTheme.of( context) .secondaryText, size: 16, @@ -696,20 +693,20 @@ class _SettingsWidgetState extends State child: Text( 'Dark Mode', style: - FlutterFlowTheme.of( + FlutterFlowAppTheme.of( context) .bodyMedium .override( fontFamily: 'Readex Pro', - color: Theme.of(context) + color: AppTheme.of(context) .brightness == Brightness .dark - ? FlutterFlowTheme.of( + ? FlutterFlowAppTheme.of( context) .primaryText - : FlutterFlowTheme.of( + : FlutterFlowAppTheme.of( context) .secondaryText, letterSpacing: 0, @@ -730,7 +727,7 @@ class _SettingsWidgetState extends State Divider( height: 12, thickness: 2, - color: FlutterFlowTheme.of(context).alternate, + color: AppTheme.alternate, ), Padding( padding: @@ -742,11 +739,11 @@ class _SettingsWidgetState extends State width: 50, height: 50, decoration: BoxDecoration( - color: FlutterFlowTheme.of(context).accent1, + color: AppTheme.accent1, borderRadius: BorderRadius.circular(12), border: Border.all( color: - FlutterFlowTheme.of(context).primary, + AppTheme.primary, width: 2, ), ), @@ -779,7 +776,7 @@ class _SettingsWidgetState extends State children: [ Text( 'Casper Ghost', - style: FlutterFlowTheme.of(context) + style: AppTheme .bodyLarge .override( fontFamily: 'Readex Pro', @@ -788,7 +785,7 @@ class _SettingsWidgetState extends State ), Text( 'admin@gmail.com', - style: FlutterFlowTheme.of(context) + style: AppTheme .labelMedium .override( fontFamily: 'Readex Pro', @@ -811,7 +808,7 @@ class _SettingsWidgetState extends State children: [ Icon( Icons.menu_open_rounded, - color: FlutterFlowTheme.of(context) + color: AppTheme .secondaryText, size: 24, ), @@ -860,11 +857,11 @@ class _SettingsWidgetState extends State maxWidth: 390, ), decoration: BoxDecoration( - color: FlutterFlowTheme.of(context) + color: AppTheme .secondaryBackground, borderRadius: BorderRadius.circular(12), border: Border.all( - color: FlutterFlowTheme.of(context) + color: AppTheme .alternate, width: 1, ), @@ -881,13 +878,13 @@ class _SettingsWidgetState extends State width: 72, height: 72, decoration: BoxDecoration( - color: FlutterFlowTheme.of( + color: FlutterFlowAppTheme.of( context) .accent1, borderRadius: BorderRadius.circular(12), border: Border.all( - color: FlutterFlowTheme.of( + color: FlutterFlowAppTheme.of( context) .primary, width: 2, @@ -926,7 +923,7 @@ class _SettingsWidgetState extends State children: [ Text( 'Casper Ghost', - style: FlutterFlowTheme + style: FlutterFlowAppTheme .of(context) .headlineSmall .override( @@ -942,7 +939,7 @@ class _SettingsWidgetState extends State 0, 4, 0, 0), child: Text( 'casper@ghustbusters.com', - style: FlutterFlowTheme + style: FlutterFlowAppTheme .of(context) .labelMedium .override( @@ -963,7 +960,7 @@ class _SettingsWidgetState extends State Divider( height: 2, thickness: 1, - color: FlutterFlowTheme.of(context) + color: AppTheme .alternate, ), Row( @@ -976,7 +973,7 @@ class _SettingsWidgetState extends State child: Container( width: 200, decoration: BoxDecoration( - color: FlutterFlowTheme.of( + color: FlutterFlowAppTheme.of( context) .secondaryBackground, borderRadius: @@ -997,7 +994,7 @@ class _SettingsWidgetState extends State children: [ Icon( Icons.receipt_rounded, - color: FlutterFlowTheme + color: FlutterFlowAppTheme .of(context) .primary, size: 44, @@ -1011,7 +1008,7 @@ class _SettingsWidgetState extends State '2,200', textAlign: TextAlign .center, - style: FlutterFlowTheme + style: FlutterFlowAppTheme .of(context) .headlineSmall .override( @@ -1026,7 +1023,7 @@ class _SettingsWidgetState extends State 'Orders Placed', textAlign: TextAlign.center, - style: FlutterFlowTheme + style: FlutterFlowAppTheme .of(context) .labelMedium .override( @@ -1050,7 +1047,7 @@ class _SettingsWidgetState extends State child: Container( width: 200, decoration: BoxDecoration( - color: FlutterFlowTheme.of( + color: FlutterFlowAppTheme.of( context) .secondaryBackground, borderRadius: @@ -1072,7 +1069,7 @@ class _SettingsWidgetState extends State Icon( Icons .ssid_chart_rounded, - color: FlutterFlowTheme + color: FlutterFlowAppTheme .of(context) .primary, size: 44, @@ -1086,7 +1083,7 @@ class _SettingsWidgetState extends State '\$212.4k', textAlign: TextAlign .center, - style: FlutterFlowTheme + style: FlutterFlowAppTheme .of(context) .headlineSmall .override( @@ -1101,7 +1098,7 @@ class _SettingsWidgetState extends State 'Money Earned', textAlign: TextAlign.center, - style: FlutterFlowTheme + style: FlutterFlowAppTheme .of(context) .labelMedium .override( @@ -1137,13 +1134,13 @@ class _SettingsWidgetState extends State Container( width: double.infinity, decoration: BoxDecoration( - color: FlutterFlowTheme.of(context) + color: AppTheme .primaryBackground, borderRadius: BorderRadius.circular(12), border: Border.all( color: - FlutterFlowTheme.of(context) + AppTheme .alternate, ), ), @@ -1154,7 +1151,7 @@ class _SettingsWidgetState extends State child: Text( 'My Account Information', style: - FlutterFlowTheme.of(context) + AppTheme .labelMedium .override( fontFamily: @@ -1173,12 +1170,12 @@ class _SettingsWidgetState extends State height: 60, decoration: BoxDecoration( color: - FlutterFlowTheme.of(context) + AppTheme .secondaryBackground, boxShadow: [ BoxShadow( blurRadius: 0, - color: FlutterFlowTheme.of( + color: FlutterFlowAppTheme.of( context) .alternate, offset: const Offset( @@ -1203,7 +1200,7 @@ class _SettingsWidgetState extends State children: [ Text( 'Change Password', - style: FlutterFlowTheme + style: FlutterFlowAppTheme .of(context) .bodyLarge .override( @@ -1220,7 +1217,7 @@ class _SettingsWidgetState extends State child: Icon( Icons .chevron_right_rounded, - color: FlutterFlowTheme + color: FlutterFlowAppTheme .of(context) .secondaryText, size: 20, @@ -1243,12 +1240,12 @@ class _SettingsWidgetState extends State height: 60, decoration: BoxDecoration( color: - FlutterFlowTheme.of(context) + AppTheme .secondaryBackground, boxShadow: [ BoxShadow( blurRadius: 0, - color: FlutterFlowTheme.of( + color: FlutterFlowAppTheme.of( context) .alternate, offset: const Offset( @@ -1274,13 +1271,13 @@ class _SettingsWidgetState extends State Text( 'Edit Profile', style: - FlutterFlowTheme.of( + FlutterFlowAppTheme.of( context) .bodyLarge .override( fontFamily: 'Readex Pro', - color: FlutterFlowTheme.of( + color: FlutterFlowAppTheme.of( context) .primaryText, letterSpacing: @@ -1295,7 +1292,7 @@ class _SettingsWidgetState extends State child: Icon( Icons .chevron_right_rounded, - color: FlutterFlowTheme + color: FlutterFlowAppTheme .of(context) .secondaryText, size: 20, @@ -1317,13 +1314,13 @@ class _SettingsWidgetState extends State width: double.infinity, decoration: BoxDecoration( color: - FlutterFlowTheme.of(context) + AppTheme .primaryBackground, borderRadius: BorderRadius.circular(12), border: Border.all( color: - FlutterFlowTheme.of(context) + AppTheme .alternate, ), ), @@ -1333,7 +1330,7 @@ class _SettingsWidgetState extends State child: Text( 'Support', style: - FlutterFlowTheme.of(context) + AppTheme .labelMedium .override( fontFamily: @@ -1374,12 +1371,12 @@ class _SettingsWidgetState extends State height: 60, decoration: BoxDecoration( color: - FlutterFlowTheme.of(context) + AppTheme .secondaryBackground, boxShadow: [ BoxShadow( blurRadius: 0, - color: FlutterFlowTheme.of( + color: FlutterFlowAppTheme.of( context) .alternate, offset: const Offset( @@ -1405,13 +1402,13 @@ class _SettingsWidgetState extends State children: [ Text( 'Bug or Feature Request', - style: FlutterFlowTheme + style: FlutterFlowAppTheme .of(context) .bodyLarge .override( fontFamily: 'Readex Pro', - color: FlutterFlowTheme.of( + color: FlutterFlowAppTheme.of( context) .primaryText, letterSpacing: @@ -1426,7 +1423,7 @@ class _SettingsWidgetState extends State child: Icon( Icons .chevron_right_rounded, - color: FlutterFlowTheme + color: FlutterFlowAppTheme .of(context) .secondaryText, size: 20, @@ -1474,12 +1471,12 @@ class _SettingsWidgetState extends State height: 60, decoration: BoxDecoration( color: - FlutterFlowTheme.of(context) + AppTheme .secondaryBackground, boxShadow: [ BoxShadow( blurRadius: 0, - color: FlutterFlowTheme.of( + color: FlutterFlowAppTheme.of( context) .alternate, offset: const Offset( @@ -1505,7 +1502,7 @@ class _SettingsWidgetState extends State children: [ Text( 'Open-source licenses & other info', - style: FlutterFlowTheme + style: FlutterFlowAppTheme .of(context) .bodyLarge .override( @@ -1523,7 +1520,7 @@ class _SettingsWidgetState extends State child: Icon( Icons .chevron_right_rounded, - color: FlutterFlowTheme + color: FlutterFlowAppTheme .of(context) .secondaryText, size: 20, @@ -1551,12 +1548,12 @@ class _SettingsWidgetState extends State ), decoration: BoxDecoration( color: - FlutterFlowTheme.of(context) + AppTheme .primaryBackground, borderRadius: BorderRadius.circular(12), border: Border.all( - color: FlutterFlowTheme.of( + color: FlutterFlowAppTheme.of( context) .alternate, width: 1, @@ -1583,21 +1580,21 @@ class _SettingsWidgetState extends State onTap: () async { setDarkModeSetting( context, - ThemeMode.light); + AppThemeMode.light); }, child: Container( width: 115, height: 100, decoration: BoxDecoration( - color: Theme.of(context) + color: AppTheme.of(context) .brightness == Brightness .light - ? FlutterFlowTheme.of( + ? FlutterFlowAppTheme.of( context) .secondaryBackground - : FlutterFlowTheme.of( + : FlutterFlowAppTheme.of( context) .primaryBackground, borderRadius: @@ -1608,17 +1605,17 @@ class _SettingsWidgetState extends State color: valueOrDefault< Color>( - Theme.of(context) + AppTheme.of(context) .brightness == Brightness .light - ? FlutterFlowTheme.of( + ? FlutterFlowAppTheme.of( context) .alternate - : FlutterFlowTheme.of( + : FlutterFlowAppTheme.of( context) .primaryBackground, - FlutterFlowTheme.of( + FlutterFlowAppTheme.of( context) .alternate, ), @@ -1636,14 +1633,14 @@ class _SettingsWidgetState extends State Icon( Icons .wb_sunny_rounded, - color: Theme.of(context) + color: AppTheme.of(context) .brightness == Brightness .light - ? FlutterFlowTheme.of( + ? FlutterFlowAppTheme.of( context) .primaryText - : FlutterFlowTheme.of( + : FlutterFlowAppTheme.of( context) .secondaryText, size: 16, @@ -1658,16 +1655,16 @@ class _SettingsWidgetState extends State 0), child: Text( 'Light ', - style: FlutterFlowTheme.of( + style: FlutterFlowAppTheme.of( context) .bodyMedium .override( fontFamily: 'Readex Pro', - color: Theme.of(context).brightness == + color: AppTheme.of(context).brightness == Brightness.light - ? FlutterFlowTheme.of(context).primaryText - : FlutterFlowTheme.of(context).secondaryText, + ? AppTheme.primaryText + : AppTheme.secondaryText, letterSpacing: 0, ), @@ -1691,21 +1688,21 @@ class _SettingsWidgetState extends State onTap: () async { setDarkModeSetting( context, - ThemeMode.dark); + AppThemeMode.dark); }, child: Container( width: 115, height: 100, decoration: BoxDecoration( - color: Theme.of(context) + color: AppTheme.of(context) .brightness == Brightness .dark - ? FlutterFlowTheme.of( + ? FlutterFlowAppTheme.of( context) .secondaryBackground - : FlutterFlowTheme.of( + : FlutterFlowAppTheme.of( context) .primaryBackground, borderRadius: @@ -1713,14 +1710,14 @@ class _SettingsWidgetState extends State .circular( 10), border: Border.all( - color: Theme.of(context) + color: AppTheme.of(context) .brightness == Brightness .dark - ? FlutterFlowTheme.of( + ? FlutterFlowAppTheme.of( context) .alternate - : FlutterFlowTheme.of( + : FlutterFlowAppTheme.of( context) .primaryBackground, width: 1, @@ -1737,14 +1734,14 @@ class _SettingsWidgetState extends State Icon( Icons .nightlight_round, - color: Theme.of(context) + color: AppTheme.of(context) .brightness == Brightness .dark - ? FlutterFlowTheme.of( + ? FlutterFlowAppTheme.of( context) .primaryText - : FlutterFlowTheme.of( + : FlutterFlowAppTheme.of( context) .secondaryText, size: 16, @@ -1759,16 +1756,16 @@ class _SettingsWidgetState extends State 0), child: Text( 'Dark', - style: FlutterFlowTheme.of( + style: FlutterFlowAppTheme.of( context) .bodyMedium .override( fontFamily: 'Readex Pro', - color: Theme.of(context).brightness == + color: AppTheme.of(context).brightness == Brightness.dark - ? FlutterFlowTheme.of(context).primaryText - : FlutterFlowTheme.of(context).secondaryText, + ? AppTheme.primaryText + : AppTheme.secondaryText, letterSpacing: 0, ), diff --git a/mobile/app/lib/pages/setup_model.dart b/mobile/app/lib/pages/setup_model.dart index 5ce1f8c..8df6f41 100644 --- a/mobile/app/lib/pages/setup_model.dart +++ b/mobile/app/lib/pages/setup_model.dart @@ -1,7 +1,4 @@ -import '/flutter_flow/flutter_flow_animations.dart'; -import '/flutter_flow/flutter_flow_theme.dart'; -import '/flutter_flow/flutter_flow_util.dart'; -import '/flutter_flow/flutter_flow_widgets.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'setup_widget.dart' show SetupWidget; import 'package:flutter/material.dart'; import 'package:flutter_animate/flutter_animate.dart'; diff --git a/mobile/app/lib/pages/setup_widget.dart b/mobile/app/lib/pages/setup_widget.dart index ae4feed..8483283 100644 --- a/mobile/app/lib/pages/setup_widget.dart +++ b/mobile/app/lib/pages/setup_widget.dart @@ -1,7 +1,4 @@ -import '/flutter_flow/flutter_flow_animations.dart'; -import '/flutter_flow/flutter_flow_theme.dart'; -import '/flutter_flow/flutter_flow_util.dart'; -import '/flutter_flow/flutter_flow_widgets.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:google_fonts/google_fonts.dart'; @@ -149,7 +146,7 @@ class _SetupWidgetState extends State : FocusScope.of(context).unfocus(), child: Scaffold( key: scaffoldKey, - backgroundColor: FlutterFlowTheme.of(context).secondaryBackground, + backgroundColor: AppTheme.secondaryBackground, body: Column( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -161,9 +158,9 @@ class _SetupWidgetState extends State decoration: BoxDecoration( gradient: LinearGradient( colors: [ - FlutterFlowTheme.of(context).primary, - FlutterFlowTheme.of(context).error, - FlutterFlowTheme.of(context).tertiary + AppTheme.primary, + AppTheme.error, + AppTheme.tertiary ], stops: const [0, 0.5, 1], begin: const AlignmentDirectional(-1, -1), @@ -177,7 +174,7 @@ class _SetupWidgetState extends State gradient: LinearGradient( colors: [ const Color(0x00FFFFFF), - FlutterFlowTheme.of(context).secondaryBackground + AppTheme.secondaryBackground ], stops: const [0, 1], begin: const AlignmentDirectional(0, -1), @@ -192,7 +189,7 @@ class _SetupWidgetState extends State width: 120, height: 120, decoration: BoxDecoration( - color: FlutterFlowTheme.of(context).accent4, + color: AppTheme.accent4, shape: BoxShape.circle, ), child: Padding( @@ -210,7 +207,7 @@ class _SetupWidgetState extends State padding: const EdgeInsetsDirectional.fromSTEB(0, 44, 0, 0), child: Text( 'Welcome!', - style: FlutterFlowTheme.of(context) + style: AppTheme .displaySmall .override( fontFamily: 'Outfit', @@ -225,7 +222,7 @@ class _SetupWidgetState extends State 'Thanks for joining! Access or create your account below, and get started on your journey!', textAlign: TextAlign.center, style: - FlutterFlowTheme.of(context).labelMedium.override( + AppTheme.labelMedium.override( fontFamily: 'Readex Pro', letterSpacing: 0, ), @@ -259,16 +256,16 @@ class _SetupWidgetState extends State padding: const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), iconPadding: const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), - color: FlutterFlowTheme.of(context) + color: AppTheme .secondaryBackground, textStyle: - FlutterFlowTheme.of(context).bodyLarge.override( + AppTheme.bodyLarge.override( fontFamily: 'Readex Pro', letterSpacing: 0, ), elevation: 0, borderSide: BorderSide( - color: FlutterFlowTheme.of(context).alternate, + color: AppTheme.alternate, width: 2, ), borderRadius: BorderRadius.circular(12), @@ -293,8 +290,8 @@ class _SetupWidgetState extends State padding: const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), iconPadding: const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), - color: FlutterFlowTheme.of(context).primary, - textStyle: FlutterFlowTheme.of(context) + color: AppTheme.primary, + textStyle: AppTheme .titleSmall .override( fontFamily: 'Readex Pro', diff --git a/mobile/app/lib/pages/support_form_model.dart b/mobile/app/lib/pages/support_form_model.dart index 547e54b..39ee919 100644 --- a/mobile/app/lib/pages/support_form_model.dart +++ b/mobile/app/lib/pages/support_form_model.dart @@ -1,8 +1,4 @@ -import '/flutter_flow/flutter_flow_animations.dart'; -import '/flutter_flow/flutter_flow_icon_button.dart'; -import '/flutter_flow/flutter_flow_theme.dart'; -import '/flutter_flow/flutter_flow_util.dart'; -import '/flutter_flow/flutter_flow_widgets.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'support_form_widget.dart' show SupportFormWidget; import 'package:flutter/material.dart'; import 'package:flutter_animate/flutter_animate.dart'; diff --git a/mobile/app/lib/pages/support_form_widget.dart b/mobile/app/lib/pages/support_form_widget.dart index 960d4fe..402285e 100644 --- a/mobile/app/lib/pages/support_form_widget.dart +++ b/mobile/app/lib/pages/support_form_widget.dart @@ -1,8 +1,4 @@ -import '/flutter_flow/flutter_flow_animations.dart'; -import '/flutter_flow/flutter_flow_icon_button.dart'; -import '/flutter_flow/flutter_flow_theme.dart'; -import '/flutter_flow/flutter_flow_util.dart'; -import '/flutter_flow/flutter_flow_widgets.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:google_fonts/google_fonts.dart'; @@ -11,6 +7,8 @@ import 'package:provider/provider.dart'; import 'support_form_model.dart'; export 'support_form_model.dart'; +import 'package:app/theme.dart'; + class SupportFormWidget extends StatefulWidget { const SupportFormWidget({super.key}); @@ -122,30 +120,27 @@ class _SupportFormWidgetState extends State : FocusScope.of(context).unfocus(), child: Scaffold( key: scaffoldKey, - backgroundColor: FlutterFlowTheme.of(context).secondaryBackground, + backgroundColor: AppTheme.secondaryBackground, appBar: AppBar( - backgroundColor: FlutterFlowTheme.of(context).secondaryBackground, + backgroundColor: AppTheme.secondaryBackground, automaticallyImplyLeading: false, leading: FlutterFlowIconButton( borderColor: Colors.transparent, borderRadius: 30, borderWidth: 1, buttonSize: 60, - icon: Icon( + icon: const Icon( Icons.arrow_back_rounded, - color: FlutterFlowTheme.of(context).primaryText, + color: AppTheme.primaryText, size: 30, ), onPressed: () async { context.pop(); }, ), - title: Text( + title: const Text( 'Submit Ticket', - style: FlutterFlowTheme.of(context).titleLarge.override( - fontFamily: 'Outfit', - letterSpacing: 0, - ), + style: AppTheme.titleLarge, ), actions: const [], centerTitle: false, @@ -162,23 +157,15 @@ class _SupportFormWidgetState extends State mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( + const Text( 'Welcome to support', - style: FlutterFlowTheme.of(context).labelLarge.override( - fontFamily: 'Readex Pro', - letterSpacing: 0, - ), + style: AppTheme.labelLarge, ), - Padding( - padding: const EdgeInsetsDirectional.fromSTEB(0, 4, 0, 0), + const Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 4, 0, 0), child: Text( 'Submit a bug', - style: FlutterFlowTheme.of(context) - .headlineMedium - .override( - fontFamily: 'Outfit', - letterSpacing: 0, - ), + style: AppTheme.headlineMedium, ), ), Row( @@ -194,16 +181,16 @@ class _SupportFormWidgetState extends State maxWidth: 500, ), decoration: BoxDecoration( - color: FlutterFlowTheme.of(context) + color: AppTheme .secondaryBackground, borderRadius: BorderRadius.circular(12), border: Border.all( - color: FlutterFlowTheme.of(context).alternate, + color: AppTheme.alternate, width: 2, ), ), - child: Padding( - padding: const EdgeInsetsDirectional.fromSTEB( + child: const Padding( + padding: EdgeInsetsDirectional.fromSTEB( 8, 16, 8, 16), child: Column( mainAxisSize: MainAxisSize.max, @@ -213,21 +200,16 @@ class _SupportFormWidgetState extends State Icon( Icons.email_outlined, color: - FlutterFlowTheme.of(context).primary, + AppTheme.primary, size: 36, ), Padding( - padding: const EdgeInsetsDirectional.fromSTEB( + padding: EdgeInsetsDirectional.fromSTEB( 0, 12, 0, 0), child: Text( 'Email Us', textAlign: TextAlign.center, - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - fontFamily: 'Readex Pro', - letterSpacing: 0, - ), + style: AppTheme.bodyMedium, ), ), ], @@ -247,16 +229,16 @@ class _SupportFormWidgetState extends State maxWidth: 500, ), decoration: BoxDecoration( - color: FlutterFlowTheme.of(context) + color: AppTheme .secondaryBackground, borderRadius: BorderRadius.circular(12), border: Border.all( - color: FlutterFlowTheme.of(context).alternate, + color: AppTheme.alternate, width: 2, ), ), - child: Padding( - padding: const EdgeInsetsDirectional.fromSTEB( + child: const Padding( + padding: EdgeInsetsDirectional.fromSTEB( 8, 16, 8, 16), child: Column( mainAxisSize: MainAxisSize.max, @@ -266,21 +248,16 @@ class _SupportFormWidgetState extends State Icon( Icons.search_rounded, color: - FlutterFlowTheme.of(context).primary, + AppTheme.primary, size: 36, ), Padding( - padding: const EdgeInsetsDirectional.fromSTEB( + padding: EdgeInsetsDirectional.fromSTEB( 0, 12, 0, 0), child: Text( 'Search FAQs', textAlign: TextAlign.center, - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - fontFamily: 'Readex Pro', - letterSpacing: 0, - ), + style: AppTheme.bodyMedium, ), ), ], @@ -304,42 +281,32 @@ class _SupportFormWidgetState extends State obscureText: false, decoration: InputDecoration( labelText: 'Channel Name', - labelStyle: FlutterFlowTheme.of(context) - .labelMedium - .override( - fontFamily: 'Readex Pro', - letterSpacing: 0, - ), - hintStyle: FlutterFlowTheme.of(context) - .labelMedium - .override( - fontFamily: 'Readex Pro', - letterSpacing: 0, - ), + labelStyle: AppTheme.labelMedium, + hintStyle: AppTheme.labelMedium, enabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: FlutterFlowTheme.of(context).alternate, + borderSide: const BorderSide( + color: AppTheme.alternate, width: 2, ), borderRadius: BorderRadius.circular(12), ), focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: FlutterFlowTheme.of(context).primary, + borderSide: const BorderSide( + color: AppTheme.primary, width: 2, ), borderRadius: BorderRadius.circular(12), ), errorBorder: OutlineInputBorder( - borderSide: BorderSide( - color: FlutterFlowTheme.of(context).error, + borderSide: const BorderSide( + color: AppTheme.error, width: 2, ), borderRadius: BorderRadius.circular(12), ), focusedErrorBorder: OutlineInputBorder( - borderSide: BorderSide( - color: FlutterFlowTheme.of(context).error, + borderSide: const BorderSide( + color: AppTheme.error, width: 2, ), borderRadius: BorderRadius.circular(12), @@ -347,13 +314,8 @@ class _SupportFormWidgetState extends State contentPadding: const EdgeInsetsDirectional.fromSTEB( 16, 12, 16, 12), ), - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - fontFamily: 'Readex Pro', - letterSpacing: 0, - ), - cursorColor: FlutterFlowTheme.of(context).primary, + style: AppTheme.bodyMedium, + cursorColor: AppTheme.primary, validator: _model.textController1Validator .asValidator(context), ), @@ -364,42 +326,32 @@ class _SupportFormWidgetState extends State obscureText: false, decoration: InputDecoration( labelText: 'Channel ID', - labelStyle: FlutterFlowTheme.of(context) - .labelMedium - .override( - fontFamily: 'Readex Pro', - letterSpacing: 0, - ), - hintStyle: FlutterFlowTheme.of(context) - .labelMedium - .override( - fontFamily: 'Readex Pro', - letterSpacing: 0, - ), + labelStyle: AppTheme.labelMedium, + hintStyle: AppTheme.labelMedium, enabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: FlutterFlowTheme.of(context).alternate, + borderSide: const BorderSide( + color: AppTheme.alternate, width: 2, ), borderRadius: BorderRadius.circular(12), ), focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: FlutterFlowTheme.of(context).primary, + borderSide: const BorderSide( + color: AppTheme.primary, width: 2, ), borderRadius: BorderRadius.circular(12), ), errorBorder: OutlineInputBorder( - borderSide: BorderSide( - color: FlutterFlowTheme.of(context).error, + borderSide: const BorderSide( + color: AppTheme.error, width: 2, ), borderRadius: BorderRadius.circular(12), ), focusedErrorBorder: OutlineInputBorder( - borderSide: BorderSide( - color: FlutterFlowTheme.of(context).error, + borderSide: const BorderSide( + color: AppTheme.error, width: 2, ), borderRadius: BorderRadius.circular(12), @@ -407,13 +359,8 @@ class _SupportFormWidgetState extends State contentPadding: const EdgeInsetsDirectional.fromSTEB( 16, 12, 16, 12), ), - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - fontFamily: 'Readex Pro', - letterSpacing: 0, - ), - cursorColor: FlutterFlowTheme.of(context).primary, + style: AppTheme.bodyMedium, + cursorColor: AppTheme.primary, validator: _model.textController2Validator .asValidator(context), ), @@ -423,44 +370,34 @@ class _SupportFormWidgetState extends State autofocus: true, obscureText: false, decoration: InputDecoration( - labelStyle: FlutterFlowTheme.of(context) - .labelMedium - .override( - fontFamily: 'Readex Pro', - letterSpacing: 0, - ), + labelStyle: AppTheme.labelMedium, hintText: 'Short Description of what is going on...', - hintStyle: FlutterFlowTheme.of(context) - .labelMedium - .override( - fontFamily: 'Readex Pro', - letterSpacing: 0, - ), + hintStyle: AppTheme.labelMedium, enabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: FlutterFlowTheme.of(context).alternate, + borderSide: const BorderSide( + color: AppTheme.alternate, width: 2, ), borderRadius: BorderRadius.circular(12), ), focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: FlutterFlowTheme.of(context).primary, + borderSide: const BorderSide( + color: AppTheme.primary, width: 2, ), borderRadius: BorderRadius.circular(12), ), errorBorder: OutlineInputBorder( - borderSide: BorderSide( - color: FlutterFlowTheme.of(context).error, + borderSide: const BorderSide( + color: AppTheme.error, width: 2, ), borderRadius: BorderRadius.circular(12), ), focusedErrorBorder: OutlineInputBorder( - borderSide: BorderSide( - color: FlutterFlowTheme.of(context).error, + borderSide: const BorderSide( + color: AppTheme.error, width: 2, ), borderRadius: BorderRadius.circular(12), @@ -468,15 +405,10 @@ class _SupportFormWidgetState extends State contentPadding: const EdgeInsetsDirectional.fromSTEB( 16, 24, 16, 12), ), - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - fontFamily: 'Readex Pro', - letterSpacing: 0, - ), + style: AppTheme.bodyMedium, maxLines: 16, minLines: 6, - cursorColor: FlutterFlowTheme.of(context).primary, + cursorColor: AppTheme.primary, validator: _model.textController3Validator .asValidator(context), ), @@ -492,35 +424,30 @@ class _SupportFormWidgetState extends State ), decoration: BoxDecoration( color: - FlutterFlowTheme.of(context).secondaryBackground, + AppTheme.secondaryBackground, borderRadius: BorderRadius.circular(12), border: Border.all( - color: FlutterFlowTheme.of(context).alternate, + color: AppTheme.alternate, width: 2, ), ), - child: Padding( - padding: const EdgeInsets.all(8), + child: const Padding( + padding: EdgeInsets.all(8), child: Row( mainAxisSize: MainAxisSize.max, children: [ Icon( Icons.add_a_photo_rounded, - color: FlutterFlowTheme.of(context).primary, + color: AppTheme.primary, size: 32, ), Padding( padding: - const EdgeInsetsDirectional.fromSTEB(16, 0, 0, 0), + EdgeInsetsDirectional.fromSTEB(16, 0, 0, 0), child: Text( 'Upload Screenshot', textAlign: TextAlign.center, - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - fontFamily: 'Readex Pro', - letterSpacing: 0, - ), + style: AppTheme.bodyMedium, ), ), ], @@ -546,13 +473,8 @@ class _SupportFormWidgetState extends State padding: const EdgeInsets.all(0), iconPadding: const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), - color: FlutterFlowTheme.of(context).primary, - textStyle: - FlutterFlowTheme.of(context).titleSmall.override( - fontFamily: 'Readex Pro', - color: Colors.white, - letterSpacing: 0, - ), + color: AppTheme.primary, + textStyle: AppTheme.titleSmall, elevation: 4, borderSide: const BorderSide( color: Colors.transparent, diff --git a/mobile/app/lib/theme.dart b/mobile/app/lib/theme.dart new file mode 100644 index 0000000..42fbdec --- /dev/null +++ b/mobile/app/lib/theme.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; + +class AppTheme { + static const primary = Colors.deepPurple; + static const secondary = Colors.deepPurpleAccent; + static const tertiary = Colors.white; + static const alternate = Colors.purple; + + static const primaryBackground = Colors.purple; + static const secondaryBackground = Colors.purpleAccent; + + static const primaryText = Colors.white; + static const secondaryText = Colors.white; + static const bodyMedium = TextStyle(color: Colors.white, fontSize: 12); + + static const error = Colors.red; + + static const labelMedium = TextStyle(color: Colors.grey, fontSize: 16); + static const labelLarge = TextStyle(color: Colors.black, fontSize: 24); + + static const headlineMedium = TextStyle(color: Colors.blue, fontWeight: FontWeight.bold); + + static const titleLarge = TextStyle(fontFamily: 'Outfit', letterSpacing: 0, fontSize: 24); + static const titleSmall = TextStyle(fontFamily: 'Outfit', letterSpacing: 0, fontSize: 16); + +} \ No newline at end of file diff --git a/mobile/app/linux/flutter/generated_plugin_registrant.cc b/mobile/app/linux/flutter/generated_plugin_registrant.cc index e71a16d..f6f23bf 100644 --- a/mobile/app/linux/flutter/generated_plugin_registrant.cc +++ b/mobile/app/linux/flutter/generated_plugin_registrant.cc @@ -6,6 +6,10 @@ #include "generated_plugin_registrant.h" +#include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); + url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); } diff --git a/mobile/app/linux/flutter/generated_plugins.cmake b/mobile/app/linux/flutter/generated_plugins.cmake index 2e1de87..f16b4c3 100644 --- a/mobile/app/linux/flutter/generated_plugins.cmake +++ b/mobile/app/linux/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + url_launcher_linux ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/mobile/app/macos/Flutter/GeneratedPluginRegistrant.swift b/mobile/app/macos/Flutter/GeneratedPluginRegistrant.swift index 2bfe7e4..40a1f70 100644 --- a/mobile/app/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/mobile/app/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,9 +6,15 @@ import FlutterMacOS import Foundation import path_provider_foundation +import rive_common +import shared_preferences_foundation import sqflite +import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) + RivePlugin.register(with: registry.registrar(forPlugin: "RivePlugin")) + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) + UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) } diff --git a/mobile/app/pubspec.lock b/mobile/app/pubspec.lock index 3f7529b..664298d 100644 --- a/mobile/app/pubspec.lock +++ b/mobile/app/pubspec.lock @@ -1,6 +1,30 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + aligned_dialog: + dependency: transitive + description: + name: aligned_dialog + sha256: "5d227d0e9d96bfa06699656b05f2cfba2f45645ccc52c43272e33ae307b26be8" + url: "https://pub.dev" + source: hosted + version: "0.0.7" + aligned_tooltip: + dependency: transitive + description: + name: aligned_tooltip + sha256: "1f8d69cc560e693905f8d9e2e7b85312c854c91a447b818bac02a8d0c73adf72" + url: "https://pub.dev" + source: hosted + version: "0.0.1" + apivideo_live_stream: + dependency: transitive + description: + name: apivideo_live_stream + sha256: "87f4d8791e25d9c1db063d21003e7d0c4cf9e7be492ac1ecd79bfe22541a319b" + url: "https://pub.dev" + source: hosted + version: "1.0.7" async: dependency: transitive description: @@ -17,6 +41,22 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0" + barcode: + dependency: transitive + description: + name: barcode + sha256: ab180ce22c6555d77d45f0178a523669db67f95856e3378259ef2ffeb43e6003 + url: "https://pub.dev" + source: hosted + version: "2.2.8" + barcode_widget: + dependency: transitive + description: + name: barcode_widget + sha256: "6f2c5b08659b1a5f4d88d183e6007133ea2f96e50e7b8bb628f03266c3931427" + url: "https://pub.dev" + source: hosted + version: "2.0.4" boolean_selector: dependency: transitive description: @@ -49,6 +89,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + carousel_slider: + dependency: transitive + description: + name: carousel_slider + sha256: "9c695cc963bf1d04a47bd6021f68befce8970bcd61d24938e1fb0918cf5d9c42" + url: "https://pub.dev" + source: hosted + version: "4.2.1" characters: dependency: transitive description: @@ -73,6 +121,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.18.0" + color: + dependency: transitive + description: + name: color + sha256: ddcdf1b3badd7008233f5acffaf20ca9f5dc2cd0172b75f68f24526a5f5725cb + url: "https://pub.dev" + source: hosted + version: "3.0.0" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: "55d7b444feb71301ef6b8838dbc1ae02e63dd48c8773f3810ff53bb1e2945b32" + url: "https://pub.dev" + source: hosted + version: "0.3.4+1" crypto: dependency: transitive description: @@ -81,6 +145,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.3" + csslib: + dependency: transitive + description: + name: csslib + sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb" + url: "https://pub.dev" + source: hosted + version: "1.0.0" cupertino_icons: dependency: "direct main" description: @@ -89,6 +161,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.8" + data_table_2: + dependency: transitive + description: + name: data_table_2 + sha256: f02ec9b24f44420816a87370ff4f4e533e15b274f6267e4c9a88a585ad1a0473 + url: "https://pub.dev" + source: hosted + version: "2.5.15" + dropdown_button2: + dependency: transitive + description: + name: dropdown_button2 + sha256: b0fe8d49a030315e9eef6c7ac84ca964250155a6224d491c1365061bc974a9e1 + url: "https://pub.dev" + source: hosted + version: "2.3.9" easy_debounce: dependency: "direct main" description: @@ -97,6 +185,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.3" + emoji_flag_converter: + dependency: transitive + description: + name: emoji_flag_converter + sha256: "3be8a0d1b0efbc7713113369ca9671ed73c04dfe80159562b1b725c4259d3e35" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + equatable: + dependency: transitive + description: + name: equatable + sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + url: "https://pub.dev" + source: hosted + version: "2.0.5" + expandable: + dependency: transitive + description: + name: expandable + sha256: "9604d612d4d1146dafa96c6d8eec9c2ff0994658d6d09fed720ab788c7f5afc2" + url: "https://pub.dev" + source: hosted + version: "5.0.1" fake_async: dependency: transitive description: @@ -121,6 +233,14 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.0" + file_picker: + dependency: transitive + description: + name: file_picker + sha256: "824f5b9f389bfc4dddac3dea76cd70c51092d9dff0b2ece7ef4f53db8547d258" + url: "https://pub.dev" + source: hosted + version: "8.0.6" fixnum: dependency: transitive description: @@ -129,6 +249,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + fl_chart: + dependency: transitive + description: + name: fl_chart + sha256: d0f0d49112f2f4b192481c16d05b6418bd7820e021e265a3c22db98acf7ed7fb + url: "https://pub.dev" + source: hosted + version: "0.68.0" + flip_card: + dependency: transitive + description: + name: flip_card + sha256: "5d4aa58f3983cced0782f4ce45826b7eea36e8e464964d9209dcbc7a87b2292f" + url: "https://pub.dev" + source: hosted + version: "0.7.0" + floating_bottom_navigation_bar: + dependency: transitive + description: + name: floating_bottom_navigation_bar + sha256: "2ecffcb10841c8bf1a084adaef544ecd5451577b31bd80767e6bab1875c941a6" + url: "https://pub.dev" + source: hosted + version: "1.5.2" flutter: dependency: "direct main" description: flutter @@ -150,6 +294,22 @@ packages: url: "https://pub.dev" source: hosted version: "3.3.2" + flutter_card_swiper: + dependency: transitive + description: + name: flutter_card_swiper + sha256: "024ba0fbce670eb7f71fa5aaf137de32017f2503fa05260a8e0a872e075a88c6" + url: "https://pub.dev" + source: hosted + version: "6.1.0" + flutter_credit_card: + dependency: transitive + description: + name: flutter_credit_card + sha256: dffaee369b0e64fb81e63e78d6410762f73b67346692bd2ae7bdcc6d911452e3 + url: "https://pub.dev" + source: hosted + version: "4.0.1" flutter_lints: dependency: "direct dev" description: @@ -158,6 +318,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.2" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: c6b0b4c05c458e1c01ad9bcc14041dd7b1f6783d487be4386f793f47a8a4d03e + url: "https://pub.dev" + source: hosted + version: "2.0.20" flutter_shaders: dependency: transitive description: @@ -166,11 +334,48 @@ packages: url: "https://pub.dev" source: hosted version: "0.1.2" + flutter_staggered_grid_view: + dependency: transitive + description: + name: flutter_staggered_grid_view + sha256: "19e7abb550c96fbfeb546b23f3ff356ee7c59a019a651f8f102a4ba9b7349395" + url: "https://pub.dev" + source: hosted + version: "0.7.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" + flutterflow_ui: + dependency: "direct main" + description: + name: flutterflow_ui + sha256: bca5d9eef934159be1bb3db101ccd19cb726535e2c0830ccae8b84c281f967cd + url: "https://pub.dev" + source: hosted + version: "0.3.1" + font_awesome_flutter: + dependency: transitive + description: + name: font_awesome_flutter + sha256: "275ff26905134bcb59417cf60ad979136f1f8257f2f449914b2c3e05bbb4cd6f" + url: "https://pub.dev" + source: hosted + version: "10.7.0" + from_css_color: + dependency: transitive + description: + name: from_css_color + sha256: "5e4d1795c8d10af94e51dd97636b2a29170a132be1aceba103e9866028d20823" + url: "https://pub.dev" + source: hosted + version: "2.0.0" google_fonts: dependency: "direct main" description: @@ -179,6 +384,78 @@ packages: url: "https://pub.dev" source: hosted version: "6.2.1" + google_maps: + dependency: transitive + description: + name: google_maps + sha256: "47eef3836b49bb030d5cb3afc60b8451408bf34cf753e571b645d6529eb4251a" + url: "https://pub.dev" + source: hosted + version: "7.1.0" + google_maps_flutter: + dependency: transitive + description: + name: google_maps_flutter + sha256: acf0ec482d86b2ac55ade80597ce7f797a47971f5210ebfd030f0d58130e0a94 + url: "https://pub.dev" + source: hosted + version: "2.7.0" + google_maps_flutter_android: + dependency: transitive + description: + name: google_maps_flutter_android + sha256: "6b7f7730960f9adb1b77a530572182451d66e30808ebb052665e64c4276a8f0e" + url: "https://pub.dev" + source: hosted + version: "2.11.1" + google_maps_flutter_ios: + dependency: transitive + description: + name: google_maps_flutter_ios + sha256: "1043d0a4ad52612444b24edbd2d61f6de40e01e842fe2a9248be53fa70bc7047" + url: "https://pub.dev" + source: hosted + version: "2.8.1" + google_maps_flutter_platform_interface: + dependency: transitive + description: + name: google_maps_flutter_platform_interface + sha256: bd60ca330e3c7763b95b477054adec338a522d982af73ecc520b232474063ac5 + url: "https://pub.dev" + source: hosted + version: "2.8.0" + google_maps_flutter_web: + dependency: transitive + description: + name: google_maps_flutter_web + sha256: f3155c12119d8a5c2732fdf39ceb5cc095bc662059a03b4ea23294ecebe1d199 + url: "https://pub.dev" + source: hosted + version: "0.5.8" + google_mobile_ads: + dependency: transitive + description: + name: google_mobile_ads + sha256: e2d18992d30b2be77cb6976b931112fc3c4612feffb5eb7a8b036bd7a64934da + url: "https://pub.dev" + source: hosted + version: "5.1.0" + graphs: + dependency: transitive + description: + name: graphs + sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 + url: "https://pub.dev" + source: hosted + version: "2.3.1" + html: + dependency: transitive + description: + name: html + sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" + url: "https://pub.dev" + source: hosted + version: "0.15.4" http: dependency: transitive description: @@ -195,6 +472,54 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" + intl: + dependency: transitive + description: + name: intl + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + url: "https://pub.dev" + source: hosted + version: "0.19.0" + iregexp: + dependency: transitive + description: + name: iregexp + sha256: "143859dcaeecf6f683102786762d70a47ef8441a0d2287a158172d32d38799cf" + url: "https://pub.dev" + source: hosted + version: "0.1.2" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" + js_wrapping: + dependency: transitive + description: + name: js_wrapping + sha256: e385980f7c76a8c1c9a560dfb623b890975841542471eade630b2871d243851c + url: "https://pub.dev" + source: hosted + version: "0.7.4" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" + json_path: + dependency: transitive + description: + name: json_path + sha256: dc25b4e2297a6bd39fb52b7d122a7787b7dab751fb278d315b54706b98bb76db + url: "https://pub.dev" + source: hosted + version: "0.7.2" leak_tracker: dependency: transitive description: @@ -227,6 +552,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0" + mapbox_search: + dependency: transitive + description: + name: mapbox_search + sha256: "6606ab1af8f34a22b5b096d6037d95117e4b51d48c63161d817130c91c736e7e" + url: "https://pub.dev" + source: hosted + version: "4.2.2" matcher: dependency: transitive description: @@ -243,6 +576,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.8.0" + maybe_just_nothing: + dependency: transitive + description: + name: maybe_just_nothing + sha256: "0c06326e26d08f6ed43247404376366dc4d756cef23a4f1db765f546224c35e0" + url: "https://pub.dev" + source: hosted + version: "0.5.3" meta: dependency: transitive description: @@ -251,6 +592,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.12.0" + mime_type: + dependency: transitive + description: + name: mime_type + sha256: "2ad6e67d3d2de9ac0f8ef5352d998fd103cb21351ae8c02fb0c78b079b37d275" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + native_device_orientation: + dependency: transitive + description: + name: native_device_orientation + sha256: "744a03030fad5a332a54833cd34f1e2ee51ae9acf477b4ef85bacc8823af9937" + url: "https://pub.dev" + source: hosted + version: "1.2.1" nested: dependency: transitive description: @@ -267,6 +624,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" + page_transition: + dependency: transitive + description: + name: page_transition + sha256: dee976b1f23de9bbef5cd512fe567e9f6278caee11f5eaca9a2115c19dc49ef6 + url: "https://pub.dev" + source: hosted + version: "2.1.0" path: dependency: transitive description: @@ -323,6 +688,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.1" + percent_indicator: + dependency: transitive + description: + name: percent_indicator + sha256: c37099ad833a883c9d71782321cb65c3a848c21b6939b6185f0ff6640d05814c + url: "https://pub.dev" + source: hosted + version: "4.2.3" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 + url: "https://pub.dev" + source: hosted + version: "6.0.2" + photo_view: + dependency: transitive + description: + name: photo_view + sha256: "8036802a00bae2a78fc197af8a158e3e2f7b500561ed23b4c458107685e645bb" + url: "https://pub.dev" + source: hosted + version: "0.14.0" platform: dependency: transitive description: @@ -339,6 +728,38 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" + pointer_interceptor: + dependency: transitive + description: + name: pointer_interceptor + sha256: d0a8e660d1204eaec5bd34b34cc92174690e076d2e4f893d9d68c486a13b07c4 + url: "https://pub.dev" + source: hosted + version: "0.10.1+1" + pointer_interceptor_ios: + dependency: transitive + description: + name: pointer_interceptor_ios + sha256: a6906772b3205b42c44614fcea28f818b1e5fdad73a4ca742a7bd49818d9c917 + url: "https://pub.dev" + source: hosted + version: "0.10.1" + pointer_interceptor_platform_interface: + dependency: transitive + description: + name: pointer_interceptor_platform_interface + sha256: "0597b0560e14354baeb23f8375cd612e8bd4841bf8306ecb71fcd0bb78552506" + url: "https://pub.dev" + source: hosted + version: "0.10.0+1" + pointer_interceptor_web: + dependency: transitive + description: + name: pointer_interceptor_web + sha256: a6237528b46c411d8d55cdfad8fcb3269fc4cbb26060b14bff94879165887d1e + url: "https://pub.dev" + source: hosted + version: "0.10.2" provider: dependency: "direct main" description: @@ -347,6 +768,38 @@ packages: url: "https://pub.dev" source: hosted version: "6.1.2" + qr: + dependency: transitive + description: + name: qr + sha256: "64957a3930367bf97cc211a5af99551d630f2f4625e38af10edd6b19131b64b3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + rfc_6901: + dependency: transitive + description: + name: rfc_6901 + sha256: df1bbfa3d023009598f19636d6114c6ac1e0b7bb7bf6a260f0e6e6ce91416820 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + rive: + dependency: transitive + description: + name: rive + sha256: ae75a6e9cfbf146630bfb1feba97ee582d935508be6b362e4bd197b9c55a6dd3 + url: "https://pub.dev" + source: hosted + version: "0.12.4" + rive_common: + dependency: transitive + description: + name: rive_common + sha256: f4e20d0a99c5040c85624a3eb2b0b6b19e614d93a693c3bb25cf6e7bb2d3d6d3 + url: "https://pub.dev" + source: hosted + version: "0.2.8" rxdart: dependency: transitive description: @@ -355,11 +808,91 @@ packages: url: "https://pub.dev" source: hosted version: "0.27.7" + sanitize_html: + dependency: transitive + description: + name: sanitize_html + sha256: "12669c4a913688a26555323fb9cec373d8f9fbe091f2d01c40c723b33caa8989" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + shared_preferences: + dependency: transitive + description: + name: shared_preferences + sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180 + url: "https://pub.dev" + source: hosted + version: "2.2.3" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "93d0ec9dd902d85f326068e6a899487d1f65ffcd5798721a95330b26c8131577" + url: "https://pub.dev" + source: hosted + version: "2.2.3" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "0a8a893bf4fd1152f93fec03a415d11c27c74454d96e2318a7ac38dd18683ab7" + url: "https://pub.dev" + source: hosted + version: "2.4.0" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + simple_gesture_detector: + dependency: transitive + description: + name: simple_gesture_detector + sha256: ba2cd5af24ff20a0b8d609cec3f40e5b0744d2a71804a2616ae086b9c19d19a3 + url: "https://pub.dev" + source: hosted + version: "0.2.1" sky_engine: dependency: transitive description: flutter source: sdk version: "0.0.99" + smooth_page_indicator: + dependency: transitive + description: + name: smooth_page_indicator + sha256: "725bc638d5e79df0c84658e1291449996943f93bacbc2cec49963dbbab48d8ae" + url: "https://pub.dev" + source: hosted + version: "1.1.0" source_span: dependency: transitive description: @@ -400,6 +933,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.11.1" + stop_watch_timer: + dependency: transitive + description: + name: stop_watch_timer + sha256: "9128717892ac94094e61544cb5259aff79eefebc0c4549a46d815689c56be8a7" + url: "https://pub.dev" + source: hosted + version: "3.1.1" stream_channel: dependency: transitive description: @@ -408,6 +949,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + url: "https://pub.dev" + source: hosted + version: "2.1.0" string_scanner: dependency: transitive description: @@ -416,6 +965,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + substring_highlight: + dependency: transitive + description: + name: substring_highlight + sha256: "96c61e8316098831f6bee87d2386617e4be6aaf87fbc89402dc049d371b67efb" + url: "https://pub.dev" + source: hosted + version: "1.0.33" synchronized: dependency: transitive description: @@ -424,6 +981,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.0+1" + table_calendar: + dependency: transitive + description: + name: table_calendar + sha256: "4ca32b2fc919452c9974abd4c6ea611a63e33b9e4f0b8c38dba3ac1f4a6549d1" + url: "https://pub.dev" + source: hosted + version: "3.1.2" term_glyph: dependency: transitive description: @@ -440,6 +1005,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.0" + timeago: + dependency: transitive + description: + name: timeago + sha256: "054cedf68706bb142839ba0ae6b135f6b68039f0b8301cbe8784ae653d5ff8de" + url: "https://pub.dev" + source: hosted + version: "3.7.0" typed_data: dependency: transitive description: @@ -448,6 +1021,70 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" + url_launcher: + dependency: transitive + description: + name: url_launcher + sha256: "21b704ce5fa560ea9f3b525b43601c678728ba46725bab9b01187b4831377ed3" + url: "https://pub.dev" + source: hosted + version: "6.3.0" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + sha256: ceb2625f0c24ade6ef6778d1de0b2e44f2db71fded235eb52295247feba8c5cf + url: "https://pub.dev" + source: hosted + version: "6.3.3" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + sha256: "7068716403343f6ba4969b4173cbf3b84fc768042124bc2c011e5d782b24fe89" + url: "https://pub.dev" + source: hosted + version: "6.3.0" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811 + url: "https://pub.dev" + source: hosted + version: "3.1.1" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + sha256: "9a1a42d5d2d95400c795b2914c36fdcb525870c752569438e4ebb09a2b5d90de" + url: "https://pub.dev" + source: hosted + version: "3.2.0" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: "8d9e750d8c9338601e709cd0885f95825086bd8b642547f26bda435aade95d8a" + url: "https://pub.dev" + source: hosted + version: "2.3.1" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7 + url: "https://pub.dev" + source: hosted + version: "3.1.1" uuid: dependency: transitive description: @@ -480,6 +1117,46 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.1" + webview_flutter: + dependency: transitive + description: + name: webview_flutter + sha256: "6869c8786d179f929144b4a1f86e09ac0eddfe475984951ea6c634774c16b522" + url: "https://pub.dev" + source: hosted + version: "4.8.0" + webview_flutter_android: + dependency: transitive + description: + name: webview_flutter_android + sha256: f42447ca49523f11d8f70abea55ea211b3cafe172dd7a0e7ac007bb35dd356dc + url: "https://pub.dev" + source: hosted + version: "3.16.4" + webview_flutter_platform_interface: + dependency: transitive + description: + name: webview_flutter_platform_interface + sha256: d937581d6e558908d7ae3dc1989c4f87b786891ab47bb9df7de548a151779d8d + url: "https://pub.dev" + source: hosted + version: "2.10.0" + webview_flutter_wkwebview: + dependency: transitive + description: + name: webview_flutter_wkwebview + sha256: "7affdf9d680c015b11587181171d3cad8093e449db1f7d9f0f08f4f33d24f9a0" + url: "https://pub.dev" + source: hosted + version: "3.13.1" + webviewx_plus: + dependency: transitive + description: + name: webviewx_plus + sha256: "2832047d46c5b881483f8ab4aeb3cd4de13708740f3df27201164f2daa7e3da2" + url: "https://pub.dev" + source: hosted + version: "0.5.0" win32: dependency: transitive description: diff --git a/mobile/app/pubspec.yaml b/mobile/app/pubspec.yaml index 6dc73af..a8ff033 100644 --- a/mobile/app/pubspec.yaml +++ b/mobile/app/pubspec.yaml @@ -30,6 +30,7 @@ environment: dependencies: flutter: sdk: flutter + flutterflow_ui: 0.3.1 # The following adds the Cupertino Icons font to your application. diff --git a/mobile/app/windows/flutter/generated_plugin_registrant.cc b/mobile/app/windows/flutter/generated_plugin_registrant.cc index 8b6d468..72ff4b7 100644 --- a/mobile/app/windows/flutter/generated_plugin_registrant.cc +++ b/mobile/app/windows/flutter/generated_plugin_registrant.cc @@ -6,6 +6,12 @@ #include "generated_plugin_registrant.h" +#include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { + RivePluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("RivePlugin")); + UrlLauncherWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/mobile/app/windows/flutter/generated_plugins.cmake b/mobile/app/windows/flutter/generated_plugins.cmake index b93c4c3..e141d51 100644 --- a/mobile/app/windows/flutter/generated_plugins.cmake +++ b/mobile/app/windows/flutter/generated_plugins.cmake @@ -3,6 +3,8 @@ # list(APPEND FLUTTER_PLUGIN_LIST + rive_common + url_launcher_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST From a568151b2c551d02dfa18140d2f3e53f1e9869e9 Mon Sep 17 00:00:00 2001 From: Joshua Date: Sat, 6 Jul 2024 13:00:36 -0600 Subject: [PATCH 05/24] fixed errors and removed dependency on flutterflowtheme. next working on getting it running. --- mobile/app/ios/Flutter/Debug.xcconfig | 1 + mobile/app/ios/Flutter/Release.xcconfig | 1 + mobile/app/ios/Podfile | 44 ++ .../conversation_details_overlay_model.dart | 4 +- .../conversation_details_overlay_widget.dart | 67 +- .../conversation_options_widget.dart | 82 +- .../conversation_thread_model.dart | 7 +- .../conversation_thread_widget.dart | 85 +- .../model_item/model_item_widget.dart | 37 +- .../app/lib/components/no_chats_widget.dart | 13 +- .../options_dialog/options_dialog_model.dart | 4 +- .../options_dialog/options_dialog_widget.dart | 45 +- .../lib/components/popup/popup_widget.dart | 45 +- .../prompt_box/prompt_box_widget.dart | 49 +- mobile/app/lib/pages/conversation_widget.dart | 42 +- .../lib/pages/conversations_list_widget.dart | 77 +- .../app/lib/pages/image_details_widget.dart | 45 +- mobile/app/lib/pages/settings_widget.dart | 744 +++++------------- mobile/app/lib/pages/setup_widget.dart | 55 +- mobile/app/lib/pages/support_form_widget.dart | 4 +- mobile/app/lib/theme.dart | 17 +- .../app/macos/Flutter/Flutter-Debug.xcconfig | 1 + .../macos/Flutter/Flutter-Release.xcconfig | 1 + .../Flutter/GeneratedPluginRegistrant.swift | 2 + mobile/app/macos/Podfile | 43 + mobile/app/pubspec.lock | 64 +- mobile/app/pubspec.yaml | 1 + one-ai-mobile | 1 + output.txt | 0 29 files changed, 604 insertions(+), 977 deletions(-) create mode 100644 mobile/app/ios/Podfile create mode 100644 mobile/app/macos/Podfile create mode 160000 one-ai-mobile create mode 100644 output.txt diff --git a/mobile/app/ios/Flutter/Debug.xcconfig b/mobile/app/ios/Flutter/Debug.xcconfig index 592ceee..ec97fc6 100644 --- a/mobile/app/ios/Flutter/Debug.xcconfig +++ b/mobile/app/ios/Flutter/Debug.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/mobile/app/ios/Flutter/Release.xcconfig b/mobile/app/ios/Flutter/Release.xcconfig index 592ceee..c4855bf 100644 --- a/mobile/app/ios/Flutter/Release.xcconfig +++ b/mobile/app/ios/Flutter/Release.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/mobile/app/ios/Podfile b/mobile/app/ios/Podfile new file mode 100644 index 0000000..d97f17e --- /dev/null +++ b/mobile/app/ios/Podfile @@ -0,0 +1,44 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '12.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/mobile/app/lib/components/conversation_details_overlay/conversation_details_overlay_model.dart b/mobile/app/lib/components/conversation_details_overlay/conversation_details_overlay_model.dart index 00629a4..552924c 100644 --- a/mobile/app/lib/components/conversation_details_overlay/conversation_details_overlay_model.dart +++ b/mobile/app/lib/components/conversation_details_overlay/conversation_details_overlay_model.dart @@ -11,11 +11,11 @@ class ConversationDetailsOverlayModel /// State fields for stateful widgets in this component. // Model for OptionsDialog component. - late OptionsDialogModel optionsDialogModel; + late PopupModel optionsDialogModel; @override void initState(BuildContext context) { - optionsDialogModel = createModel(context, () => OptionsDialogModel()); + optionsDialogModel = createModel(context, () => PopupModel()); } @override diff --git a/mobile/app/lib/components/conversation_details_overlay/conversation_details_overlay_widget.dart b/mobile/app/lib/components/conversation_details_overlay/conversation_details_overlay_widget.dart index c8b9d15..13f0b48 100644 --- a/mobile/app/lib/components/conversation_details_overlay/conversation_details_overlay_widget.dart +++ b/mobile/app/lib/components/conversation_details_overlay/conversation_details_overlay_widget.dart @@ -5,6 +5,8 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; +import 'package:app/theme.dart'; + import 'conversation_details_overlay_model.dart'; export 'conversation_details_overlay_model.dart'; @@ -73,29 +75,24 @@ class _ConversationDetailsOverlayWidgetState mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Padding( - padding: const EdgeInsetsDirectional.fromSTEB(16, 0, 0, 0), + const Padding( + padding: EdgeInsetsDirectional.fromSTEB(16, 0, 0, 0), child: Text( 'Chat Details', - style: AppAppAppAppAppAppAppAppAppTheme - .headlineSmall - .override( - fontFamily: 'Outfit', - letterSpacing: 0, - ), + style: AppTheme.headlineSmall, ), ), Padding( padding: const EdgeInsetsDirectional.fromSTEB(0, 0, 16, 0), child: FlutterFlowIconButton( - borderColor: AppAppAppAppAppAppAppAppAppTheme.alternate, + borderColor: AppTheme.alternate, borderRadius: 12, borderWidth: 1, buttonSize: 40, - fillColor: AppAppAppAppAppAppAppAppAppTheme.accent4, - icon: Icon( + fillColor: AppTheme.accent4, + icon: const Icon( Icons.close_rounded, - color: AppAppAppAppAppAppAppAppAppTheme.primaryText, + color: AppTheme.primaryTextColor, size: 24, ), onPressed: () async { @@ -109,38 +106,34 @@ class _ConversationDetailsOverlayWidgetState padding: const EdgeInsetsDirectional.fromSTEB(16, 4, 0, 4), child: RichText( textScaler: MediaQuery.of(context).textScaler, - text: TextSpan( + text: const TextSpan( children: [ - const TextSpan( + TextSpan( text: 'Group Chat ID: ', style: TextStyle(), ), TextSpan( text: 'Hello World ', style: TextStyle( - color: AppAppAppAppAppAppAppAppAppTheme.primary, + color: AppTheme.primary, fontWeight: FontWeight.bold, ), ) ], - style: - AppAppAppAppAppAppAppAppAppTheme.labelMedium.override( - fontFamily: 'Readex Pro', - letterSpacing: 0, - ), + style: AppTheme.labelMedium, ), ), ), - Expanded( + const Expanded( child: Align( - alignment: const AlignmentDirectional(0, 0), + alignment: AlignmentDirectional(0, 0), child: Column( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.start, children: [ Divider( thickness: 1, - color: AppAppAppAppAppAppAppAppAppTheme.tertiary, + color: AppTheme.tertiary, ), ], ), @@ -154,18 +147,18 @@ class _ConversationDetailsOverlayWidgetState borderRadius: BorderRadius.circular(12), child: Container( decoration: BoxDecoration( - color: AppAppAppAppAppAppAppAppAppTheme + color: AppTheme .secondaryBackground, borderRadius: BorderRadius.circular(12), border: Border.all( - color: AppAppAppAppAppAppAppAppAppTheme.alternate, + color: AppTheme.alternate, ), ), child: wrapWithModel( model: _model.optionsDialogModel, updateCallback: () => setState(() {}), updateOnChange: true, - child: OptionsDialogWidget(), + child: const PopupWidget(), ), ), ), @@ -185,26 +178,20 @@ class _ConversationDetailsOverlayWidgetState iconPadding: const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), color: - AppAppAppAppAppAppAppAppAppTheme.secondaryBackground, - textStyle: - AppAppAppAppAppAppAppAppAppTheme.titleLarge.override( - fontFamily: 'Outfit', - fontSize: 18, - letterSpacing: 0, - ), + AppTheme.secondaryBackground, + textStyle: AppTheme.titleLarge, elevation: 0, - borderSide: BorderSide( - color: AppAppAppAppAppAppAppAppAppTheme.alternate, + borderSide: const BorderSide( + color: AppTheme.alternate, width: 2, ), borderRadius: BorderRadius.circular(12), - hoverColor: AppAppAppAppAppAppAppAppAppTheme.alternate, - hoverBorderSide: BorderSide( - color: AppAppAppAppAppAppAppAppAppTheme.alternate, + hoverColor: AppTheme.alternate, + hoverBorderSide: const BorderSide( + color: AppTheme.alternate, width: 2, ), - hoverTextColor: - AppAppAppAppAppAppAppAppAppTheme.primaryText, + hoverTextColor: AppTheme.primaryTextColor, hoverElevation: 3, ), ), diff --git a/mobile/app/lib/components/conversation_options/conversation_options_widget.dart b/mobile/app/lib/components/conversation_options/conversation_options_widget.dart index 37b279f..7af4b4a 100644 --- a/mobile/app/lib/components/conversation_options/conversation_options_widget.dart +++ b/mobile/app/lib/components/conversation_options/conversation_options_widget.dart @@ -3,6 +3,8 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; +import 'package:app/theme.dart'; + import 'conversation_options_model.dart'; export 'conversation_options_model.dart'; @@ -40,8 +42,8 @@ class _ConversationOptionsWidgetState extends State { Widget build(BuildContext context) { return Container( width: double.infinity, - decoration: BoxDecoration( - color: AppAppAppAppAppAppAppAppAppTheme.secondaryBackground, + decoration: const BoxDecoration( + color: AppTheme.secondaryBackground, ), child: Padding( padding: const EdgeInsetsDirectional.fromSTEB(0, 12, 0, 24), @@ -60,35 +62,30 @@ class _ConversationOptionsWidgetState extends State { children: [ Card( clipBehavior: Clip.antiAliasWithSaveLayer, - color: AppAppAppAppAppAppAppAppAppTheme.primaryBackground, + color: AppTheme.primaryBackground, elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(40), ), - child: Padding( - padding: const EdgeInsets.all(8), + child: const Padding( + padding: EdgeInsets.all(8), child: Icon( Icons.share_rounded, - color: AppAppAppAppAppAppAppAppAppTheme.secondaryText, + color: AppTheme.secondaryText, size: 20, ), ), ), - Expanded( + const Expanded( child: Padding( - padding: const EdgeInsetsDirectional.fromSTEB(12, 0, 0, 0), + padding: EdgeInsetsDirectional.fromSTEB(12, 0, 0, 0), child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Share', - style: AppAppAppAppAppAppAppAppAppTheme - .labelLarge - .override( - fontFamily: 'Readex Pro', - letterSpacing: 0, - ), + style: AppTheme.labelLarge, ), ], ), @@ -109,35 +106,30 @@ class _ConversationOptionsWidgetState extends State { children: [ Card( clipBehavior: Clip.antiAliasWithSaveLayer, - color: AppAppAppAppAppAppAppAppAppTheme.primaryBackground, + color: AppTheme.primaryBackground, elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(40), ), - child: Padding( - padding: const EdgeInsets.all(8), + child: const Padding( + padding: EdgeInsets.all(8), child: Icon( Icons.insert_link, - color: AppAppAppAppAppAppAppAppAppTheme.secondaryText, + color: AppTheme.secondaryText, size: 20, ), ), ), - Expanded( + const Expanded( child: Padding( - padding: const EdgeInsetsDirectional.fromSTEB(12, 0, 0, 0), + padding: EdgeInsetsDirectional.fromSTEB(12, 0, 0, 0), child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Get Link', - style: AppAppAppAppAppAppAppAppAppTheme - .labelLarge - .override( - fontFamily: 'Readex Pro', - letterSpacing: 0, - ), + style: AppTheme.labelLarge, ), ], ), @@ -158,35 +150,30 @@ class _ConversationOptionsWidgetState extends State { children: [ Card( clipBehavior: Clip.antiAliasWithSaveLayer, - color: AppAppAppAppAppAppAppAppAppTheme.primaryBackground, + color: AppTheme.primaryBackground, elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(40), ), - child: Padding( - padding: const EdgeInsets.all(8), + child: const Padding( + padding: EdgeInsets.all(8), child: Icon( Icons.mode_edit, - color: AppAppAppAppAppAppAppAppAppTheme.secondaryText, + color: AppTheme.secondaryText, size: 20, ), ), ), - Expanded( + const Expanded( child: Padding( - padding: const EdgeInsetsDirectional.fromSTEB(12, 0, 0, 0), + padding: EdgeInsetsDirectional.fromSTEB(12, 0, 0, 0), child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Model selection', - style: AppAppAppAppAppAppAppAppAppTheme - .labelLarge - .override( - fontFamily: 'Readex Pro', - letterSpacing: 0, - ), + style: AppTheme.labelLarge, ), ], ), @@ -207,35 +194,30 @@ class _ConversationOptionsWidgetState extends State { children: [ Card( clipBehavior: Clip.antiAliasWithSaveLayer, - color: AppAppAppAppAppAppAppAppAppTheme.primaryBackground, + color: AppTheme.primaryBackground, elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(40), ), - child: Padding( - padding: const EdgeInsets.all(8), + child: const Padding( + padding: EdgeInsets.all(8), child: Icon( Icons.hide_source, - color: AppAppAppAppAppAppAppAppAppTheme.secondaryText, + color: AppTheme.secondaryText, size: 20, ), ), ), - Expanded( + const Expanded( child: Padding( - padding: const EdgeInsetsDirectional.fromSTEB(12, 0, 0, 0), + padding: EdgeInsetsDirectional.fromSTEB(12, 0, 0, 0), child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Private', - style: AppAppAppAppAppAppAppAppAppTheme - .labelLarge - .override( - fontFamily: 'Readex Pro', - letterSpacing: 0, - ), + style: AppTheme.labelLarge, ), ], ), diff --git a/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart b/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart index f2886ac..c574eb4 100644 --- a/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart +++ b/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart @@ -13,20 +13,19 @@ class ConversationThreadModel final formKey = GlobalKey(); // Model for ConversationBubbles component. - late ConversationBubblesModel conversationBubblesModel; + //late ConversationBubblesModel conversationBubblesModel; // Model for PromptBox component. late PromptBoxModel promptBoxModel; @override void initState(BuildContext context) { - conversationBubblesModel = - createModel(context, () => ConversationBubblesModel()); + //conversationBubblesModel = createModel(context, () => ConversationBubblesModel()); promptBoxModel = createModel(context, () => PromptBoxModel()); } @override void dispose() { - conversationBubblesModel.dispose(); + //conversationBubblesModel.dispose(); promptBoxModel.dispose(); } } diff --git a/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart b/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart index fb4678f..869317f 100644 --- a/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart +++ b/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart @@ -6,6 +6,8 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; +import 'package:app/theme.dart'; + import 'conversation_thread_model.dart'; export 'conversation_thread_model.dart'; @@ -44,8 +46,8 @@ class _ConversationThreadWidgetState extends State { return Container( width: double.infinity, height: double.infinity, - decoration: BoxDecoration( - color: AppAppAppAppAppAppAppAppAppTheme.primaryBackground, + decoration: const BoxDecoration( + color: AppTheme.primaryBackground, ), child: Column( mainAxisSize: MainAxisSize.max, @@ -71,20 +73,21 @@ class _ConversationThreadWidgetState extends State { minHeight: MediaQuery.sizeOf(context).height * 1, ), decoration: const BoxDecoration(), - child: wrapWithModel( - model: _model.conversationBubblesModel, - updateCallback: () => setState(() {}), - child: ConversationBubblesWidget(), - ), + //child: wrapWithModel( + //model: _model.conversationBubblesModel, + //updateCallback: () => setState(() {}), + //child: ConversationBubblesWidget(), + //child: const Text("test"), + //), ), ], ), ), Container( width: double.infinity, - decoration: BoxDecoration( - color: AppAppAppAppAppAppAppAppAppTheme.secondaryBackground, - boxShadow: const [ + decoration: const BoxDecoration( + color: AppTheme.secondaryBackground, + boxShadow: [ BoxShadow( blurRadius: 3, color: Color(0x33000000), @@ -95,6 +98,7 @@ class _ConversationThreadWidgetState extends State { ) ], ), + // TODO child: Column( mainAxisSize: MainAxisSize.max, children: [ @@ -110,44 +114,45 @@ class _ConversationThreadWidgetState extends State { mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.start, children: [ - FlutterFlowMediaDisplay( - path: '', - imageBuilder: (path) => ClipRRect( - borderRadius: BorderRadius.circular(8), - child: CachedNetworkImage( - fadeInDuration: const Duration(milliseconds: 500), - fadeOutDuration: - const Duration(milliseconds: 500), - imageUrl: path, - width: 120, - height: 100, - fit: BoxFit.cover, - ), - ), - videoPlayerBuilder: (path) => - FlutterFlowVideoPlayer( - path: path, - width: 300, - autoPlay: false, - looping: true, - showControls: true, - allowFullScreen: true, - allowPlaybackSpeedMenu: false, - ), - ), + // FlutterFlowMediaDisplay( + // path: '', + // imageBuilder: (path) => ClipRRect( + // borderRadius: BorderRadius.circular(8), + // child: CachedNetworkImage( + // fadeInDuration: const Duration(milliseconds: 500), + // fadeOutDuration: + // const Duration(milliseconds: 500), + // imageUrl: path, + // width: 120, + // height: 100, + // fit: BoxFit.cover, + // ), + // ), + // TODO + // videoPlayerBuilder: (path) => + // FlutterFlowVideoPlayer( + // path: path, + // width: 300, + // autoPlay: false, + // looping: true, + // showControls: true, + // allowFullScreen: true, + // allowPlaybackSpeedMenu: false, + // ), + // ), Align( alignment: const AlignmentDirectional(-1, -1), child: FlutterFlowIconButton( borderColor: - AppAppAppAppAppAppAppAppAppTheme.error, + AppTheme.error, borderRadius: 20, borderWidth: 2, buttonSize: 40, - fillColor: AppAppAppAppAppAppAppAppAppTheme + fillColor: AppTheme .primaryBackground, - icon: Icon( + icon: const Icon( Icons.delete_outline_rounded, - color: AppAppAppAppAppAppAppAppAppTheme.error, + color: AppTheme.error, size: 24, ), onPressed: () { @@ -173,7 +178,7 @@ class _ConversationThreadWidgetState extends State { child: wrapWithModel( model: _model.promptBoxModel, updateCallback: () => setState(() {}), - child: PromptBoxWidget(), + child: const PromptBoxWidget(), ), ), ), diff --git a/mobile/app/lib/components/model_item/model_item_widget.dart b/mobile/app/lib/components/model_item/model_item_widget.dart index 2d1eaeb..56656f7 100644 --- a/mobile/app/lib/components/model_item/model_item_widget.dart +++ b/mobile/app/lib/components/model_item/model_item_widget.dart @@ -3,6 +3,8 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; +import 'package:app/theme.dart'; + import 'model_item_model.dart'; export 'model_item_model.dart'; @@ -48,7 +50,7 @@ class _ModelItemWidgetState extends State { }), child: Container( decoration: BoxDecoration( - color: AppAppAppAppAppAppAppAppAppTheme.primaryBackground, + color: AppTheme.primaryBackground, borderRadius: BorderRadius.circular(0), ), child: Padding( @@ -60,29 +62,25 @@ class _ModelItemWidgetState extends State { width: 40, height: 40, decoration: BoxDecoration( - color: AppAppAppAppAppAppAppAppAppTheme.accent1, + color: AppTheme.accent1, borderRadius: BorderRadius.circular(12), border: Border.all( - color: AppAppAppAppAppAppAppAppAppTheme.primary, + color: AppTheme.primary, width: 2, ), ), - child: Align( - alignment: const AlignmentDirectional(0, 0), + child: const Align( + alignment: AlignmentDirectional(0, 0), child: Text( 'A', textAlign: TextAlign.center, - style: AppAppAppAppAppAppAppAppAppTheme.bodyLarge.override( - fontFamily: 'Readex Pro', - letterSpacing: 0, - fontWeight: FontWeight.bold, - ), + style: AppTheme.bodyLarge, ), ), ), - Expanded( + const Expanded( child: Padding( - padding: const EdgeInsetsDirectional.fromSTEB(12, 0, 8, 0), + padding: EdgeInsetsDirectional.fromSTEB(12, 0, 8, 0), child: Column( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, @@ -90,22 +88,13 @@ class _ModelItemWidgetState extends State { children: [ Text( 'Randy Peterson', - style: AppAppAppAppAppAppAppAppAppTheme.bodyMedium.override( - fontFamily: 'Readex Pro', - letterSpacing: 0, - fontWeight: FontWeight.bold, - ), + style: AppTheme.bodyMedium, ), Padding( - padding: const EdgeInsetsDirectional.fromSTEB(0, 4, 0, 0), + padding: EdgeInsetsDirectional.fromSTEB(0, 4, 0, 0), child: Text( 'name@domainname.com', - style: - AppAppAppAppAppAppAppAppAppTheme.bodySmall.override( - fontFamily: 'Readex Pro', - color: AppAppAppAppAppAppAppAppAppTheme.primary, - letterSpacing: 0, - ), + style: AppTheme.bodySmall, ), ), ], diff --git a/mobile/app/lib/components/no_chats_widget.dart b/mobile/app/lib/components/no_chats_widget.dart index 5ce3259..3fca32d 100644 --- a/mobile/app/lib/components/no_chats_widget.dart +++ b/mobile/app/lib/components/no_chats_widget.dart @@ -3,6 +3,8 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; +import 'package:app/theme.dart'; + import 'no_chats_model.dart'; export 'no_chats_model.dart'; @@ -59,11 +61,7 @@ class _NoChatsWidgetState extends State { child: Text( widget.title, textAlign: TextAlign.center, - style: AppAppAppAppAppAppAppAppAppTheme.headlineSmall.override( - fontFamily: 'Outfit', - color: AppAppAppAppAppAppAppAppAppTheme.primaryText, - letterSpacing: 0, - ), + style: AppTheme.headlineSmall, ), ), Padding( @@ -71,10 +69,7 @@ class _NoChatsWidgetState extends State { child: Text( widget.body, textAlign: TextAlign.center, - style: AppAppAppAppAppAppAppAppAppTheme.labelMedium.override( - fontFamily: 'Readex Pro', - letterSpacing: 0, - ), + style: AppTheme.labelMedium, ), ), ], diff --git a/mobile/app/lib/components/options_dialog/options_dialog_model.dart b/mobile/app/lib/components/options_dialog/options_dialog_model.dart index d178ffe..b8c73cb 100644 --- a/mobile/app/lib/components/options_dialog/options_dialog_model.dart +++ b/mobile/app/lib/components/options_dialog/options_dialog_model.dart @@ -1,10 +1,10 @@ import 'package:flutterflow_ui/flutterflow_ui.dart'; -import 'options_dialog_widget.dart' show OptionsDialogWidget; +import 'options_dialog_widget.dart' show PopupWidget; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; -class OptionsDialogModel extends FlutterFlowModel { +class OptionsDialogModel extends FlutterFlowModel { /// State fields for stateful widgets in this component. // State field(s) for MouseRegion widget. diff --git a/mobile/app/lib/components/options_dialog/options_dialog_widget.dart b/mobile/app/lib/components/options_dialog/options_dialog_widget.dart index c645d80..93692f2 100644 --- a/mobile/app/lib/components/options_dialog/options_dialog_widget.dart +++ b/mobile/app/lib/components/options_dialog/options_dialog_widget.dart @@ -3,6 +3,8 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; +import 'package:app/theme.dart'; + import '/components/popup/popup_model.dart'; export '/components/popup/popup_model.dart'; @@ -78,20 +80,12 @@ class _PopupWidgetState extends State { Row( mainAxisSize: MainAxisSize.max, children: [ - Expanded( + const Expanded( child: Padding( - padding: const EdgeInsetsDirectional.fromSTEB(0, 0, 12, 0), + padding: EdgeInsetsDirectional.fromSTEB(0, 0, 12, 0), child: Text( 'Refine the components modal.', - style: AppAppAppAppAppAppAppAppAppTheme - .headlineMedium - .override( - fontFamily: 'Outfit', - color: const Color(0xFF14181B), - fontSize: 24, - letterSpacing: 0, - fontWeight: FontWeight.normal, - ), + style: AppTheme.headlineMedium, ), ), ), @@ -116,15 +110,9 @@ class _PopupWidgetState extends State { thickness: 2, color: Color(0xFFF1F4F8), ), - Text( + const Text( 'FlutterFlow is a visual development platform that allows you to easily create beautiful and responsive user interfaces for your mobile and web applications. With its drag-and-drop interface and pre-built components, you can quickly prototype and build your app without writing any code. \nAdditionally, FlutterFlow\'s real-time preview feature allows you to see your changes in real-time and make adjustments on the fly.', - style: AppAppAppAppAppAppAppAppAppTheme.labelMedium.override( - fontFamily: 'Readex Pro', - color: const Color(0xFF57636C), - fontSize: 14, - letterSpacing: 0, - fontWeight: FontWeight.normal, - ), + style: AppTheme.labelMedium, ), Padding( padding: const EdgeInsetsDirectional.fromSTEB(0, 16, 0, 0), @@ -144,14 +132,7 @@ class _PopupWidgetState extends State { iconPadding: const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), color: Colors.white, - textStyle: - AppAppAppAppAppAppAppAppAppTheme.bodySmall.override( - fontFamily: 'Readex Pro', - color: const Color(0xFF14181B), - fontSize: 12, - letterSpacing: 0, - fontWeight: FontWeight.normal, - ), + textStyle: AppTheme.bodySmall, elevation: 0, borderSide: const BorderSide( color: Color(0xFFF1F4F8), @@ -174,15 +155,7 @@ class _PopupWidgetState extends State { iconPadding: const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), color: const Color(0xFF4B39EF), - textStyle: AppAppAppAppAppAppAppAppAppTheme - .titleSmall - .override( - fontFamily: 'Readex Pro', - color: Colors.white, - fontSize: 16, - letterSpacing: 0, - fontWeight: FontWeight.w500, - ), + textStyle: AppTheme.titleSmall, elevation: 3, borderSide: const BorderSide( color: Colors.transparent, diff --git a/mobile/app/lib/components/popup/popup_widget.dart b/mobile/app/lib/components/popup/popup_widget.dart index 32175e6..fdf314b 100644 --- a/mobile/app/lib/components/popup/popup_widget.dart +++ b/mobile/app/lib/components/popup/popup_widget.dart @@ -3,6 +3,8 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; +import 'package:app/theme.dart'; + import 'popup_model.dart'; export 'popup_model.dart'; @@ -78,20 +80,12 @@ class _PopupWidgetState extends State { Row( mainAxisSize: MainAxisSize.max, children: [ - Expanded( + const Expanded( child: Padding( - padding: const EdgeInsetsDirectional.fromSTEB(0, 0, 12, 0), + padding: EdgeInsetsDirectional.fromSTEB(0, 0, 12, 0), child: Text( 'Refine the components modal.', - style: AppAppAppAppAppAppAppAppAppTheme - .headlineMedium - .override( - fontFamily: 'Outfit', - color: const Color(0xFF14181B), - fontSize: 24, - letterSpacing: 0, - fontWeight: FontWeight.normal, - ), + style: AppTheme.headlineMedium, ), ), ), @@ -116,15 +110,9 @@ class _PopupWidgetState extends State { thickness: 2, color: Color(0xFFF1F4F8), ), - Text( + const Text( 'FlutterFlow is a visual development platform that allows you to easily create beautiful and responsive user interfaces for your mobile and web applications. With its drag-and-drop interface and pre-built components, you can quickly prototype and build your app without writing any code. \nAdditionally, FlutterFlow\'s real-time preview feature allows you to see your changes in real-time and make adjustments on the fly.', - style: AppAppAppAppAppAppAppAppAppTheme.labelMedium.override( - fontFamily: 'Readex Pro', - color: const Color(0xFF57636C), - fontSize: 14, - letterSpacing: 0, - fontWeight: FontWeight.normal, - ), + style: AppTheme.labelMedium, ), Padding( padding: const EdgeInsetsDirectional.fromSTEB(0, 16, 0, 0), @@ -144,14 +132,7 @@ class _PopupWidgetState extends State { iconPadding: const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), color: Colors.white, - textStyle: - AppAppAppAppAppAppAppAppAppTheme.bodySmall.override( - fontFamily: 'Readex Pro', - color: const Color(0xFF14181B), - fontSize: 12, - letterSpacing: 0, - fontWeight: FontWeight.normal, - ), + textStyle: AppTheme.bodySmall, elevation: 0, borderSide: const BorderSide( color: Color(0xFFF1F4F8), @@ -174,15 +155,7 @@ class _PopupWidgetState extends State { iconPadding: const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), color: const Color(0xFF4B39EF), - textStyle: AppAppAppAppAppAppAppAppAppTheme - .titleSmall - .override( - fontFamily: 'Readex Pro', - color: Colors.white, - fontSize: 16, - letterSpacing: 0, - fontWeight: FontWeight.w500, - ), + textStyle: AppTheme.titleSmall, elevation: 3, borderSide: const BorderSide( color: Colors.transparent, diff --git a/mobile/app/lib/components/prompt_box/prompt_box_widget.dart b/mobile/app/lib/components/prompt_box/prompt_box_widget.dart index 77a7119..81688c0 100644 --- a/mobile/app/lib/components/prompt_box/prompt_box_widget.dart +++ b/mobile/app/lib/components/prompt_box/prompt_box_widget.dart @@ -4,6 +4,8 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; +import 'package:app/theme.dart'; + import 'prompt_box_model.dart'; export 'prompt_box_model.dart'; @@ -45,7 +47,7 @@ class _PromptBoxWidgetState extends State { child: Container( width: double.infinity, decoration: BoxDecoration( - color: AppAppAppAppAppAppAppAppAppTheme.secondaryBackground, + color: AppTheme.secondaryBackground, borderRadius: BorderRadius.circular(10), ), alignment: const AlignmentDirectional(0, 0), @@ -65,9 +67,9 @@ class _PromptBoxWidgetState extends State { borderRadius: 30, borderWidth: 1, buttonSize: 40, - icon: Icon( + icon: const Icon( Icons.file_upload_outlined, - color: AppAppAppAppAppAppAppAppAppTheme.secondaryText, + color: AppTheme.secondaryText, size: 30, ), onPressed: () { @@ -84,9 +86,9 @@ class _PromptBoxWidgetState extends State { borderRadius: 30, borderWidth: 1, buttonSize: 40, - icon: Icon( + icon: const Icon( Icons.photo_outlined, - color: AppAppAppAppAppAppAppAppAppTheme.secondaryText, + color: AppTheme.secondaryText, size: 30, ), onPressed: () { @@ -103,9 +105,9 @@ class _PromptBoxWidgetState extends State { borderRadius: 30, borderWidth: 1, buttonSize: 40, - icon: Icon( + icon: const Icon( Icons.mic_none, - color: AppAppAppAppAppAppAppAppAppTheme.secondaryText, + color: AppTheme.secondaryText, size: 30, ), onPressed: () { @@ -122,9 +124,9 @@ class _PromptBoxWidgetState extends State { borderRadius: 30, borderWidth: 1, buttonSize: 40, - icon: Icon( + icon: const Icon( Icons.videocam_outlined, - color: AppAppAppAppAppAppAppAppAppTheme.secondaryText, + color: AppTheme.secondaryText, size: 30, ), onPressed: () { @@ -141,9 +143,9 @@ class _PromptBoxWidgetState extends State { borderRadius: 30, borderWidth: 1, buttonSize: 40, - icon: Icon( + icon: const Icon( Icons.camera_alt_outlined, - color: AppAppAppAppAppAppAppAppAppTheme.secondaryText, + color: AppTheme.secondaryText, size: 30, ), onPressed: () { @@ -175,16 +177,10 @@ class _PromptBoxWidgetState extends State { obscureText: false, decoration: InputDecoration( isDense: false, - counterStyle: AppAppAppAppAppAppAppAppAppTheme - .bodyMedium - .override( - fontFamily: 'Readex Pro', - color: AppAppAppAppAppAppAppAppAppTheme.secondaryText, - letterSpacing: 0, - ), + counterStyle: AppTheme.bodyMedium, enabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: AppAppAppAppAppAppAppAppAppTheme.secondaryText, + borderSide: const BorderSide( + color: AppTheme.secondaryText, width: 2, ), borderRadius: BorderRadius.circular(12), @@ -211,19 +207,14 @@ class _PromptBoxWidgetState extends State { borderRadius: BorderRadius.circular(12), ), filled: true, - fillColor: AppAppAppAppAppAppAppAppAppTheme.alternate, - suffixIcon: Icon( + fillColor: AppTheme.alternate, + suffixIcon: const Icon( Icons.send, - color: AppAppAppAppAppAppAppAppAppTheme.primary, + color: AppTheme.primary, size: 30, ), ), - style: AppAppAppAppAppAppAppAppAppTheme.bodyMedium.override( - fontFamily: 'Lato', - fontSize: 14, - letterSpacing: 0, - fontWeight: FontWeight.normal, - ), + style: AppTheme.bodyMedium, textAlign: TextAlign.start, maxLines: 5, minLines: 1, diff --git a/mobile/app/lib/pages/conversation_widget.dart b/mobile/app/lib/pages/conversation_widget.dart index 76aa4cb..dc5990f 100644 --- a/mobile/app/lib/pages/conversation_widget.dart +++ b/mobile/app/lib/pages/conversation_widget.dart @@ -6,7 +6,7 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; -import '/theme.dart'; +import 'package:app/theme.dart'; import 'conversation_model.dart'; export 'conversation_model.dart'; @@ -44,37 +44,39 @@ class _ConversationWidgetState extends State { : FocusScope.of(context).unfocus(), child: Scaffold( key: scaffoldKey, - backgroundColor: myAppTheme.of(context).secondaryBackground, + backgroundColor: AppTheme.secondaryBackground, appBar: AppBar( - backgroundColor: myAppTheme.of(context).primaryBackground, + backgroundColor: AppTheme.primaryBackground, automaticallyImplyLeading: false, leading: FlutterFlowIconButton( borderColor: Colors.transparent, borderRadius: 30, borderWidth: 1, buttonSize: 60, - icon: Icon( + icon: const Icon( Icons.arrow_back_rounded, - color: AppTheme.primaryText, + color: AppTheme.primaryTextColor, size: 30, ), onPressed: () async { - context.goNamed( - 'ConversationsList', - extra: { - kTransitionInfoKey: TransitionInfo( - hasTransition: true, - transitionType: PageTransitionType.leftToRight, - duration: const Duration(milliseconds: 250), - ), - }, - ); + print("something pressed in conversation_widget.dart"); + // TODO + // context.goNamed( + // 'ConversationsList', + // extra: { + // kTransitionInfoKey: TransitionInfo( + // hasTransition: true, + // transitionType: PageTransitionType.leftToRight, + // duration: const Duration(milliseconds: 250), + // ), + // }, + // ); }, ), title: wrapWithModel( model: _model.modelItemModel, updateCallback: () => setState(() {}), - child: ModelItemWidget(), + child: const ModelItemWidget(), ), actions: [ Padding( @@ -85,9 +87,9 @@ class _ConversationWidgetState extends State { borderWidth: 2, buttonSize: 40, fillColor: AppTheme.primaryBackground, - icon: Icon( + icon: const Icon( Icons.more_vert, - color: AppTheme.primaryText, + color: AppTheme.primaryTextColor, size: 24, ), onPressed: () async { @@ -104,7 +106,7 @@ class _ConversationWidgetState extends State { : FocusScope.of(context).unfocus(), child: Padding( padding: MediaQuery.viewInsetsOf(context), - child: ConversationDetailsOverlayWidget(), + child: const ConversationDetailsOverlayWidget(), ), ); }, @@ -121,7 +123,7 @@ class _ConversationWidgetState extends State { child: wrapWithModel( model: _model.conversationThreadModel, updateCallback: () => setState(() {}), - child: ConversationThreadWidget(), + child: const ConversationThreadWidget(), ), ), ), diff --git a/mobile/app/lib/pages/conversations_list_widget.dart b/mobile/app/lib/pages/conversations_list_widget.dart index 1a2eff3..c2fe95b 100644 --- a/mobile/app/lib/pages/conversations_list_widget.dart +++ b/mobile/app/lib/pages/conversations_list_widget.dart @@ -1,9 +1,13 @@ +import 'dart:nativewrappers/_internal/vm/lib/internal_patch.dart'; + import '/components/conversation_options/conversation_options_widget.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; +import 'package:app/theme.dart'; + import 'conversations_list_model.dart'; export 'conversations_list_model.dart'; @@ -60,21 +64,26 @@ class _ConversationsListWidgetState extends State { : FocusScope.of(context).unfocus(), child: Padding( padding: MediaQuery.viewInsetsOf(context), - child: ConversationOptionsWidget(), + child: const ConversationOptionsWidget(), ), ); }, ).then((value) => safeSetState(() {})); - context.pushNamed('Conversation'); + // TODO + //context.pushNamed('Conversation'); }, child: FloatingActionButton( - onPressed: () async { - context.pushNamed('Conversation'); + // TODO + // onPressed: () async { + // context.pushNamed('Conversation'); + // }, + onPressed: () { + print("fab pressed"); }, backgroundColor: AppTheme.primary, elevation: 8, - child: Icon( + child: const Icon( Icons.add, color: AppTheme.info, size: 24, @@ -84,12 +93,9 @@ class _ConversationsListWidgetState extends State { appBar: AppBar( backgroundColor: AppTheme.secondaryBackground, automaticallyImplyLeading: false, - title: Text( + title: const Text( 'My Chats', - style: AppTheme.headlineLarge.override( - fontFamily: 'Outfit', - letterSpacing: 0, - ), + style: AppTheme.headlineLarge, ), actions: const [], centerTitle: false, @@ -101,14 +107,11 @@ class _ConversationsListWidgetState extends State { mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Padding( - padding: const EdgeInsetsDirectional.fromSTEB(16, 0, 0, 0), + const Padding( + padding: EdgeInsetsDirectional.fromSTEB(16, 0, 0, 0), child: Text( 'AI conversations', - style: AppTheme.labelMedium.override( - fontFamily: 'Readex Pro', - letterSpacing: 0, - ), + style: AppTheme.labelMedium, ), ), Expanded( @@ -126,9 +129,10 @@ class _ConversationsListWidgetState extends State { focusColor: Colors.transparent, hoverColor: Colors.transparent, highlightColor: Colors.transparent, - onTap: () async { - context.pushNamed('Conversation'); - }, + // TODO + // onTap: () async { + // context.pushNamed('Conversation'); + // }, child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.start, @@ -137,12 +141,12 @@ class _ConversationsListWidgetState extends State { Container( width: 100, height: 100, - decoration: BoxDecoration( + decoration: const BoxDecoration( color: AppTheme .secondaryBackground, ), - child: Align( - alignment: const AlignmentDirectional(0, 0), + child: const Align( + alignment: AlignmentDirectional(0, 0), child: Icon( Icons.settings_outlined, color: AppTheme @@ -161,12 +165,12 @@ class _ConversationsListWidgetState extends State { minWidth: MediaQuery.sizeOf(context).width, ), - decoration: BoxDecoration( + decoration: const BoxDecoration( color: AppTheme .secondaryBackground, shape: BoxShape.rectangle, ), - child: Column( + child: const Column( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceEvenly, @@ -175,40 +179,27 @@ class _ConversationsListWidgetState extends State { children: [ Align( alignment: - const AlignmentDirectional(-1, 0), + AlignmentDirectional(-1, 0), child: Row( mainAxisSize: MainAxisSize.max, children: [ Text( 'Hello World', - style: - AppTheme - .headlineLarge - .override( - fontFamily: 'Outfit', - letterSpacing: 0, - ), + style: AppTheme.headlineLarge, ), ], ), ), Align( alignment: - const AlignmentDirectional(-1, 0), + AlignmentDirectional(-1, 0), child: Row( mainAxisSize: MainAxisSize.max, children: [ Expanded( child: Text( 'Hello World', - style: FlutterFlowAppTheme.of( - context) - .bodyMedium - .override( - fontFamily: - 'Readex Pro', - letterSpacing: 0, - ), + style: AppTheme.bodyMedium, ), ), ], @@ -222,11 +213,11 @@ class _ConversationsListWidgetState extends State { Container( width: 100, height: 100, - decoration: BoxDecoration( + decoration: const BoxDecoration( color: AppTheme .secondaryBackground, ), - child: Icon( + child: const Icon( Icons.chevron_right_rounded, color: AppTheme .secondaryText, diff --git a/mobile/app/lib/pages/image_details_widget.dart b/mobile/app/lib/pages/image_details_widget.dart index 2694247..6efcc8c 100644 --- a/mobile/app/lib/pages/image_details_widget.dart +++ b/mobile/app/lib/pages/image_details_widget.dart @@ -6,6 +6,7 @@ import 'package:flutter_animate/flutter_animate.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; +import 'package:app/theme.dart'; import 'image_details_model.dart'; export 'image_details_model.dart'; @@ -106,19 +107,20 @@ class _ImageDetailsWidgetState extends State appBar: AppBar( backgroundColor: AppTheme.secondaryBackground, automaticallyImplyLeading: false, - leading: FlutterFlowIconButton( + leading: const FlutterFlowIconButton( borderColor: Colors.transparent, borderRadius: 30, borderWidth: 1, buttonSize: 60, icon: Icon( Icons.arrow_back_rounded, - color: AppTheme.primaryText, + color: AppTheme.primaryTextColor, size: 30, ), - onPressed: () async { - context.pop(); - }, + // TODO + // onPressed: () async { + // context.pop(); + // }, ), actions: const [], centerTitle: false, @@ -199,47 +201,30 @@ class _ImageDetailsWidgetState extends State mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.end, children: [ - SelectionArea( + const SelectionArea( child: AutoSizeText( 'Thanks so much! We really do appreciate it!', textAlign: TextAlign.start, - style: AppTheme - .bodyMedium - .override( - fontFamily: 'Readex Pro', - letterSpacing: 0, - lineHeight: 1.5, - ), + style: AppTheme.bodyMedium, )), - Padding( - padding: const EdgeInsetsDirectional.fromSTEB( + const Padding( + padding: EdgeInsetsDirectional.fromSTEB( 0, 4, 0, 0), child: Text( 'Just Now', - style: AppTheme - .labelSmall - .override( - fontFamily: 'Readex Pro', - letterSpacing: 0, - ), + style: AppTheme.labelSmall, ), ), ].divide(const SizedBox(width: 4)), ), - Padding( + const Padding( padding: - const EdgeInsetsDirectional.fromSTEB(0, 4, 0, 0), + EdgeInsetsDirectional.fromSTEB(0, 4, 0, 0), child: SelectionArea( child: AutoSizeText( 'Thanks so much! We really do appreciate it!', textAlign: TextAlign.start, - style: AppTheme - .labelLarge - .override( - fontFamily: 'Readex Pro', - letterSpacing: 0, - lineHeight: 1.5, - ), + style: AppTheme.labelLarge, )), ), ], diff --git a/mobile/app/lib/pages/settings_widget.dart b/mobile/app/lib/pages/settings_widget.dart index 2558ae0..5c62546 100644 --- a/mobile/app/lib/pages/settings_widget.dart +++ b/mobile/app/lib/pages/settings_widget.dart @@ -6,6 +6,8 @@ import 'package:flutter_animate/flutter_animate.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; +import 'package:app/theme.dart'; + import 'settings_model.dart'; export 'settings_model.dart'; @@ -93,12 +95,9 @@ class _SettingsWidgetState extends State ? AppBar( backgroundColor: AppTheme.secondaryBackground, automaticallyImplyLeading: false, - title: Text( + title: const Text( 'My Profile', - style: AppTheme.headlineMedium.override( - fontFamily: 'Outfit', - letterSpacing: 0, - ), + style: AppTheme.headlineMedium, ), actions: const [], centerTitle: false, @@ -134,9 +133,9 @@ class _SettingsWidgetState extends State mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Padding( + const Padding( padding: - const EdgeInsetsDirectional.fromSTEB(16, 0, 16, 12), + EdgeInsetsDirectional.fromSTEB(16, 0, 16, 12), child: Row( mainAxisSize: MainAxisSize.max, children: [ @@ -146,22 +145,17 @@ class _SettingsWidgetState extends State size: 32, ), Padding( - padding: const EdgeInsetsDirectional.fromSTEB( + padding: EdgeInsetsDirectional.fromSTEB( 12, 0, 0, 0), child: Text( 'check.io', - style: AppTheme - .headlineMedium - .override( - fontFamily: 'Outfit', - letterSpacing: 0, - ), + style: AppTheme.headlineMedium, ), ), ], ), ), - Divider( + const Divider( height: 12, thickness: 2, color: AppTheme.alternate, @@ -171,17 +165,12 @@ class _SettingsWidgetState extends State mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Padding( - padding: const EdgeInsetsDirectional.fromSTEB( + const Padding( + padding: EdgeInsetsDirectional.fromSTEB( 16, 12, 0, 0), child: Text( 'Platform Navigation', - style: AppTheme - .labelMedium - .override( - fontFamily: 'Readex Pro', - letterSpacing: 0, - ), + style: AppTheme.labelMedium, ), ), Padding( @@ -198,32 +187,25 @@ class _SettingsWidgetState extends State borderRadius: BorderRadius.circular(12), shape: BoxShape.rectangle, ), - child: Padding( - padding: const EdgeInsetsDirectional.fromSTEB( + child: const Padding( + padding: EdgeInsetsDirectional.fromSTEB( 8, 0, 6, 0), child: Row( mainAxisSize: MainAxisSize.max, children: [ Icon( Icons.space_dashboard, - color: AppTheme - .primaryText, + color: AppTheme.primaryTextColor, size: 24, ), Padding( padding: - const EdgeInsetsDirectional.fromSTEB( + EdgeInsetsDirectional.fromSTEB( 12, 0, 0, 0), child: Text( 'Dashboard', style: - AppTheme - .bodyMedium - .override( - fontFamily: - 'Readex Pro', - letterSpacing: 0, - ), + AppTheme.bodyMedium, ), ), ], @@ -240,37 +222,28 @@ class _SettingsWidgetState extends State width: double.infinity, height: 44, decoration: BoxDecoration( - color: AppTheme - .primaryBackground, + color: AppTheme.primaryBackground, borderRadius: BorderRadius.circular(12), shape: BoxShape.rectangle, ), - child: Padding( - padding: const EdgeInsetsDirectional.fromSTEB( + child: const Padding( + padding: EdgeInsetsDirectional.fromSTEB( 8, 0, 6, 0), child: Row( mainAxisSize: MainAxisSize.max, children: [ Icon( Icons.forum_rounded, - color: AppTheme - .primaryText, + color: AppTheme.primaryTextColor, size: 24, ), Padding( padding: - const EdgeInsetsDirectional.fromSTEB( + EdgeInsetsDirectional.fromSTEB( 12, 0, 0, 0), child: Text( 'Chats', - style: - AppTheme - .bodyMedium - .override( - fontFamily: - 'Readex Pro', - letterSpacing: 0, - ), + style: AppTheme.bodyMedium, ), ), ], @@ -292,32 +265,24 @@ class _SettingsWidgetState extends State borderRadius: BorderRadius.circular(12), shape: BoxShape.rectangle, ), - child: Padding( - padding: const EdgeInsetsDirectional.fromSTEB( + child: const Padding( + padding: EdgeInsetsDirectional.fromSTEB( 8, 0, 6, 0), child: Row( mainAxisSize: MainAxisSize.max, children: [ Icon( Icons.work, - color: AppTheme - .primaryText, + color: AppTheme.primaryTextColor, size: 24, ), Padding( padding: - const EdgeInsetsDirectional.fromSTEB( + EdgeInsetsDirectional.fromSTEB( 12, 0, 0, 0), child: Text( 'Projects', - style: - AppTheme - .bodyMedium - .override( - fontFamily: - 'Readex Pro', - letterSpacing: 0, - ), + style: AppTheme.bodyMedium, ), ), ], @@ -345,26 +310,19 @@ class _SettingsWidgetState extends State child: Row( mainAxisSize: MainAxisSize.max, children: [ - Icon( + const Icon( Icons.receipt_rounded, color: AppTheme .primary, size: 24, ), - Expanded( + const Expanded( child: Padding( - padding: const EdgeInsetsDirectional + padding: EdgeInsetsDirectional .fromSTEB(12, 0, 0, 0), child: Text( 'Recent Orders', - style: - AppTheme - .bodyMedium - .override( - fontFamily: - 'Readex Pro', - letterSpacing: 0, - ), + style: AppTheme.bodyMedium, ), ), ), @@ -377,26 +335,15 @@ class _SettingsWidgetState extends State borderRadius: BorderRadius.circular(8), ), - child: Align( + child: const Align( alignment: - const AlignmentDirectional(0, 0), + AlignmentDirectional(0, 0), child: Padding( - padding: const EdgeInsetsDirectional + padding: EdgeInsetsDirectional .fromSTEB(8, 4, 8, 4), child: Text( '12', - style: FlutterFlowAppTheme.of( - context) - .bodyMedium - .override( - fontFamily: - 'Readex Pro', - color: - FlutterFlowAppTheme.of( - context) - .info, - letterSpacing: 0, - ), + style: AppTheme.bodyMedium, ), ), ), @@ -406,17 +353,12 @@ class _SettingsWidgetState extends State ), ), ), - Padding( - padding: const EdgeInsetsDirectional.fromSTEB( + const Padding( + padding: EdgeInsetsDirectional.fromSTEB( 16, 0, 0, 0), child: Text( 'Settings', - style: AppTheme - .labelMedium - .override( - fontFamily: 'Readex Pro', - letterSpacing: 0, - ), + style: AppTheme.labelMedium, ), ), Padding( @@ -433,32 +375,25 @@ class _SettingsWidgetState extends State borderRadius: BorderRadius.circular(12), shape: BoxShape.rectangle, ), - child: Padding( - padding: const EdgeInsetsDirectional.fromSTEB( + child: const Padding( + padding: EdgeInsetsDirectional.fromSTEB( 8, 0, 6, 0), child: Row( mainAxisSize: MainAxisSize.max, children: [ Icon( Icons.attach_money_rounded, - color: AppTheme - .primaryText, + color: AppTheme.primaryTextColor, size: 24, ), Padding( padding: - const EdgeInsetsDirectional.fromSTEB( + EdgeInsetsDirectional.fromSTEB( 12, 0, 0, 0), child: Text( 'Billing', style: - AppTheme - .bodyMedium - .override( - fontFamily: - 'Readex Pro', - letterSpacing: 0, - ), + AppTheme.bodyMedium, ), ), ], @@ -480,32 +415,25 @@ class _SettingsWidgetState extends State borderRadius: BorderRadius.circular(12), shape: BoxShape.rectangle, ), - child: Padding( - padding: const EdgeInsetsDirectional.fromSTEB( + child: const Padding( + padding: EdgeInsetsDirectional.fromSTEB( 8, 0, 6, 0), child: Row( mainAxisSize: MainAxisSize.max, children: [ Icon( Icons.wifi_tethering_rounded, - color: AppTheme - .primaryText, + color: AppTheme.primaryTextColor, size: 24, ), Padding( padding: - const EdgeInsetsDirectional.fromSTEB( + EdgeInsetsDirectional.fromSTEB( 12, 0, 0, 0), child: Text( 'Explore', style: - AppTheme - .bodyMedium - .override( - fontFamily: - 'Readex Pro', - letterSpacing: 0, - ), + AppTheme.bodyMedium, ), ), ], @@ -546,82 +474,42 @@ class _SettingsWidgetState extends State focusColor: Colors.transparent, hoverColor: Colors.transparent, highlightColor: Colors.transparent, - onTap: () async { - setDarkModeSetting( - context, AppThemeMode.light); - }, + //TODO + // onTap: () async { + // setDarkModeSetting( + // context, AppThemeMode.light); + // }, child: Container( width: 115, height: 100, decoration: BoxDecoration( - color: AppTheme.of(context) - .brightness == - Brightness.light - ? AppTheme - .secondaryBackground - : AppTheme - .primaryBackground, + color: AppTheme.primaryBackground, borderRadius: BorderRadius.circular(10), border: Border.all( color: valueOrDefault( - AppTheme.of(context) - .brightness == - Brightness.light - ? FlutterFlowAppTheme.of( - context) - .alternate - : FlutterFlowAppTheme.of( - context) - .primaryBackground, - AppTheme - .alternate, + AppTheme.primaryBackground, + AppTheme.alternate, ), width: 1, ), ), - child: Row( + child: const Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.wb_sunny_rounded, - color: AppTheme.of(context) - .brightness == - Brightness.light - ? FlutterFlowAppTheme.of( - context) - .primaryText - : FlutterFlowAppTheme.of( - context) - .secondaryText, + color: AppTheme.secondaryText, size: 16, ), Padding( - padding: const EdgeInsetsDirectional + padding: EdgeInsetsDirectional .fromSTEB(4, 0, 0, 0), child: Text( 'Light Mode', - style: - FlutterFlowAppTheme.of( - context) - .bodyMedium - .override( - fontFamily: - 'Readex Pro', - color: AppTheme.of(context) - .brightness == - Brightness - .light - ? FlutterFlowAppTheme.of( - context) - .primaryText - : FlutterFlowAppTheme.of( - context) - .secondaryText, - letterSpacing: 0, - ), + style: AppTheme.primaryText, ), ), ], @@ -635,82 +523,39 @@ class _SettingsWidgetState extends State focusColor: Colors.transparent, hoverColor: Colors.transparent, highlightColor: Colors.transparent, - onTap: () async { - setDarkModeSetting( - context, AppThemeMode.dark); - }, + // TODO + // onTap: () async { + // setDarkModeSetting( + // context, AppThemeMode.dark); + // }, child: Container( width: 115, height: 100, decoration: BoxDecoration( - color: AppTheme.of(context) - .brightness == - Brightness.dark - ? AppTheme - .secondaryBackground - : AppTheme - .primaryBackground, + color: AppTheme.primaryBackground, borderRadius: BorderRadius.circular(10), border: Border.all( - color: valueOrDefault( - AppTheme.of(context) - .brightness == - Brightness.dark - ? FlutterFlowAppTheme.of( - context) - .alternate - : FlutterFlowAppTheme.of( - context) - .primaryBackground, - AppTheme - .primaryBackground, - ), + color: AppTheme.primaryBackground, width: 1, ), ), - child: Row( + child: const Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.nightlight_round, - color: AppTheme.of(context) - .brightness == - Brightness.dark - ? FlutterFlowAppTheme.of( - context) - .primaryText - : FlutterFlowAppTheme.of( - context) - .secondaryText, + color: AppTheme.primaryTextColor, size: 16, ), Padding( - padding: const EdgeInsetsDirectional + padding: EdgeInsetsDirectional .fromSTEB(4, 0, 0, 0), child: Text( 'Dark Mode', - style: - FlutterFlowAppTheme.of( - context) - .bodyMedium - .override( - fontFamily: - 'Readex Pro', - color: AppTheme.of(context) - .brightness == - Brightness - .dark - ? FlutterFlowAppTheme.of( - context) - .primaryText - : FlutterFlowAppTheme.of( - context) - .secondaryText, - letterSpacing: 0, - ), + style: AppTheme.bodyMedium, ), ), ], @@ -724,7 +569,7 @@ class _SettingsWidgetState extends State ), ), ), - Divider( + const Divider( height: 12, thickness: 2, color: AppTheme.alternate, @@ -765,9 +610,9 @@ class _SettingsWidgetState extends State ), ), ), - Expanded( + const Expanded( child: Padding( - padding: const EdgeInsetsDirectional.fromSTEB( + padding: EdgeInsetsDirectional.fromSTEB( 12, 0, 0, 0), child: Column( mainAxisSize: MainAxisSize.max, @@ -776,21 +621,11 @@ class _SettingsWidgetState extends State children: [ Text( 'Casper Ghost', - style: AppTheme - .bodyLarge - .override( - fontFamily: 'Readex Pro', - letterSpacing: 0, - ), + style: AppTheme.bodyLarge, ), Text( 'admin@gmail.com', - style: AppTheme - .labelMedium - .override( - fontFamily: 'Readex Pro', - letterSpacing: 0, - ), + style: AppTheme.labelMedium, ), ], ), @@ -799,9 +634,9 @@ class _SettingsWidgetState extends State ], ), ), - Padding( + const Padding( padding: - const EdgeInsetsDirectional.fromSTEB(16, 0, 16, 0), + EdgeInsetsDirectional.fromSTEB(16, 0, 16, 0), child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.end, @@ -878,15 +713,11 @@ class _SettingsWidgetState extends State width: 72, height: 72, decoration: BoxDecoration( - color: FlutterFlowAppTheme.of( - context) - .accent1, + color: AppTheme.accent1, borderRadius: BorderRadius.circular(12), border: Border.all( - color: FlutterFlowAppTheme.of( - context) - .primary, + color: AppTheme.primary, width: 2, ), ), @@ -910,9 +741,9 @@ class _SettingsWidgetState extends State ), ), ), - Expanded( + const Expanded( child: Padding( - padding: const EdgeInsetsDirectional + padding: EdgeInsetsDirectional .fromSTEB(16, 4, 0, 4), child: Column( mainAxisSize: @@ -923,31 +754,16 @@ class _SettingsWidgetState extends State children: [ Text( 'Casper Ghost', - style: FlutterFlowAppTheme - .of(context) - .headlineSmall - .override( - fontFamily: - 'Outfit', - letterSpacing: 0, - ), + style: AppTheme.headlineSmall, ), Padding( padding: - const EdgeInsetsDirectional + EdgeInsetsDirectional .fromSTEB( 0, 4, 0, 0), child: Text( 'casper@ghustbusters.com', - style: FlutterFlowAppTheme - .of(context) - .labelMedium - .override( - fontFamily: - 'Readex Pro', - letterSpacing: - 0, - ), + style: AppTheme.labelMedium, ), ), ], @@ -957,7 +773,7 @@ class _SettingsWidgetState extends State ], ), ), - Divider( + const Divider( height: 2, thickness: 1, color: AppTheme @@ -973,16 +789,14 @@ class _SettingsWidgetState extends State child: Container( width: 200, decoration: BoxDecoration( - color: FlutterFlowAppTheme.of( - context) - .secondaryBackground, + color: AppTheme.secondaryBackground, borderRadius: BorderRadius.circular( 8), ), - child: Padding( + child: const Padding( padding: - const EdgeInsetsDirectional + EdgeInsetsDirectional .fromSTEB( 12, 12, 12, 0), child: Column( @@ -994,44 +808,26 @@ class _SettingsWidgetState extends State children: [ Icon( Icons.receipt_rounded, - color: FlutterFlowAppTheme - .of(context) - .primary, + color: AppTheme.primary, size: 44, ), Padding( padding: - const EdgeInsetsDirectional + EdgeInsetsDirectional .fromSTEB(0, 8, 0, 4), child: Text( '2,200', textAlign: TextAlign .center, - style: FlutterFlowAppTheme - .of(context) - .headlineSmall - .override( - fontFamily: - 'Outfit', - letterSpacing: - 0, - ), + style: AppTheme.headlineSmall, ), ), Text( 'Orders Placed', textAlign: TextAlign.center, - style: FlutterFlowAppTheme - .of(context) - .labelMedium - .override( - fontFamily: - 'Readex Pro', - letterSpacing: - 0, - ), + style: AppTheme.labelMedium, ), ], ), @@ -1047,16 +843,14 @@ class _SettingsWidgetState extends State child: Container( width: 200, decoration: BoxDecoration( - color: FlutterFlowAppTheme.of( - context) - .secondaryBackground, + color: AppTheme.secondaryBackground, borderRadius: BorderRadius.circular( 8), ), - child: Padding( + child: const Padding( padding: - const EdgeInsetsDirectional + EdgeInsetsDirectional .fromSTEB( 12, 12, 12, 0), child: Column( @@ -1069,44 +863,26 @@ class _SettingsWidgetState extends State Icon( Icons .ssid_chart_rounded, - color: FlutterFlowAppTheme - .of(context) - .primary, + color: AppTheme.primary, size: 44, ), Padding( padding: - const EdgeInsetsDirectional + EdgeInsetsDirectional .fromSTEB(0, 8, 0, 4), child: Text( '\$212.4k', textAlign: TextAlign .center, - style: FlutterFlowAppTheme - .of(context) - .headlineSmall - .override( - fontFamily: - 'Outfit', - letterSpacing: - 0, - ), + style: AppTheme.headlineSmall, ), ), Text( 'Money Earned', textAlign: TextAlign.center, - style: FlutterFlowAppTheme - .of(context) - .labelMedium - .override( - fontFamily: - 'Readex Pro', - letterSpacing: - 0, - ), + style: AppTheme.labelMedium, ), ], ), @@ -1144,20 +920,14 @@ class _SettingsWidgetState extends State .alternate, ), ), - child: Padding( + child: const Padding( padding: - const EdgeInsetsDirectional.fromSTEB( + EdgeInsetsDirectional.fromSTEB( 16, 12, 0, 12), child: Text( 'My Account Information', style: - AppTheme - .labelMedium - .override( - fontFamily: - 'Readex Pro', - letterSpacing: 0, - ), + AppTheme.labelMedium, ), ), ), @@ -1168,17 +938,15 @@ class _SettingsWidgetState extends State child: Container( width: double.infinity, height: 60, - decoration: BoxDecoration( + decoration: const BoxDecoration( color: AppTheme .secondaryBackground, boxShadow: [ BoxShadow( blurRadius: 0, - color: FlutterFlowAppTheme.of( - context) - .alternate, - offset: const Offset( + color: AppTheme.alternate, + offset: Offset( 0, 1, ), @@ -1186,8 +954,8 @@ class _SettingsWidgetState extends State ], shape: BoxShape.rectangle, ), - child: Padding( - padding: const EdgeInsetsDirectional + child: const Padding( + padding: EdgeInsetsDirectional .fromSTEB(16, 0, 16, 0), child: Column( mainAxisSize: MainAxisSize.max, @@ -1200,26 +968,17 @@ class _SettingsWidgetState extends State children: [ Text( 'Change Password', - style: FlutterFlowAppTheme - .of(context) - .bodyLarge - .override( - fontFamily: - 'Readex Pro', - letterSpacing: 0, - ), + style: AppTheme.bodyLarge, ), Expanded( child: Align( alignment: - const AlignmentDirectional( + AlignmentDirectional( 1, 0), child: Icon( Icons .chevron_right_rounded, - color: FlutterFlowAppTheme - .of(context) - .secondaryText, + color: AppTheme.secondaryText, size: 20, ), ), @@ -1238,17 +997,15 @@ class _SettingsWidgetState extends State child: Container( width: double.infinity, height: 60, - decoration: BoxDecoration( + decoration: const BoxDecoration( color: AppTheme .secondaryBackground, boxShadow: [ BoxShadow( blurRadius: 0, - color: FlutterFlowAppTheme.of( - context) - .alternate, - offset: const Offset( + color: AppTheme.alternate, + offset: Offset( 0, 1, ), @@ -1256,8 +1013,8 @@ class _SettingsWidgetState extends State ], shape: BoxShape.rectangle, ), - child: Padding( - padding: const EdgeInsetsDirectional + child: const Padding( + padding: EdgeInsetsDirectional .fromSTEB(16, 0, 16, 0), child: Column( mainAxisSize: MainAxisSize.max, @@ -1270,31 +1027,17 @@ class _SettingsWidgetState extends State children: [ Text( 'Edit Profile', - style: - FlutterFlowAppTheme.of( - context) - .bodyLarge - .override( - fontFamily: - 'Readex Pro', - color: FlutterFlowAppTheme.of( - context) - .primaryText, - letterSpacing: - 0, - ), + style: AppTheme.bodyLarge, ), Expanded( child: Align( alignment: - const AlignmentDirectional( + AlignmentDirectional( 1, 0), child: Icon( Icons .chevron_right_rounded, - color: FlutterFlowAppTheme - .of(context) - .secondaryText, + color: AppTheme.secondaryText, size: 20, ), ), @@ -1324,19 +1067,13 @@ class _SettingsWidgetState extends State .alternate, ), ), - child: Padding( - padding: const EdgeInsetsDirectional + child: const Padding( + padding: EdgeInsetsDirectional .fromSTEB(16, 12, 0, 12), child: Text( 'Support', style: - AppTheme - .labelMedium - .override( - fontFamily: - 'Readex Pro', - letterSpacing: 0, - ), + AppTheme.labelMedium, ), ), ), @@ -1350,36 +1087,35 @@ class _SettingsWidgetState extends State focusColor: Colors.transparent, hoverColor: Colors.transparent, highlightColor: Colors.transparent, - onTap: () async { - context.pushNamed( - 'SupportForm', - extra: { - kTransitionInfoKey: - TransitionInfo( - hasTransition: true, - transitionType: - PageTransitionType - .rightToLeft, - duration: const Duration( - milliseconds: 250), - ), - }, - ); - }, + // TODO + // onTap: () async { + // context.pushNamed( + // 'SupportForm', + // extra: { + // kTransitionInfoKey: + // TransitionInfo( + // hasTransition: true, + // transitionType: + // PageTransitionType + // .rightToLeft, + // duration: const Duration( + // milliseconds: 250), + // ), + // }, + // ); + // }, child: Container( width: double.infinity, height: 60, - decoration: BoxDecoration( + decoration: const BoxDecoration( color: AppTheme .secondaryBackground, boxShadow: [ BoxShadow( blurRadius: 0, - color: FlutterFlowAppTheme.of( - context) - .alternate, - offset: const Offset( + color: AppTheme.alternate, + offset: Offset( 0, 1, ), @@ -1387,8 +1123,8 @@ class _SettingsWidgetState extends State ], shape: BoxShape.rectangle, ), - child: Padding( - padding: const EdgeInsetsDirectional + child: const Padding( + padding: EdgeInsetsDirectional .fromSTEB(16, 0, 16, 0), child: Column( mainAxisSize: @@ -1402,30 +1138,16 @@ class _SettingsWidgetState extends State children: [ Text( 'Bug or Feature Request', - style: FlutterFlowAppTheme - .of(context) - .bodyLarge - .override( - fontFamily: - 'Readex Pro', - color: FlutterFlowAppTheme.of( - context) - .primaryText, - letterSpacing: - 0, - ), + style: AppTheme.bodyLarge, ), Expanded( child: Align( alignment: - const AlignmentDirectional( + AlignmentDirectional( 1, 0), child: Icon( - Icons - .chevron_right_rounded, - color: FlutterFlowAppTheme - .of(context) - .secondaryText, + Icons.chevron_right_rounded, + color: AppTheme.secondaryText, size: 20, ), ), @@ -1460,7 +1182,7 @@ class _SettingsWidgetState extends State padding: MediaQuery.viewInsetsOf( context), - child: PopupWidget(), + child: const PopupWidget(), ); }, ).then((value) => @@ -1469,17 +1191,15 @@ class _SettingsWidgetState extends State child: Container( width: double.infinity, height: 60, - decoration: BoxDecoration( + decoration: const BoxDecoration( color: AppTheme .secondaryBackground, boxShadow: [ BoxShadow( blurRadius: 0, - color: FlutterFlowAppTheme.of( - context) - .alternate, - offset: const Offset( + color: AppTheme.alternate, + offset: Offset( 0, 1, ), @@ -1487,8 +1207,8 @@ class _SettingsWidgetState extends State ], shape: BoxShape.rectangle, ), - child: Padding( - padding: const EdgeInsetsDirectional + child: const Padding( + padding: EdgeInsetsDirectional .fromSTEB(16, 0, 16, 0), child: Column( mainAxisSize: @@ -1502,27 +1222,16 @@ class _SettingsWidgetState extends State children: [ Text( 'Open-source licenses & other info', - style: FlutterFlowAppTheme - .of(context) - .bodyLarge - .override( - fontFamily: - 'Readex Pro', - letterSpacing: - 0, - ), + style: AppTheme.bodyLarge, ), Expanded( child: Align( alignment: - const AlignmentDirectional( + AlignmentDirectional( 1, 0), child: Icon( - Icons - .chevron_right_rounded, - color: FlutterFlowAppTheme - .of(context) - .secondaryText, + Icons.chevron_right_rounded, + color: AppTheme.secondaryText, size: 20, ), ), @@ -1553,9 +1262,7 @@ class _SettingsWidgetState extends State borderRadius: BorderRadius.circular(12), border: Border.all( - color: FlutterFlowAppTheme.of( - context) - .alternate, + color: AppTheme.alternate, width: 1, ), ), @@ -1577,52 +1284,28 @@ class _SettingsWidgetState extends State Colors.transparent, highlightColor: Colors.transparent, - onTap: () async { - setDarkModeSetting( - context, - AppThemeMode.light); - }, + // TODO + // onTap: () async { + // setDarkModeSetting( + // context, + // AppThemeMode.light); + // }, child: Container( width: 115, height: 100, decoration: BoxDecoration( - color: AppTheme.of(context) - .brightness == - Brightness - .light - ? FlutterFlowAppTheme.of( - context) - .secondaryBackground - : FlutterFlowAppTheme.of( - context) - .primaryBackground, + color: AppTheme.primaryBackground, borderRadius: BorderRadius .circular( 10), border: Border.all( - color: - valueOrDefault< - Color>( - AppTheme.of(context) - .brightness == - Brightness - .light - ? FlutterFlowAppTheme.of( - context) - .alternate - : FlutterFlowAppTheme.of( - context) - .primaryBackground, - FlutterFlowAppTheme.of( - context) - .alternate, - ), + color: AppTheme.primaryBackground, width: 1, ), ), - child: Row( + child: const Row( mainAxisSize: MainAxisSize .max, @@ -1633,21 +1316,12 @@ class _SettingsWidgetState extends State Icon( Icons .wb_sunny_rounded, - color: AppTheme.of(context) - .brightness == - Brightness - .light - ? FlutterFlowAppTheme.of( - context) - .primaryText - : FlutterFlowAppTheme.of( - context) - .secondaryText, + color: AppTheme.primaryTextColor, size: 16, ), Padding( padding: - const EdgeInsetsDirectional + EdgeInsetsDirectional .fromSTEB( 4, 0, @@ -1655,19 +1329,7 @@ class _SettingsWidgetState extends State 0), child: Text( 'Light ', - style: FlutterFlowAppTheme.of( - context) - .bodyMedium - .override( - fontFamily: - 'Readex Pro', - color: AppTheme.of(context).brightness == - Brightness.light - ? AppTheme.primaryText - : AppTheme.secondaryText, - letterSpacing: - 0, - ), + style: AppTheme.primaryText, ), ), ], @@ -1685,45 +1347,28 @@ class _SettingsWidgetState extends State Colors.transparent, highlightColor: Colors.transparent, - onTap: () async { - setDarkModeSetting( - context, - AppThemeMode.dark); - }, + // TODO + // onTap: () async { + // setDarkModeSetting( + // context, + // AppThemeMode.dark); + // }, child: Container( width: 115, height: 100, decoration: BoxDecoration( - color: AppTheme.of(context) - .brightness == - Brightness - .dark - ? FlutterFlowAppTheme.of( - context) - .secondaryBackground - : FlutterFlowAppTheme.of( - context) - .primaryBackground, + color: AppTheme.primaryBackground, borderRadius: BorderRadius .circular( 10), border: Border.all( - color: AppTheme.of(context) - .brightness == - Brightness - .dark - ? FlutterFlowAppTheme.of( - context) - .alternate - : FlutterFlowAppTheme.of( - context) - .primaryBackground, + color: AppTheme.primaryBackground, width: 1, ), ), - child: Row( + child: const Row( mainAxisSize: MainAxisSize .max, @@ -1734,21 +1379,12 @@ class _SettingsWidgetState extends State Icon( Icons .nightlight_round, - color: AppTheme.of(context) - .brightness == - Brightness - .dark - ? FlutterFlowAppTheme.of( - context) - .primaryText - : FlutterFlowAppTheme.of( - context) - .secondaryText, + color: AppTheme.secondaryText, size: 16, ), Padding( padding: - const EdgeInsetsDirectional + EdgeInsetsDirectional .fromSTEB( 4, 0, @@ -1756,19 +1392,7 @@ class _SettingsWidgetState extends State 0), child: Text( 'Dark', - style: FlutterFlowAppTheme.of( - context) - .bodyMedium - .override( - fontFamily: - 'Readex Pro', - color: AppTheme.of(context).brightness == - Brightness.dark - ? AppTheme.primaryText - : AppTheme.secondaryText, - letterSpacing: - 0, - ), + style: AppTheme.bodyMedium, ), ), ], diff --git a/mobile/app/lib/pages/setup_widget.dart b/mobile/app/lib/pages/setup_widget.dart index 8483283..94dd717 100644 --- a/mobile/app/lib/pages/setup_widget.dart +++ b/mobile/app/lib/pages/setup_widget.dart @@ -4,6 +4,8 @@ import 'package:flutter_animate/flutter_animate.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; +import 'package:app/theme.dart'; + import 'setup_model.dart'; export 'setup_model.dart'; @@ -155,30 +157,30 @@ class _SetupWidgetState extends State child: Container( width: double.infinity, height: 500, - decoration: BoxDecoration( + decoration: const BoxDecoration( gradient: LinearGradient( colors: [ AppTheme.primary, AppTheme.error, AppTheme.tertiary ], - stops: const [0, 0.5, 1], - begin: const AlignmentDirectional(-1, -1), - end: const AlignmentDirectional(1, 1), + stops: [0, 0.5, 1], + begin: AlignmentDirectional(-1, -1), + end: AlignmentDirectional(1, 1), ), ), child: Container( width: 100, height: 100, - decoration: BoxDecoration( + decoration: const BoxDecoration( gradient: LinearGradient( colors: [ - const Color(0x00FFFFFF), + Color(0x00FFFFFF), AppTheme.secondaryBackground ], - stops: const [0, 1], - begin: const AlignmentDirectional(0, -1), - end: const AlignmentDirectional(0, 1), + stops: [0, 1], + begin: AlignmentDirectional(0, -1), + end: AlignmentDirectional(0, 1), ), ), child: Column( @@ -188,7 +190,7 @@ class _SetupWidgetState extends State Container( width: 120, height: 120, - decoration: BoxDecoration( + decoration: const BoxDecoration( color: AppTheme.accent4, shape: BoxShape.circle, ), @@ -205,27 +207,18 @@ class _SetupWidgetState extends State animationsMap['containerOnPageLoadAnimation2']!), Padding( padding: const EdgeInsetsDirectional.fromSTEB(0, 44, 0, 0), - child: Text( + child: const Text( 'Welcome!', - style: AppTheme - .displaySmall - .override( - fontFamily: 'Outfit', - letterSpacing: 0, - ), + style: AppTheme.displaySmall, ).animateOnPageLoad( animationsMap['textOnPageLoadAnimation1']!), ), Padding( padding: const EdgeInsetsDirectional.fromSTEB(44, 8, 44, 0), - child: Text( + child: const Text( 'Thanks for joining! Access or create your account below, and get started on your journey!', textAlign: TextAlign.center, - style: - AppTheme.labelMedium.override( - fontFamily: 'Readex Pro', - letterSpacing: 0, - ), + style: AppTheme.labelMedium, ).animateOnPageLoad( animationsMap['textOnPageLoadAnimation2']!), ), @@ -258,13 +251,9 @@ class _SetupWidgetState extends State const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), color: AppTheme .secondaryBackground, - textStyle: - AppTheme.bodyLarge.override( - fontFamily: 'Readex Pro', - letterSpacing: 0, - ), + textStyle: AppTheme.bodyLarge, elevation: 0, - borderSide: BorderSide( + borderSide: const BorderSide( color: AppTheme.alternate, width: 2, ), @@ -291,13 +280,7 @@ class _SetupWidgetState extends State iconPadding: const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), color: AppTheme.primary, - textStyle: AppTheme - .titleSmall - .override( - fontFamily: 'Readex Pro', - color: Colors.white, - letterSpacing: 0, - ), + textStyle: AppTheme.titleSmall, elevation: 3, borderSide: const BorderSide( color: Colors.transparent, diff --git a/mobile/app/lib/pages/support_form_widget.dart b/mobile/app/lib/pages/support_form_widget.dart index 402285e..5037056 100644 --- a/mobile/app/lib/pages/support_form_widget.dart +++ b/mobile/app/lib/pages/support_form_widget.dart @@ -131,11 +131,11 @@ class _SupportFormWidgetState extends State buttonSize: 60, icon: const Icon( Icons.arrow_back_rounded, - color: AppTheme.primaryText, + color: AppTheme.primaryTextColor, size: 30, ), onPressed: () async { - context.pop(); + //TODO: context.pop(); }, ), title: const Text( diff --git a/mobile/app/lib/theme.dart b/mobile/app/lib/theme.dart index 42fbdec..b947a32 100644 --- a/mobile/app/lib/theme.dart +++ b/mobile/app/lib/theme.dart @@ -6,19 +6,32 @@ class AppTheme { static const tertiary = Colors.white; static const alternate = Colors.purple; + static const accent1 = Colors.amber; + static const accent4 = Colors.amber; + static const primaryBackground = Colors.purple; static const secondaryBackground = Colors.purpleAccent; - static const primaryText = Colors.white; + static const primaryTextColor = Colors.white; + static const primaryText = TextStyle(color: primaryTextColor, fontSize: 12); static const secondaryText = Colors.white; + + static const bodyLarge = TextStyle(color: Colors.white, fontSize: 24); static const bodyMedium = TextStyle(color: Colors.white, fontSize: 12); + static const bodySmall = TextStyle(color: Colors.white, fontSize: 10); static const error = Colors.red; + static const info = Colors.grey; + + static const displaySmall = TextStyle(color: Colors.white, fontSize: 12); - static const labelMedium = TextStyle(color: Colors.grey, fontSize: 16); static const labelLarge = TextStyle(color: Colors.black, fontSize: 24); + static const labelMedium = TextStyle(color: Colors.grey, fontSize: 16); + static const labelSmall = TextStyle(color: Colors.grey, fontSize: 12); + static const headlineLarge = TextStyle(color: Colors.blue, fontWeight: FontWeight.bold); static const headlineMedium = TextStyle(color: Colors.blue, fontWeight: FontWeight.bold); + static const headlineSmall = TextStyle(color: Colors.blue, fontWeight: FontWeight.bold); static const titleLarge = TextStyle(fontFamily: 'Outfit', letterSpacing: 0, fontSize: 24); static const titleSmall = TextStyle(fontFamily: 'Outfit', letterSpacing: 0, fontSize: 16); diff --git a/mobile/app/macos/Flutter/Flutter-Debug.xcconfig b/mobile/app/macos/Flutter/Flutter-Debug.xcconfig index c2efd0b..4b81f9b 100644 --- a/mobile/app/macos/Flutter/Flutter-Debug.xcconfig +++ b/mobile/app/macos/Flutter/Flutter-Debug.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "ephemeral/Flutter-Generated.xcconfig" diff --git a/mobile/app/macos/Flutter/Flutter-Release.xcconfig b/mobile/app/macos/Flutter/Flutter-Release.xcconfig index c2efd0b..5caa9d1 100644 --- a/mobile/app/macos/Flutter/Flutter-Release.xcconfig +++ b/mobile/app/macos/Flutter/Flutter-Release.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "ephemeral/Flutter-Generated.xcconfig" diff --git a/mobile/app/macos/Flutter/GeneratedPluginRegistrant.swift b/mobile/app/macos/Flutter/GeneratedPluginRegistrant.swift index 40a1f70..513efaa 100644 --- a/mobile/app/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/mobile/app/macos/Flutter/GeneratedPluginRegistrant.swift @@ -10,6 +10,7 @@ import rive_common import shared_preferences_foundation import sqflite import url_launcher_macos +import video_player_avfoundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) @@ -17,4 +18,5 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) + FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin")) } diff --git a/mobile/app/macos/Podfile b/mobile/app/macos/Podfile new file mode 100644 index 0000000..c795730 --- /dev/null +++ b/mobile/app/macos/Podfile @@ -0,0 +1,43 @@ +platform :osx, '10.14' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/mobile/app/pubspec.lock b/mobile/app/pubspec.lock index 664298d..ac7fad9 100644 --- a/mobile/app/pubspec.lock +++ b/mobile/app/pubspec.lock @@ -524,18 +524,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.4" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -572,10 +572,10 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" maybe_just_nothing: dependency: transitive description: @@ -588,10 +588,10 @@ packages: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: "25dfcaf170a0190f47ca6355bdd4552cb8924b430512ff0cafb8db9bd41fe33b" url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.14.0" mime_type: dependency: transitive description: @@ -1001,10 +1001,10 @@ packages: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "2419f20b0c8677b2d67c8ac4d1ac7372d862dc6c460cdbb052b40155408cd794" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.1" timeago: dependency: transitive description: @@ -1101,14 +1101,54 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + video_player: + dependency: "direct main" + description: + name: video_player + sha256: e30df0d226c4ef82e2c150ebf6834b3522cf3f654d8e2f9419d376cdc071425d + url: "https://pub.dev" + source: hosted + version: "2.9.1" + video_player_android: + dependency: transitive + description: + name: video_player_android + sha256: fdc0331ce9f808cc2714014cb8126bd6369943affefd54f8fdab0ea0bb617b7f + url: "https://pub.dev" + source: hosted + version: "2.5.2" + video_player_avfoundation: + dependency: transitive + description: + name: video_player_avfoundation + sha256: d1e9a824f2b324000dc8fb2dcb2a3285b6c1c7c487521c63306cc5b394f68a7c + url: "https://pub.dev" + source: hosted + version: "2.6.1" + video_player_platform_interface: + dependency: transitive + description: + name: video_player_platform_interface + sha256: "236454725fafcacf98f0f39af0d7c7ab2ce84762e3b63f2cbb3ef9a7e0550bc6" + url: "https://pub.dev" + source: hosted + version: "6.2.2" + video_player_web: + dependency: transitive + description: + name: video_player_web + sha256: ff4d69a6614b03f055397c27a71c9d3ddea2b2a23d71b2ba0164f59ca32b8fe2 + url: "https://pub.dev" + source: hosted + version: "2.3.1" vm_service: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: "7475cb4dd713d57b6f7464c0e13f06da0d535d8b2067e188962a59bac2cf280b" url: "https://pub.dev" source: hosted - version: "14.2.1" + version: "14.2.2" web: dependency: transitive description: diff --git a/mobile/app/pubspec.yaml b/mobile/app/pubspec.yaml index a8ff033..7fe9796 100644 --- a/mobile/app/pubspec.yaml +++ b/mobile/app/pubspec.yaml @@ -43,6 +43,7 @@ dependencies: google_fonts: any provider: any easy_debounce: any + video_player: ^2.9.1 dev_dependencies: flutter_test: sdk: flutter diff --git a/one-ai-mobile b/one-ai-mobile new file mode 160000 index 0000000..dd29a00 --- /dev/null +++ b/one-ai-mobile @@ -0,0 +1 @@ +Subproject commit dd29a001e9eea051ccfc5b1061edbea077e8f723 diff --git a/output.txt b/output.txt new file mode 100644 index 0000000..e69de29 From 48539cb1176f6dc9b6c67c843b896c1fc067445b Mon Sep 17 00:00:00 2001 From: Joshua Date: Sat, 6 Jul 2024 13:02:57 -0600 Subject: [PATCH 06/24] added prototype GUI submodule --- .gitmodules | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .gitmodules diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..40f0f15 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "one-ai-mobile"] + path = one-ai-mobile + url = https://github.com/quaxlyqueen/one-ai-mobile From 7b0ade5735358784ed9ec9b9af16fd4d9777120c Mon Sep 17 00:00:00 2001 From: Joshua Date: Sat, 6 Jul 2024 14:17:54 -0600 Subject: [PATCH 07/24] app runs! colors are whack and many things are broken. --- mobile/app/android/app/build.gradle | 2 +- mobile/app/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/app/android/app/build.gradle b/mobile/app/android/app/build.gradle index bf2cdd3..d277963 100644 --- a/mobile/app/android/app/build.gradle +++ b/mobile/app/android/app/build.gradle @@ -35,7 +35,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId = "com.example.app" + applicationId = "com.example.app" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. minSdk = flutter.minSdkVersion diff --git a/mobile/app/pubspec.yaml b/mobile/app/pubspec.yaml index 7fe9796..3fa09a3 100644 --- a/mobile/app/pubspec.yaml +++ b/mobile/app/pubspec.yaml @@ -1,5 +1,5 @@ name: app -description: "A new Flutter project." +description: "Front-end app for One AI." # 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 From 766591546fe1be78cdda5eacdbf4d4f2daf6caed Mon Sep 17 00:00:00 2001 From: Joshua Date: Sat, 6 Jul 2024 15:25:43 -0600 Subject: [PATCH 08/24] Re-implemented bottom navigation bar w/ successful page changes. --- mobile/app/lib/main.dart | 82 +++- .../lib/pages/conversations_list_widget.dart | 363 +++++++++--------- mobile/app/lib/theme.dart | 6 +- mobile/app/pubspec.lock | 8 +- mobile/app/pubspec.yaml | 2 +- 5 files changed, 263 insertions(+), 198 deletions(-) diff --git a/mobile/app/lib/main.dart b/mobile/app/lib/main.dart index 501f923..76fe504 100644 --- a/mobile/app/lib/main.dart +++ b/mobile/app/lib/main.dart @@ -1,21 +1,91 @@ import 'package:app/pages/settings_widget.dart'; +import 'package:app/pages/conversations_list_widget.dart'; import 'package:flutter/material.dart'; -import 'theme.dart'; +import 'package:provider/provider.dart'; +import 'package:app/theme.dart'; + +// my_state.dart (Provider for selected index) +final _selectedIndex = ValueNotifier(0); + +class MyState with ChangeNotifier { + int get selectedIndex => _selectedIndex.value; + + set selectedIndex(int value) { + _selectedIndex.value = value; + notifyListeners(); + } +} + +// This function creates a BottomNavigationBarItem for each page +BottomNavigationBarItem _buildNavigationItem(int index) { + String label = ""; + IconData iconData = Icons.error; + switch (index) { + case 0: + label = 'Home'; + iconData = Icons.home; + break; + case 1: + label = 'Settings'; + iconData = Icons.settings; + break; + } + return BottomNavigationBarItem( + icon: Icon(iconData), + label: label, + ); +} + +// main.dart void main() { - runApp(const MyApp()); + runApp( + ChangeNotifierProvider( + create: (_) => MyState(), + child: MyApp(), + ), + ); } class MyApp extends StatelessWidget { - const MyApp({super.key}); + MyApp({super.key}); + + // This list holds the pages to navigate between + final List _pages = [ + // Replace with the widget for your first page (e.g., HomeScreen()) + const Center(child: ConversationsListWidget()), + // Replace with the widget for your second page (e.g., SettingsPage()) + const Center(child: SettingsWidget()), + ]; // This widget is the root of your application. @override Widget build(BuildContext context) { + int selectedIndex = Provider.of(context).selectedIndex; - return const MaterialApp( - title: 'Flutter Demo', - home: SettingsWidget(), + return MaterialApp( + title: 'App', + home: Scaffold( + backgroundColor: AppTheme.secondaryBackground, + appBar: AppBar( + backgroundColor: AppTheme.secondaryBackground, + automaticallyImplyLeading: false, + title: const Text( + 'App', + style: AppTheme.headlineLarge, + ), + actions: const [], + centerTitle: false, + elevation: 0, + ), + body: _pages[Provider.of(context, listen: false).selectedIndex], + bottomNavigationBar: BottomNavigationBar( + // Set the number of items to the length of the pages list + items: List.generate(_pages.length, (index) => _buildNavigationItem(index)), + currentIndex: selectedIndex, + onTap: (index) => Provider.of(context, listen: false).selectedIndex = index, // Update using Provider + ), + ), ); } } diff --git a/mobile/app/lib/pages/conversations_list_widget.dart b/mobile/app/lib/pages/conversations_list_widget.dart index c2fe95b..a08c023 100644 --- a/mobile/app/lib/pages/conversations_list_widget.dart +++ b/mobile/app/lib/pages/conversations_list_widget.dart @@ -1,5 +1,3 @@ -import 'dart:nativewrappers/_internal/vm/lib/internal_patch.dart'; - import '/components/conversation_options/conversation_options_widget.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:flutter/material.dart'; @@ -43,198 +41,195 @@ class _ConversationsListWidgetState extends State { onTap: () => _model.unfocusNode.canRequestFocus ? FocusScope.of(context).requestFocus(_model.unfocusNode) : FocusScope.of(context).unfocus(), - child: Scaffold( - key: scaffoldKey, - backgroundColor: AppTheme.secondaryBackground, - floatingActionButton: InkWell( - splashColor: Colors.transparent, - focusColor: Colors.transparent, - hoverColor: Colors.transparent, - highlightColor: Colors.transparent, - onLongPress: () async { - await showModalBottomSheet( - isScrollControlled: true, - backgroundColor: Colors.transparent, - enableDrag: false, - context: context, - builder: (context) { - return GestureDetector( - onTap: () => _model.unfocusNode.canRequestFocus - ? FocusScope.of(context).requestFocus(_model.unfocusNode) - : FocusScope.of(context).unfocus(), - child: Padding( - padding: MediaQuery.viewInsetsOf(context), - child: const ConversationOptionsWidget(), + child: SafeArea( + top: true, + child: Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Padding( + padding: EdgeInsetsDirectional.fromSTEB(16, 0, 0, 0), + child: Text( + 'AI conversations', + style: AppTheme.labelMedium, ), - ); - }, - ).then((value) => safeSetState(() {})); - - // TODO - //context.pushNamed('Conversation'); - }, - child: FloatingActionButton( - // TODO - // onPressed: () async { - // context.pushNamed('Conversation'); - // }, - onPressed: () { - print("fab pressed"); - }, - backgroundColor: AppTheme.primary, - elevation: 8, - child: const Icon( - Icons.add, - color: AppTheme.info, - size: 24, - ), - ), - ), - appBar: AppBar( - backgroundColor: AppTheme.secondaryBackground, - automaticallyImplyLeading: false, - title: const Text( - 'My Chats', - style: AppTheme.headlineLarge, - ), - actions: const [], - centerTitle: false, - elevation: 0, - ), - body: SafeArea( - top: true, - child: Column( - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Padding( - padding: EdgeInsetsDirectional.fromSTEB(16, 0, 0, 0), - child: Text( - 'AI conversations', - style: AppTheme.labelMedium, ), - ), - Expanded( - child: Align( - alignment: const AlignmentDirectional(0, 0), - child: ListView( - padding: EdgeInsets.zero, - reverse: true, - scrollDirection: Axis.vertical, - children: [ - Align( - alignment: const AlignmentDirectional(0, 0), - child: InkWell( - splashColor: Colors.transparent, - focusColor: Colors.transparent, - hoverColor: Colors.transparent, - highlightColor: Colors.transparent, - // TODO - // onTap: () async { - // context.pushNamed('Conversation'); - // }, - child: Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - width: 100, - height: 100, - decoration: const BoxDecoration( - color: AppTheme - .secondaryBackground, + Expanded( + child: Align( + alignment: const AlignmentDirectional(0, 0), + child: ListView( + padding: EdgeInsets.zero, + reverse: true, + scrollDirection: Axis.vertical, + children: [ + Align( + alignment: const AlignmentDirectional(0, 0), + child: InkWell( + splashColor: Colors.transparent, + focusColor: Colors.transparent, + hoverColor: Colors.transparent, + highlightColor: Colors.transparent, + // TODO + // onTap: () async { + // context.pushNamed('Conversation'); + // }, + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + width: 100, + height: 100, + decoration: const BoxDecoration( + color: AppTheme.secondaryBackground, + ), + child: const Align( + alignment: AlignmentDirectional(0, 0), + child: Icon( + Icons.settings_outlined, + color: AppTheme.secondaryText, + size: 48, + ), + ), ), - child: const Align( - alignment: AlignmentDirectional(0, 0), - child: Icon( - Icons.settings_outlined, - color: AppTheme - .secondaryText, + Expanded( + child: Align( + alignment: const AlignmentDirectional(-1, 0), + child: Container( + width: 100, + height: 100, + constraints: BoxConstraints( + minWidth: + MediaQuery.sizeOf(context).width, + ), + decoration: const BoxDecoration( + color: AppTheme.secondaryBackground, + shape: BoxShape.rectangle, + ), + child: const Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.spaceEvenly, + crossAxisAlignment: + CrossAxisAlignment.center, + children: [ + Align( + alignment: + AlignmentDirectional(-1, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Text( + 'Hello World', + style: AppTheme.headlineLarge, + ), + ], + ), + ), + Align( + alignment: + AlignmentDirectional(-1, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Expanded( + child: Text( + 'Hello World', + style: AppTheme.bodyMedium, + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + Container( + width: 100, + height: 100, + decoration: const BoxDecoration( + color: AppTheme.secondaryBackground, + ), + child: const Icon( + Icons.chevron_right_rounded, + color: AppTheme.secondaryText, size: 48, ), ), - ), - Expanded( - child: Align( - alignment: const AlignmentDirectional(-1, 0), - child: Container( - width: 100, - height: 100, - constraints: BoxConstraints( - minWidth: - MediaQuery.sizeOf(context).width, - ), - decoration: const BoxDecoration( - color: AppTheme - .secondaryBackground, - shape: BoxShape.rectangle, - ), - child: const Column( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: - MainAxisAlignment.spaceEvenly, - crossAxisAlignment: - CrossAxisAlignment.center, - children: [ - Align( - alignment: - AlignmentDirectional(-1, 0), - child: Row( - mainAxisSize: MainAxisSize.max, - children: [ - Text( - 'Hello World', - style: AppTheme.headlineLarge, - ), - ], - ), - ), - Align( - alignment: - AlignmentDirectional(-1, 0), - child: Row( - mainAxisSize: MainAxisSize.max, - children: [ - Expanded( - child: Text( - 'Hello World', - style: AppTheme.bodyMedium, - ), - ), - ], - ), - ), - ], - ), - ), - ), - ), - Container( - width: 100, - height: 100, - decoration: const BoxDecoration( - color: AppTheme - .secondaryBackground, - ), - child: const Icon( - Icons.chevron_right_rounded, - color: AppTheme - .secondaryText, - size: 48, - ), - ), - ], + ], + ), ), ), - ), - ], + ], + ), ), ), - ), - ], - ), - ), + ], + ), + // child: Scaffold( + // key: scaffoldKey, + // backgroundColor: AppTheme.secondaryBackground, + // floatingActionButton: InkWell( + // splashColor: Colors.transparent, + // focusColor: Colors.transparent, + // hoverColor: Colors.transparent, + // highlightColor: Colors.transparent, + // onLongPress: () async { + // await showModalBottomSheet( + // isScrollControlled: true, + // backgroundColor: Colors.transparent, + // enableDrag: false, + // context: context, + // builder: (context) { + // return GestureDetector( + // onTap: () => _model.unfocusNode.canRequestFocus + // ? FocusScope.of(context).requestFocus(_model.unfocusNode) + // : FocusScope.of(context).unfocus(), + // child: Padding( + // padding: MediaQuery.viewInsetsOf(context), + // child: const ConversationOptionsWidget(), + // ), + // ); + // }, + // ).then((value) => safeSetState(() {})); + // + // // TODO + // //context.pushNamed('Conversation'); + // }, + // child: FloatingActionButton( + // // TODO + // // onPressed: () async { + // // context.pushNamed('Conversation'); + // // }, + // onPressed: () { + // print("fab pressed"); + // }, + // backgroundColor: AppTheme.primaryBackground, + // elevation: 8, + // child: const Icon( + // Icons.add, + // color: AppTheme.info, + // size: 24, + // ), + // ), + // ), + // appBar: AppBar( + // backgroundColor: AppTheme.secondaryBackground, + // automaticallyImplyLeading: false, + // title: const Text( + // 'My Chats', + // style: AppTheme.headlineLarge, + // ), + // actions: const [], + // centerTitle: false, + // elevation: 0, + // ), + // body: SafeArea( + // + // ), ), ); } diff --git a/mobile/app/lib/theme.dart b/mobile/app/lib/theme.dart index b947a32..1d38ccd 100644 --- a/mobile/app/lib/theme.dart +++ b/mobile/app/lib/theme.dart @@ -4,13 +4,13 @@ class AppTheme { static const primary = Colors.deepPurple; static const secondary = Colors.deepPurpleAccent; static const tertiary = Colors.white; - static const alternate = Colors.purple; + static const alternate = Colors.deepPurple; static const accent1 = Colors.amber; static const accent4 = Colors.amber; - static const primaryBackground = Colors.purple; - static const secondaryBackground = Colors.purpleAccent; + static const primaryBackground = Colors.deepPurpleAccent; + static const secondaryBackground = Colors.deepPurple; static const primaryTextColor = Colors.white; static const primaryText = TextStyle(color: primaryTextColor, fontSize: 12); diff --git a/mobile/app/pubspec.lock b/mobile/app/pubspec.lock index ac7fad9..c58d091 100644 --- a/mobile/app/pubspec.lock +++ b/mobile/app/pubspec.lock @@ -314,10 +314,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1" + sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "4.0.0" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -548,10 +548,10 @@ packages: dependency: transitive description: name: lints - sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 + sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "4.0.0" mapbox_search: dependency: transitive description: diff --git a/mobile/app/pubspec.yaml b/mobile/app/pubspec.yaml index 3fa09a3..a5820ab 100644 --- a/mobile/app/pubspec.yaml +++ b/mobile/app/pubspec.yaml @@ -53,7 +53,7 @@ dev_dependencies: # 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: ^3.0.0 + flutter_lints: ^4.0.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec From 1d5bf55eb98151e7d7c249f9114140ac24e3cfc5 Mon Sep 17 00:00:00 2001 From: Joshua Date: Sat, 6 Jul 2024 15:37:40 -0600 Subject: [PATCH 09/24] switched to flutter_riverpod for state/page management. --- mobile/app/lib/main.dart | 17 +++++++++-------- mobile/app/pubspec.lock | 24 ++++++++++++++++++++++++ mobile/app/pubspec.yaml | 1 + 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/mobile/app/lib/main.dart b/mobile/app/lib/main.dart index 76fe504..fa58583 100644 --- a/mobile/app/lib/main.dart +++ b/mobile/app/lib/main.dart @@ -1,6 +1,7 @@ import 'package:app/pages/settings_widget.dart'; import 'package:app/pages/conversations_list_widget.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:provider/provider.dart'; import 'package:app/theme.dart'; @@ -37,19 +38,19 @@ BottomNavigationBarItem _buildNavigationItem(int index) { ); } -// main.dart void main() { runApp( - ChangeNotifierProvider( - create: (_) => MyState(), + ProviderScope( child: MyApp(), ), ); } -class MyApp extends StatelessWidget { +class MyApp extends ConsumerWidget { MyApp({super.key}); + final selectedIndexProvider = StateProvider((_) => 0); // Initial state + // This list holds the pages to navigate between final List _pages = [ // Replace with the widget for your first page (e.g., HomeScreen()) @@ -60,8 +61,8 @@ class MyApp extends StatelessWidget { // This widget is the root of your application. @override - Widget build(BuildContext context) { - int selectedIndex = Provider.of(context).selectedIndex; + Widget build(BuildContext context, WidgetRef ref) { + final selectedIndex = ref.watch(selectedIndexProvider); return MaterialApp( title: 'App', @@ -78,12 +79,12 @@ class MyApp extends StatelessWidget { centerTitle: false, elevation: 0, ), - body: _pages[Provider.of(context, listen: false).selectedIndex], + body: _pages[selectedIndex], bottomNavigationBar: BottomNavigationBar( // Set the number of items to the length of the pages list items: List.generate(_pages.length, (index) => _buildNavigationItem(index)), currentIndex: selectedIndex, - onTap: (index) => Provider.of(context, listen: false).selectedIndex = index, // Update using Provider + onTap: (index) => ref.read(selectedIndexProvider.notifier).state = index, ), ), ); diff --git a/mobile/app/pubspec.lock b/mobile/app/pubspec.lock index c58d091..8adbd99 100644 --- a/mobile/app/pubspec.lock +++ b/mobile/app/pubspec.lock @@ -326,6 +326,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.20" + flutter_riverpod: + dependency: "direct main" + description: + name: flutter_riverpod + sha256: "0f1974eff5bbe774bf1d870e406fc6f29e3d6f1c46bd9c58e7172ff68a785d7d" + url: "https://pub.dev" + source: hosted + version: "2.5.1" flutter_shaders: dependency: transitive description: @@ -800,6 +808,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.8" + riverpod: + dependency: transitive + description: + name: riverpod + sha256: f21b32ffd26a36555e501b04f4a5dca43ed59e16343f1a30c13632b2351dfa4d + url: "https://pub.dev" + source: hosted + version: "2.5.1" rxdart: dependency: transitive description: @@ -933,6 +949,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.11.1" + state_notifier: + dependency: transitive + description: + name: state_notifier + sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb + url: "https://pub.dev" + source: hosted + version: "1.0.0" stop_watch_timer: dependency: transitive description: diff --git a/mobile/app/pubspec.yaml b/mobile/app/pubspec.yaml index a5820ab..adfa4e0 100644 --- a/mobile/app/pubspec.yaml +++ b/mobile/app/pubspec.yaml @@ -44,6 +44,7 @@ dependencies: provider: any easy_debounce: any video_player: ^2.9.1 + flutter_riverpod: ^2.5.1 dev_dependencies: flutter_test: sdk: flutter From a9d4f619e331650536df5025be03236f0eab79f7 Mon Sep 17 00:00:00 2001 From: Joshua Date: Sat, 6 Jul 2024 16:06:46 -0600 Subject: [PATCH 10/24] partial implementation of riverpod for sub-page management in the conversations list --- mobile/app/lib/main.dart | 3 +- .../lib/pages/conversations_list_widget.dart | 101 ++++++------------ 2 files changed, 36 insertions(+), 68 deletions(-) diff --git a/mobile/app/lib/main.dart b/mobile/app/lib/main.dart index fa58583..68c6eaa 100644 --- a/mobile/app/lib/main.dart +++ b/mobile/app/lib/main.dart @@ -6,7 +6,6 @@ import 'package:provider/provider.dart'; import 'package:app/theme.dart'; -// my_state.dart (Provider for selected index) final _selectedIndex = ValueNotifier(0); class MyState with ChangeNotifier { @@ -54,7 +53,7 @@ class MyApp extends ConsumerWidget { // This list holds the pages to navigate between final List _pages = [ // Replace with the widget for your first page (e.g., HomeScreen()) - const Center(child: ConversationsListWidget()), + Center(child: ConversationsListWidget()), // Replace with the widget for your second page (e.g., SettingsPage()) const Center(child: SettingsWidget()), ]; diff --git a/mobile/app/lib/pages/conversations_list_widget.dart b/mobile/app/lib/pages/conversations_list_widget.dart index a08c023..7a197f7 100644 --- a/mobile/app/lib/pages/conversations_list_widget.dart +++ b/mobile/app/lib/pages/conversations_list_widget.dart @@ -1,3 +1,6 @@ +import 'package:app/pages/conversation_widget.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + import '/components/conversation_options/conversation_options_widget.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:flutter/material.dart'; @@ -9,6 +12,18 @@ import 'package:app/theme.dart'; import 'conversations_list_model.dart'; export 'conversations_list_model.dart'; +final conversationIndex = ValueNotifier(0); + + +class MyState with ChangeNotifier { + int get selectedIndex => conversationIndex.value; + + set selectedIndex(int value) { + conversationIndex.value = value; + notifyListeners(); + } +} + class ConversationsListWidget extends StatefulWidget { const ConversationsListWidget({super.key}); @@ -19,6 +34,15 @@ class ConversationsListWidget extends StatefulWidget { class _ConversationsListWidgetState extends State { late ConversationsListModel _model; + final selectedIndexProvider = StateProvider((_) => 0); // Initial state + + final List _pages = [ + // Replace with the widget for your first page (e.g., HomeScreen()) + Center(child: ConversationWidget()), + + // TODO: Dynamically generate list of pages based upon existing conversations. + // This requires retrievable conversations from the server side. + ]; final scaffoldKey = GlobalKey(); @@ -50,7 +74,7 @@ class _ConversationsListWidgetState extends State { const Padding( padding: EdgeInsetsDirectional.fromSTEB(16, 0, 0, 0), child: Text( - 'AI conversations', + 'Conversations', style: AppTheme.labelMedium, ), ), @@ -87,7 +111,7 @@ class _ConversationsListWidgetState extends State { child: const Align( alignment: AlignmentDirectional(0, 0), child: Icon( - Icons.settings_outlined, + Icons.settings_outlined, // TODO: Dynamically generate or allow users to color code/tag conversations. color: AppTheme.secondaryText, size: 48, ), @@ -121,7 +145,7 @@ class _ConversationsListWidgetState extends State { mainAxisSize: MainAxisSize.max, children: [ Text( - 'Hello World', + 'Hello World', // TODO: Dynamically obtain conversation title from server. style: AppTheme.headlineLarge, ), ], @@ -135,7 +159,7 @@ class _ConversationsListWidgetState extends State { children: [ Expanded( child: Text( - 'Hello World', + 'Hello World', // TODO: Summary or list of models used style: AppTheme.bodyMedium, ), ), @@ -161,6 +185,12 @@ class _ConversationsListWidgetState extends State { ), ], ), + onTap: () => { + print("opening existing conversation") + }, + onLongPress: () => { + print("opening options") + }, ), ), ], @@ -169,68 +199,7 @@ class _ConversationsListWidgetState extends State { ), ], ), - // child: Scaffold( - // key: scaffoldKey, - // backgroundColor: AppTheme.secondaryBackground, - // floatingActionButton: InkWell( - // splashColor: Colors.transparent, - // focusColor: Colors.transparent, - // hoverColor: Colors.transparent, - // highlightColor: Colors.transparent, - // onLongPress: () async { - // await showModalBottomSheet( - // isScrollControlled: true, - // backgroundColor: Colors.transparent, - // enableDrag: false, - // context: context, - // builder: (context) { - // return GestureDetector( - // onTap: () => _model.unfocusNode.canRequestFocus - // ? FocusScope.of(context).requestFocus(_model.unfocusNode) - // : FocusScope.of(context).unfocus(), - // child: Padding( - // padding: MediaQuery.viewInsetsOf(context), - // child: const ConversationOptionsWidget(), - // ), - // ); - // }, - // ).then((value) => safeSetState(() {})); - // - // // TODO - // //context.pushNamed('Conversation'); - // }, - // child: FloatingActionButton( - // // TODO - // // onPressed: () async { - // // context.pushNamed('Conversation'); - // // }, - // onPressed: () { - // print("fab pressed"); - // }, - // backgroundColor: AppTheme.primaryBackground, - // elevation: 8, - // child: const Icon( - // Icons.add, - // color: AppTheme.info, - // size: 24, - // ), - // ), - // ), - // appBar: AppBar( - // backgroundColor: AppTheme.secondaryBackground, - // automaticallyImplyLeading: false, - // title: const Text( - // 'My Chats', - // style: AppTheme.headlineLarge, - // ), - // actions: const [], - // centerTitle: false, - // elevation: 0, - // ), - // body: SafeArea( - // - // ), ), ); } -} +} \ No newline at end of file From a010fab5792fca1a6a189a251917d4d344d2b938 Mon Sep 17 00:00:00 2001 From: Joshua Date: Sat, 6 Jul 2024 18:04:21 -0600 Subject: [PATCH 11/24] documentation and todo items --- mobile/app/lib/main.dart | 2 +- .../lib/pages/conversations_list_model.dart | 4 ---- .../lib/pages/conversations_list_widget.dart | 22 +++++++++---------- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/mobile/app/lib/main.dart b/mobile/app/lib/main.dart index 68c6eaa..95974ea 100644 --- a/mobile/app/lib/main.dart +++ b/mobile/app/lib/main.dart @@ -53,7 +53,7 @@ class MyApp extends ConsumerWidget { // This list holds the pages to navigate between final List _pages = [ // Replace with the widget for your first page (e.g., HomeScreen()) - Center(child: ConversationsListWidget()), + const Center(child: ConversationsListWidget()), // Replace with the widget for your second page (e.g., SettingsPage()) const Center(child: SettingsWidget()), ]; diff --git a/mobile/app/lib/pages/conversations_list_model.dart b/mobile/app/lib/pages/conversations_list_model.dart index 96baaea..c4ad379 100644 --- a/mobile/app/lib/pages/conversations_list_model.dart +++ b/mobile/app/lib/pages/conversations_list_model.dart @@ -1,12 +1,8 @@ -import '/components/conversation_options/conversation_options_widget.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'conversations_list_widget.dart' show ConversationsListWidget; import 'package:flutter/material.dart'; -import 'package:google_fonts/google_fonts.dart'; -import 'package:provider/provider.dart'; class ConversationsListModel extends FlutterFlowModel { - /// State fields for stateful widgets in this page. final unfocusNode = FocusNode(); diff --git a/mobile/app/lib/pages/conversations_list_widget.dart b/mobile/app/lib/pages/conversations_list_widget.dart index 7a197f7..4f48bce 100644 --- a/mobile/app/lib/pages/conversations_list_widget.dart +++ b/mobile/app/lib/pages/conversations_list_widget.dart @@ -1,20 +1,19 @@ +import 'package:app/theme.dart'; + import 'package:app/pages/conversation_widget.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:app/components/conversation_options/conversation_options_widget.dart'; -import '/components/conversation_options/conversation_options_widget.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:flutter/material.dart'; -import 'package:google_fonts/google_fonts.dart'; -import 'package:provider/provider.dart'; - -import 'package:app/theme.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'conversations_list_model.dart'; export 'conversations_list_model.dart'; final conversationIndex = ValueNotifier(0); - +// State controller using flutter_riverpod API. +// This handles changing between different conversations. class MyState with ChangeNotifier { int get selectedIndex => conversationIndex.value; @@ -36,12 +35,10 @@ class _ConversationsListWidgetState extends State { late ConversationsListModel _model; final selectedIndexProvider = StateProvider((_) => 0); // Initial state - final List _pages = [ - // Replace with the widget for your first page (e.g., HomeScreen()) - Center(child: ConversationWidget()), - + final List conversations = [ // TODO: Dynamically generate list of pages based upon existing conversations. // This requires retrievable conversations from the server side. + const Center(child: ConversationWidget()), ]; final scaffoldKey = GlobalKey(); @@ -55,7 +52,6 @@ class _ConversationsListWidgetState extends State { @override void dispose() { _model.dispose(); - super.dispose(); } @@ -186,9 +182,11 @@ class _ConversationsListWidgetState extends State { ], ), onTap: () => { + // TODO: Need to implement. print("opening existing conversation") }, onLongPress: () => { + // TODO: Need to implement. print("opening options") }, ), From 2625a2ec6cc1d008c14f9d1ebde9953aef19b5b6 Mon Sep 17 00:00:00 2001 From: Joshua Date: Sun, 7 Jul 2024 12:44:03 -0600 Subject: [PATCH 12/24] starting docs --- mobile/app/lib/main.dart | 18 +++++++++++------- mobile/app/test/widget_test.dart | 2 +- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/mobile/app/lib/main.dart b/mobile/app/lib/main.dart index 95974ea..30672db 100644 --- a/mobile/app/lib/main.dart +++ b/mobile/app/lib/main.dart @@ -1,3 +1,4 @@ +import 'package:app/pages/conversation_widget.dart'; import 'package:app/pages/settings_widget.dart'; import 'package:app/pages/conversations_list_widget.dart'; import 'package:flutter/material.dart'; @@ -23,12 +24,16 @@ BottomNavigationBarItem _buildNavigationItem(int index) { IconData iconData = Icons.error; switch (index) { case 0: + label = 'Settings'; + iconData = Icons.settings; + break; + case 1: label = 'Home'; iconData = Icons.home; break; - case 1: - label = 'Settings'; - iconData = Icons.settings; + case 2: + label = 'Chat'; + iconData = Icons.bubble_chart; break; } return BottomNavigationBarItem( @@ -48,14 +53,13 @@ void main() { class MyApp extends ConsumerWidget { MyApp({super.key}); - final selectedIndexProvider = StateProvider((_) => 0); // Initial state + final selectedIndexProvider = StateProvider((_) => 2); // Initial state // This list holds the pages to navigate between final List _pages = [ - // Replace with the widget for your first page (e.g., HomeScreen()) - const Center(child: ConversationsListWidget()), - // Replace with the widget for your second page (e.g., SettingsPage()) const Center(child: SettingsWidget()), + const Center(child: ConversationsListWidget()), + const Center(child: ConversationWidget()), ]; // This widget is the root of your application. diff --git a/mobile/app/test/widget_test.dart b/mobile/app/test/widget_test.dart index 4e2a713..1a3d2a8 100644 --- a/mobile/app/test/widget_test.dart +++ b/mobile/app/test/widget_test.dart @@ -13,7 +13,7 @@ import 'package:app/main.dart'; void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); + await tester.pumpWidget(MyApp()); // Verify that our counter starts at 0. expect(find.text('0'), findsOneWidget); From 80f20e73a9a8671f938a88b64c2b98fe01b100c5 Mon Sep 17 00:00:00 2001 From: Joshua Date: Sun, 7 Jul 2024 14:15:29 -0600 Subject: [PATCH 13/24] integrated code from one-ai-mobile repo. working on GUI integration now. --- .../conversation_bubbles_model.dart | 3 + .../conversation_bubbles_widget.dart | 3 + .../conversation_thread_model.dart | 5 +- .../conversation_thread_widget.dart | 11 +- mobile/app/lib/pages/conversation_widget.dart | 4 +- mobile/app/lib/util/server.dart | 129 ++++++++++++++++++ mobile/app/lib/util/speech_to_text.dart | 43 ++++++ .../Flutter/GeneratedPluginRegistrant.swift | 2 + mobile/app/pubspec.lock | 32 +++++ mobile/app/pubspec.yaml | 1 + 10 files changed, 221 insertions(+), 12 deletions(-) create mode 100644 mobile/app/lib/util/server.dart create mode 100644 mobile/app/lib/util/speech_to_text.dart diff --git a/mobile/app/lib/components/conversation_bubbles/conversation_bubbles_model.dart b/mobile/app/lib/components/conversation_bubbles/conversation_bubbles_model.dart index e69de29..6c42b17 100644 --- a/mobile/app/lib/components/conversation_bubbles/conversation_bubbles_model.dart +++ b/mobile/app/lib/components/conversation_bubbles/conversation_bubbles_model.dart @@ -0,0 +1,3 @@ +class ConversationBubblesModel { + +} \ No newline at end of file diff --git a/mobile/app/lib/components/conversation_bubbles/conversation_bubbles_widget.dart b/mobile/app/lib/components/conversation_bubbles/conversation_bubbles_widget.dart index e69de29..11bd06d 100644 --- a/mobile/app/lib/components/conversation_bubbles/conversation_bubbles_widget.dart +++ b/mobile/app/lib/components/conversation_bubbles/conversation_bubbles_widget.dart @@ -0,0 +1,3 @@ +class ConversationBubblesWidget { + +} \ No newline at end of file diff --git a/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart b/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart index c574eb4..123bd4f 100644 --- a/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart +++ b/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart @@ -1,4 +1,4 @@ -import '/components/conversation_bubbles/conversation_bubbles_widget.dart'; +import '../conversation_bubbles/conversation_bubbles_model.dart'; import '/components/prompt_box/prompt_box_widget.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'conversation_thread_widget.dart' show ConversationThreadWidget; @@ -13,7 +13,8 @@ class ConversationThreadModel final formKey = GlobalKey(); // Model for ConversationBubbles component. - //late ConversationBubblesModel conversationBubblesModel; + late ConversationBubblesModel conversationBubblesModel; + // Model for PromptBox component. late PromptBoxModel promptBoxModel; diff --git a/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart b/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart index 869317f..aca9e13 100644 --- a/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart +++ b/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart @@ -55,12 +55,7 @@ class _ConversationThreadWidgetState extends State { children: [ Expanded( child: ListView( - padding: const EdgeInsets.fromLTRB( - 0, - 12, - 0, - 24, - ), + padding: const EdgeInsets.fromLTRB(0, 12, 0, 24,), reverse: true, scrollDirection: Axis.vertical, children: [ @@ -142,14 +137,14 @@ class _ConversationThreadWidgetState extends State { // ), Align( alignment: const AlignmentDirectional(-1, -1), + // TODO: Make the delete button conditional upon media upload. child: FlutterFlowIconButton( borderColor: AppTheme.error, borderRadius: 20, borderWidth: 2, buttonSize: 40, - fillColor: AppTheme - .primaryBackground, + fillColor: AppTheme.primaryBackground, icon: const Icon( Icons.delete_outline_rounded, color: AppTheme.error, diff --git a/mobile/app/lib/pages/conversation_widget.dart b/mobile/app/lib/pages/conversation_widget.dart index dc5990f..a22bbba 100644 --- a/mobile/app/lib/pages/conversation_widget.dart +++ b/mobile/app/lib/pages/conversation_widget.dart @@ -3,8 +3,6 @@ import '/components/conversation_thread/conversation_thread_widget.dart'; import '/components/model_item/model_item_widget.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:flutter/material.dart'; -import 'package:google_fonts/google_fonts.dart'; -import 'package:provider/provider.dart'; import 'package:app/theme.dart'; @@ -48,6 +46,7 @@ class _ConversationWidgetState extends State { appBar: AppBar( backgroundColor: AppTheme.primaryBackground, automaticallyImplyLeading: false, + // ---------- Back Button leading: FlutterFlowIconButton( borderColor: Colors.transparent, borderRadius: 30, @@ -81,6 +80,7 @@ class _ConversationWidgetState extends State { actions: [ Padding( padding: const EdgeInsetsDirectional.fromSTEB(0, 8, 16, 8), + // ---------- Options Button child: FlutterFlowIconButton( borderColor: AppTheme.alternate, borderRadius: 12, diff --git a/mobile/app/lib/util/server.dart b/mobile/app/lib/util/server.dart new file mode 100644 index 0000000..e1076a5 --- /dev/null +++ b/mobile/app/lib/util/server.dart @@ -0,0 +1,129 @@ +import 'dart:convert'; +import 'package:http/http.dart' as http; + +import 'package:app/util/speech_to_text.dart'; + +class Server { + // TODO: One-time process to establish server connection is required for easy setup/maintenance. + final url = "http://10.0.2.2:11434"; + final headers = {'Content-Type': 'application/json'}; + + final SpeechToText stt = SpeechToText(); + late bool _speechEnabled; + + String prompt = ""; + String response = ""; + + Server() { + // Speech-To-Text engine activation. + _speechEnabled = stt.isSpeechEnabled(); + + // TODO: Initialize all different components of STT, TTS, LLM, etc. + // TODO: Handshake w/ server to validate identity + } + + // Using native on-device Speech-To-Text capability, get the server response when ready. + Future sttGetResponseWhenReady() async { + // TODO: In case on-device STT is unavailable, use server-side STT service. + if (!_speechEnabled) "Unable to process Speech-To-Text on-device."; + + setPrompt(stt.getTextWhenReady() as String); + constructPrompt(); + httpSendRequest(); + + return response; + } + + // Arbitrary prompt, used for standard text interaction rather than STT. + void setPrompt(String p) => prompt = p; + + // Get the last response or request a response from the server. + String getResponse() { + if(response.isEmpty) { + constructPrompt(); + httpSendRequest(); + } + + return response; + } + + // Get the latest prompt. + String getPrompt() => prompt; + + // Formats the prompt in JSON. + String constructPrompt() { + // Construct the JSON payload + final Map data = { + "model": + "llama3", // TODO: Allow for different models to be dynamically selected. + "prompt": prompt, + "stream": false // TODO: Allow for toggleable states between stream + // TODO: Allow for additional flags, ie. continuous conversation, images, etc. + }; + + // Encode the JSON payload + final String body = json.encode(data); + + return body; + } + + // Send the HTTP request to the server. + void httpSendRequest() async { + String json = constructPrompt(); + + // Send the HTTP POST request + final serverResponse = await http.post( + Uri.parse('$url/api/generate'), + headers: headers, + body: json, + ); + + if (serverResponse.statusCode == 200) { + Map re = jsonDecode(serverResponse.body); + response = re["response"]!; + } else { + response = ('Error: ${serverResponse.statusCode}'); + } + } +} +// +// +// // GUI components +// child: Column( +// mainAxisAlignment: MainAxisAlignment.center, +// children: [ +// Container( +// padding: const EdgeInsets.all(16), +// child: const Text( +// 'Recognized words:', +// style: TextStyle(fontSize: 20.0), +// ), +// ), +// Expanded( +// child: Container( +// padding: const EdgeInsets.all(16), +// child: Text( +// // If listening is active show the recognized words +// _speechToText.isListening +// ? prompt +// // If listening isn't active but could be tell the user +// // how to start it, otherwise indicate that speech +// // recognition is not yet ready or not supported on +// // the target device +// : _speechEnabled +// ? responseFromAI +// : 'Speech disabled', +// ), +// ), +// ), +// ], +// ), +// ), +// floatingActionButton: FloatingActionButton( +// onPressed: +// // If not yet listening for speech start, otherwise stop +// //sendPrompt, +// _speechToText.isNotListening ? _startListening : sendPrompt, +// tooltip: 'Listen', +// child: Icon(_speechToText.isNotListening ? Icons.mic_off : Icons.mic), +// ), diff --git a/mobile/app/lib/util/speech_to_text.dart b/mobile/app/lib/util/speech_to_text.dart new file mode 100644 index 0000000..27f9d81 --- /dev/null +++ b/mobile/app/lib/util/speech_to_text.dart @@ -0,0 +1,43 @@ +import 'package:speech_to_text/speech_recognition_result.dart'; +import 'package:speech_to_text/speech_to_text.dart' as stt; + +/// Handles standard Speech-To-Text using on-device processing. +class SpeechToText { + final stt.SpeechToText _speechToText = stt.SpeechToText(); + bool _speechEnabled = false; + String text = ""; + + // Check if speech recognition is enabled. + bool isSpeechEnabled() => _speechEnabled; + + Future getTextWhenReady() async { + await _speechToText.stop(); + return text; + } + + // Retrieve the text. + String getText() => text; + + // Reset either after saving the text or prior to using STT again. + void reset() => text = ""; + + /// Start a speech recognition session + void _startListening() async => await _speechToText.listen(onResult: _onSpeechResult); + + /// Manually stop the active speech recognition session + /// Note that there are also timeouts that each platform enforces + /// and the SpeechToText plugin supports setting timeouts on the + /// listen method. + void _stopListening() async => await _speechToText.stop(); + + SpeechToText() { + _initSpeech(); + } + + /// This has to happen only once per app + void _initSpeech() async => _speechEnabled = await _speechToText.initialize(); + + /// This is the callback that the SpeechToText plugin calls when + /// the platform returns recognized words. + void _onSpeechResult(SpeechRecognitionResult result) => text = result.recognizedWords; +} \ No newline at end of file diff --git a/mobile/app/macos/Flutter/GeneratedPluginRegistrant.swift b/mobile/app/macos/Flutter/GeneratedPluginRegistrant.swift index 513efaa..7bc02f3 100644 --- a/mobile/app/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/mobile/app/macos/Flutter/GeneratedPluginRegistrant.swift @@ -8,6 +8,7 @@ import Foundation import path_provider_foundation import rive_common import shared_preferences_foundation +import speech_to_text_macos import sqflite import url_launcher_macos import video_player_avfoundation @@ -16,6 +17,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) RivePlugin.register(with: registry.registrar(forPlugin: "RivePlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) + SpeechToTextMacosPlugin.register(with: registry.registrar(forPlugin: "SpeechToTextMacosPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin")) diff --git a/mobile/app/pubspec.lock b/mobile/app/pubspec.lock index 8adbd99..7721e71 100644 --- a/mobile/app/pubspec.lock +++ b/mobile/app/pubspec.lock @@ -696,6 +696,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.1" + pedantic: + dependency: transitive + description: + name: pedantic + sha256: "67fc27ed9639506c856c840ccce7594d0bdcd91bc8d53d6e52359449a1d50602" + url: "https://pub.dev" + source: hosted + version: "1.11.1" percent_indicator: dependency: transitive description: @@ -917,6 +925,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.0" + speech_to_text: + dependency: "direct main" + description: + name: speech_to_text + sha256: "57fef1d41bdebe298e84842c89bb4ac91f31cdbec7830c8cb1fc6b91d03abd42" + url: "https://pub.dev" + source: hosted + version: "6.6.0" + speech_to_text_macos: + dependency: transitive + description: + name: speech_to_text_macos + sha256: e685750f7542fcaa087a5396ee471e727ec648bf681f4da83c84d086322173f6 + url: "https://pub.dev" + source: hosted + version: "1.1.0" + speech_to_text_platform_interface: + dependency: transitive + description: + name: speech_to_text_platform_interface + sha256: a0df1a907091ea09880077dc25aae02af9f79811264e6e97ddb08639b7f771c2 + url: "https://pub.dev" + source: hosted + version: "2.2.0" sprintf: dependency: transitive description: diff --git a/mobile/app/pubspec.yaml b/mobile/app/pubspec.yaml index adfa4e0..1d4867d 100644 --- a/mobile/app/pubspec.yaml +++ b/mobile/app/pubspec.yaml @@ -45,6 +45,7 @@ dependencies: easy_debounce: any video_player: ^2.9.1 flutter_riverpod: ^2.5.1 + speech_to_text: ^6.6.0 dev_dependencies: flutter_test: sdk: flutter From 8956ed7885e9712e19dc4a293b4850b80b7ccc41 Mon Sep 17 00:00:00 2001 From: Joshua Date: Sun, 7 Jul 2024 16:44:08 -0600 Subject: [PATCH 14/24] implemented basic chat bubbles generated from a list. --- .../conversation_bubbles_model.dart | 3 -- .../conversation_bubbles_widget.dart | 3 -- .../components/conversation_thread/chat.dart | 9 +++++ .../chat_bubble_model.dart | 18 ++++++++++ .../chat_bubble_widget.dart | 33 +++++++++++++++++ .../conversation_thread/conversation.dart | 15 ++++++++ .../conversation_thread_model.dart | 28 ++++++++------- .../conversation_thread_widget.dart | 36 +++++++------------ mobile/app/lib/pages/conversation_model.dart | 5 +-- .../lib/pages/conversations_list_model.dart | 2 ++ mobile/app/lib/util/server.dart | 22 +++++++++++- 11 files changed, 126 insertions(+), 48 deletions(-) delete mode 100644 mobile/app/lib/components/conversation_bubbles/conversation_bubbles_model.dart delete mode 100644 mobile/app/lib/components/conversation_bubbles/conversation_bubbles_widget.dart create mode 100644 mobile/app/lib/components/conversation_thread/chat.dart create mode 100644 mobile/app/lib/components/conversation_thread/chat_bubble_model.dart create mode 100644 mobile/app/lib/components/conversation_thread/chat_bubble_widget.dart create mode 100644 mobile/app/lib/components/conversation_thread/conversation.dart diff --git a/mobile/app/lib/components/conversation_bubbles/conversation_bubbles_model.dart b/mobile/app/lib/components/conversation_bubbles/conversation_bubbles_model.dart deleted file mode 100644 index 6c42b17..0000000 --- a/mobile/app/lib/components/conversation_bubbles/conversation_bubbles_model.dart +++ /dev/null @@ -1,3 +0,0 @@ -class ConversationBubblesModel { - -} \ No newline at end of file diff --git a/mobile/app/lib/components/conversation_bubbles/conversation_bubbles_widget.dart b/mobile/app/lib/components/conversation_bubbles/conversation_bubbles_widget.dart deleted file mode 100644 index 11bd06d..0000000 --- a/mobile/app/lib/components/conversation_bubbles/conversation_bubbles_widget.dart +++ /dev/null @@ -1,3 +0,0 @@ -class ConversationBubblesWidget { - -} \ No newline at end of file diff --git a/mobile/app/lib/components/conversation_thread/chat.dart b/mobile/app/lib/components/conversation_thread/chat.dart new file mode 100644 index 0000000..0b9da7a --- /dev/null +++ b/mobile/app/lib/components/conversation_thread/chat.dart @@ -0,0 +1,9 @@ +class Chat { + late String message; + late bool isMe; + + // Most basic chat, text from server or from user. + Chat(this.message, this.isMe); + + // TODO: Overload constructors to allow for multimedia. +} \ No newline at end of file diff --git a/mobile/app/lib/components/conversation_thread/chat_bubble_model.dart b/mobile/app/lib/components/conversation_thread/chat_bubble_model.dart new file mode 100644 index 0000000..b6efc16 --- /dev/null +++ b/mobile/app/lib/components/conversation_thread/chat_bubble_model.dart @@ -0,0 +1,18 @@ +import 'chat_bubble_widget.dart'; +import 'chat.dart'; + +import 'package:flutter/material.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; + + +class ChatBubbleModel extends FlutterFlowModel{ + late final Chat c; + + ChatBubbleModel(this.c); + + @override + void dispose() {} // TODO: Need to implement for prompt/response deletion. + + @override + void initState(BuildContext context) {} // TODO: Need to implement for loading in media and editing. +} \ No newline at end of file diff --git a/mobile/app/lib/components/conversation_thread/chat_bubble_widget.dart b/mobile/app/lib/components/conversation_thread/chat_bubble_widget.dart new file mode 100644 index 0000000..d4610a7 --- /dev/null +++ b/mobile/app/lib/components/conversation_thread/chat_bubble_widget.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; +import 'chat.dart'; + +class ChatBubbleWidget extends StatelessWidget{ + late Chat c; + + ChatBubbleWidget({super.key, required this.c}); + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: c.isMe ? MainAxisAlignment.end : MainAxisAlignment.start, + children: [ + Container( + padding: const EdgeInsets.all(16.0), + margin: const EdgeInsets.all(8.0), + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + topLeft: const Radius.circular(12.0), + topRight: const Radius.circular(12.0), + bottomLeft: c.isMe ? const Radius.circular(12.0) : const Radius + .circular(0.0), + bottomRight: c.isMe ? const Radius.circular(12.0) : const Radius + .circular(0.0), + ), + color: c.isMe ? Colors.blue[200] : Colors.grey[200], + ), + child: Text(c.message), + ), + ], + ); + } +} \ No newline at end of file diff --git a/mobile/app/lib/components/conversation_thread/conversation.dart b/mobile/app/lib/components/conversation_thread/conversation.dart new file mode 100644 index 0000000..5023210 --- /dev/null +++ b/mobile/app/lib/components/conversation_thread/conversation.dart @@ -0,0 +1,15 @@ +import 'package:app/util/server.dart'; +import 'chat.dart'; + +class Conversation { + int conversationID; + List chats = []; + + Conversation(this.conversationID) { + chats = Server().getConversationByID(conversationID); + } + + List getChats() { + return chats; + } +} \ No newline at end of file diff --git a/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart b/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart index 123bd4f..08eacc1 100644 --- a/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart +++ b/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart @@ -1,32 +1,34 @@ -import '../conversation_bubbles/conversation_bubbles_model.dart'; -import '/components/prompt_box/prompt_box_widget.dart'; +import 'package:app/components/conversation_thread/chat_bubble_model.dart'; +import 'package:app/components/conversation_thread/chat.dart'; +import 'package:app/components/conversation_thread/conversation.dart'; +import 'package:app/util/server.dart'; + +import 'package:app/components/prompt_box/prompt_box_widget.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'conversation_thread_widget.dart' show ConversationThreadWidget; -import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; +import 'package:cached_network_image/cached_network_image.dart'; -class ConversationThreadModel - extends FlutterFlowModel { - /// State fields for stateful widgets in this component. - +class ConversationThreadModel extends FlutterFlowModel { final formKey = GlobalKey(); - // Model for ConversationBubbles component. - late ConversationBubblesModel conversationBubblesModel; - - // Model for PromptBox component. late PromptBoxModel promptBoxModel; + late int ?conversationID; + late Conversation conversation; + + // If there's a conversation ID, retrieve the conversation history. + // Otherwise, start a new conversation with user defaults. + ConversationThreadModel(this.conversationID); @override void initState(BuildContext context) { - //conversationBubblesModel = createModel(context, () => ConversationBubblesModel()); + conversation = Conversation(conversationID!); promptBoxModel = createModel(context, () => PromptBoxModel()); } @override void dispose() { - //conversationBubblesModel.dispose(); promptBoxModel.dispose(); } } diff --git a/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart b/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart index aca9e13..6668fd6 100644 --- a/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart +++ b/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart @@ -1,10 +1,11 @@ -import '/components/conversation_bubbles/conversation_bubbles_widget.dart'; +import 'package:app/components/conversation_thread/conversation.dart'; + +import '/components/conversation_thread/chat_bubble_widget.dart'; +import 'package:app/components/conversation_thread/conversation_thread_model.dart'; import '/components/prompt_box/prompt_box_widget.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; -import 'package:google_fonts/google_fonts.dart'; -import 'package:provider/provider.dart'; import 'package:app/theme.dart'; @@ -31,7 +32,7 @@ class _ConversationThreadWidgetState extends State { @override void initState() { super.initState(); - _model = createModel(context, () => ConversationThreadModel()); + _model = createModel(context, () => ConversationThreadModel(0)); // TODO } @override @@ -43,6 +44,7 @@ class _ConversationThreadWidgetState extends State { @override Widget build(BuildContext context) { + Conversation c = _model.conversation; return Container( width: double.infinity, height: double.infinity, @@ -54,28 +56,14 @@ class _ConversationThreadWidgetState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( - child: ListView( + child: ListView.builder( + itemCount: c.chats.length, padding: const EdgeInsets.fromLTRB(0, 12, 0, 24,), - reverse: true, + reverse: false, scrollDirection: Axis.vertical, - children: [ - Container( - decoration: const BoxDecoration(), - ), - Container( - height: MediaQuery.sizeOf(context).height * 1, - constraints: BoxConstraints( - minHeight: MediaQuery.sizeOf(context).height * 1, - ), - decoration: const BoxDecoration(), - //child: wrapWithModel( - //model: _model.conversationBubblesModel, - //updateCallback: () => setState(() {}), - //child: ConversationBubblesWidget(), - //child: const Text("test"), - //), - ), - ], + itemBuilder: (BuildContext context, int index) { + return ChatBubbleWidget(c: c.chats[index]); + }, ), ), Container( diff --git a/mobile/app/lib/pages/conversation_model.dart b/mobile/app/lib/pages/conversation_model.dart index 8273bd9..4566fad 100644 --- a/mobile/app/lib/pages/conversation_model.dart +++ b/mobile/app/lib/pages/conversation_model.dart @@ -1,11 +1,8 @@ -import '/components/conversation_details_overlay/conversation_details_overlay_widget.dart'; import '/components/conversation_thread/conversation_thread_widget.dart'; import '/components/model_item/model_item_widget.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'conversation_widget.dart' show ConversationWidget; import 'package:flutter/material.dart'; -import 'package:google_fonts/google_fonts.dart'; -import 'package:provider/provider.dart'; class ConversationModel extends FlutterFlowModel { /// State fields for stateful widgets in this page. @@ -19,7 +16,7 @@ class ConversationModel extends FlutterFlowModel { @override void initState(BuildContext context) { conversationThreadModel = - createModel(context, () => ConversationThreadModel()); + createModel(context, () => ConversationThreadModel(0)); // TODO modelItemModel = createModel(context, () => ModelItemModel()); } diff --git a/mobile/app/lib/pages/conversations_list_model.dart b/mobile/app/lib/pages/conversations_list_model.dart index c4ad379..23245b2 100644 --- a/mobile/app/lib/pages/conversations_list_model.dart +++ b/mobile/app/lib/pages/conversations_list_model.dart @@ -1,6 +1,8 @@ import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'conversations_list_widget.dart' show ConversationsListWidget; import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:provider/provider.dart'; class ConversationsListModel extends FlutterFlowModel { diff --git a/mobile/app/lib/util/server.dart b/mobile/app/lib/util/server.dart index e1076a5..8e7e365 100644 --- a/mobile/app/lib/util/server.dart +++ b/mobile/app/lib/util/server.dart @@ -1,18 +1,26 @@ import 'dart:convert'; import 'package:http/http.dart' as http; +import 'package:app/components/conversation_thread/chat.dart'; + import 'package:app/util/speech_to_text.dart'; class Server { // TODO: One-time process to establish server connection is required for easy setup/maintenance. + // Server variables final url = "http://10.0.2.2:11434"; - final headers = {'Content-Type': 'application/json'}; + Map headers = {'Content-Type': 'application/json'}; + // Model variables final SpeechToText stt = SpeechToText(); + + // Status variables late bool _speechEnabled; + // Runtime variables String prompt = ""; String response = ""; + int conversationID = 0; Server() { // Speech-To-Text engine activation. @@ -22,6 +30,18 @@ class Server { // TODO: Handshake w/ server to validate identity } + List getConversationByID(conversationID) { + this.conversationID = conversationID; + + // TODO: Implement conversation IDs & retrieval from server + List chats = [ + (Chat("hello world!", true)), + (Chat("hello to you too!", false)), + ]; + + return chats; + } + // Using native on-device Speech-To-Text capability, get the server response when ready. Future sttGetResponseWhenReady() async { // TODO: In case on-device STT is unavailable, use server-side STT service. From ff4a54fbbcbedf1b234428a2867ace694e698727 Mon Sep 17 00:00:00 2001 From: Joshua Date: Mon, 8 Jul 2024 13:03:15 -0600 Subject: [PATCH 15/24] enabled togglable chat history (during runtime) that uses different api endpoints. --- .../components/conversation_thread/chat.dart | 19 ++- .../chat_bubble_widget.dart | 10 +- .../prompt_box/prompt_box_widget.dart | 27 ++-- mobile/app/lib/util/server.dart | 127 +++++++++--------- 4 files changed, 99 insertions(+), 84 deletions(-) diff --git a/mobile/app/lib/components/conversation_thread/chat.dart b/mobile/app/lib/components/conversation_thread/chat.dart index 0b9da7a..4d2fbf4 100644 --- a/mobile/app/lib/components/conversation_thread/chat.dart +++ b/mobile/app/lib/components/conversation_thread/chat.dart @@ -1,9 +1,22 @@ +import 'dart:convert'; +import 'package:json_annotation/json_annotation.dart'; + +@JsonSerializable() class Chat { - late String message; - late bool isMe; + final String content; + final bool role; // Most basic chat, text from server or from user. - Chat(this.message, this.isMe); + Chat(this.content, this.role); + + String toJson() { + Map c = { + "content": content, + "role": role, + }; + + return json.encode(c); + } // TODO: Overload constructors to allow for multimedia. } \ No newline at end of file diff --git a/mobile/app/lib/components/conversation_thread/chat_bubble_widget.dart b/mobile/app/lib/components/conversation_thread/chat_bubble_widget.dart index d4610a7..2477f96 100644 --- a/mobile/app/lib/components/conversation_thread/chat_bubble_widget.dart +++ b/mobile/app/lib/components/conversation_thread/chat_bubble_widget.dart @@ -9,7 +9,7 @@ class ChatBubbleWidget extends StatelessWidget{ @override Widget build(BuildContext context) { return Row( - mainAxisAlignment: c.isMe ? MainAxisAlignment.end : MainAxisAlignment.start, + mainAxisAlignment: c.role ? MainAxisAlignment.end : MainAxisAlignment.start, children: [ Container( padding: const EdgeInsets.all(16.0), @@ -18,14 +18,14 @@ class ChatBubbleWidget extends StatelessWidget{ borderRadius: BorderRadius.only( topLeft: const Radius.circular(12.0), topRight: const Radius.circular(12.0), - bottomLeft: c.isMe ? const Radius.circular(12.0) : const Radius + bottomLeft: c.role ? const Radius.circular(12.0) : const Radius .circular(0.0), - bottomRight: c.isMe ? const Radius.circular(12.0) : const Radius + bottomRight: c.role ? const Radius.circular(12.0) : const Radius .circular(0.0), ), - color: c.isMe ? Colors.blue[200] : Colors.grey[200], + color: c.role ? Colors.blue[200] : Colors.grey[200], ), - child: Text(c.message), + child: Text(c.content), ), ], ); diff --git a/mobile/app/lib/components/prompt_box/prompt_box_widget.dart b/mobile/app/lib/components/prompt_box/prompt_box_widget.dart index 81688c0..5870e62 100644 --- a/mobile/app/lib/components/prompt_box/prompt_box_widget.dart +++ b/mobile/app/lib/components/prompt_box/prompt_box_widget.dart @@ -1,3 +1,4 @@ +import 'package:app/util/server.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:easy_debounce/easy_debounce.dart'; import 'package:flutter/material.dart'; @@ -58,8 +59,9 @@ class _PromptBoxWidgetState extends State { mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceAround, crossAxisAlignment: CrossAxisAlignment.end, + // TODO: Implement the context upload buttons children: [ - Expanded( + Expanded( // TODO: File context upload child: Padding( padding: const EdgeInsets.all(14), child: FlutterFlowIconButton( @@ -78,7 +80,7 @@ class _PromptBoxWidgetState extends State { ), ), ), - Expanded( + Expanded( // TODO: Photo context upload child: Padding( padding: const EdgeInsets.all(14), child: FlutterFlowIconButton( @@ -97,7 +99,7 @@ class _PromptBoxWidgetState extends State { ), ), ), - Expanded( + Expanded( // TODO: Microphone context upload -> or STT?? child: Padding( padding: const EdgeInsets.all(14), child: FlutterFlowIconButton( @@ -116,7 +118,7 @@ class _PromptBoxWidgetState extends State { ), ), ), - Expanded( + Expanded( // TODO: Video context upload child: Padding( padding: const EdgeInsets.all(14), child: FlutterFlowIconButton( @@ -135,7 +137,7 @@ class _PromptBoxWidgetState extends State { ), ), ), - Expanded( + Expanded( // TODO: Camera context upload child: Padding( padding: const EdgeInsets.all(14), child: FlutterFlowIconButton( @@ -173,8 +175,6 @@ class _PromptBoxWidgetState extends State { ), autofocus: true, textCapitalization: TextCapitalization.none, - textInputAction: TextInputAction.go, - obscureText: false, decoration: InputDecoration( isDense: false, counterStyle: AppTheme.bodyMedium, @@ -218,8 +218,11 @@ class _PromptBoxWidgetState extends State { textAlign: TextAlign.start, maxLines: 5, minLines: 1, - validator: - _model.textControllerValidator.asValidator(context), + validator: _model.textControllerValidator.asValidator(context), + textInputAction: TextInputAction.send, // "Send" on keyboard + onFieldSubmitted: (text) { + updateFromLatestInput(text); // TODO: Connect with multimedia + }, ), ), ), @@ -230,4 +233,10 @@ class _PromptBoxWidgetState extends State { ), ); } + + void updateFromLatestInput(String input) { + Server s = Server(); + s.setPrompt(input); + print(s.getResponse()); + } } diff --git a/mobile/app/lib/util/server.dart b/mobile/app/lib/util/server.dart index 8e7e365..dd8646e 100644 --- a/mobile/app/lib/util/server.dart +++ b/mobile/app/lib/util/server.dart @@ -16,6 +16,13 @@ class Server { // Status variables late bool _speechEnabled; + bool _historyEnabled = true; + + // Context variables + List chats = [ + (Chat("This is just a test. Only reply Yes.", true)), + (Chat("Yes.", false)), + ]; // Runtime variables String prompt = ""; @@ -31,40 +38,48 @@ class Server { } List getConversationByID(conversationID) { - this.conversationID = conversationID; - // TODO: Implement conversation IDs & retrieval from server - List chats = [ - (Chat("hello world!", true)), - (Chat("hello to you too!", false)), - ]; - + this.conversationID = conversationID; return chats; } + // TODO: Integrate with getConversationByID() + List> convertMessages() { + List> messageList = []; + for (Chat c in chats) { + messageList.add({ + "role": c.role ? "user" : "assistant", + "content": c.content, + }); + } + + return messageList; + } + // Using native on-device Speech-To-Text capability, get the server response when ready. Future sttGetResponseWhenReady() async { // TODO: In case on-device STT is unavailable, use server-side STT service. if (!_speechEnabled) "Unable to process Speech-To-Text on-device."; setPrompt(stt.getTextWhenReady() as String); - constructPrompt(); httpSendRequest(); return response; } + Future getResponseWhenReady(String p) async { + prompt = p; + httpSendRequest(); + return response; + } + // Arbitrary prompt, used for standard text interaction rather than STT. void setPrompt(String p) => prompt = p; // Get the last response or request a response from the server. String getResponse() { - if(response.isEmpty) { - constructPrompt(); httpSendRequest(); - } - - return response; + return response; } // Get the latest prompt. @@ -72,28 +87,46 @@ class Server { // Formats the prompt in JSON. String constructPrompt() { - // Construct the JSON payload - final Map data = { - "model": - "llama3", // TODO: Allow for different models to be dynamically selected. - "prompt": prompt, - "stream": false // TODO: Allow for toggleable states between stream - // TODO: Allow for additional flags, ie. continuous conversation, images, etc. - }; + // TODO: Allow for additional flags, ie. continuous conversation, images, etc. + // TODO: Allow for toggleable states between stream + // TODO: Allow for different models to be dynamically selected. + Map data; + + if(_historyEnabled) { + chats = [...chats, Chat(prompt, true)]; + + List> messageList = convertMessages(); + + data = { + "model": "llama3", + "messages": messageList, + "stream": false, + }; + } + else { + data = { + "model": "llama3", + "prompt": prompt, + "stream": false, + }; + } // Encode the JSON payload - final String body = json.encode(data); - - return body; + return json.encode(data); } // Send the HTTP request to the server. void httpSendRequest() async { String json = constructPrompt(); + print("\n\n$json\n\n"); + + String endpoint = "/api/"; + if(_historyEnabled) endpoint += "chat"; + else endpoint += "generate"; // Send the HTTP POST request final serverResponse = await http.post( - Uri.parse('$url/api/generate'), + Uri.parse('$url$endpoint'), headers: headers, body: json, ); @@ -104,46 +137,6 @@ class Server { } else { response = ('Error: ${serverResponse.statusCode}'); } + chats.add(Chat(response, false)); } -} -// -// -// // GUI components -// child: Column( -// mainAxisAlignment: MainAxisAlignment.center, -// children: [ -// Container( -// padding: const EdgeInsets.all(16), -// child: const Text( -// 'Recognized words:', -// style: TextStyle(fontSize: 20.0), -// ), -// ), -// Expanded( -// child: Container( -// padding: const EdgeInsets.all(16), -// child: Text( -// // If listening is active show the recognized words -// _speechToText.isListening -// ? prompt -// // If listening isn't active but could be tell the user -// // how to start it, otherwise indicate that speech -// // recognition is not yet ready or not supported on -// // the target device -// : _speechEnabled -// ? responseFromAI -// : 'Speech disabled', -// ), -// ), -// ), -// ], -// ), -// ), -// floatingActionButton: FloatingActionButton( -// onPressed: -// // If not yet listening for speech start, otherwise stop -// //sendPrompt, -// _speechToText.isNotListening ? _startListening : sendPrompt, -// tooltip: 'Listen', -// child: Icon(_speechToText.isNotListening ? Icons.mic_off : Icons.mic), -// ), +} \ No newline at end of file From 7547fe110f269de4bac0225f9b5e8a18d89a7880 Mon Sep 17 00:00:00 2001 From: Joshua Date: Mon, 8 Jul 2024 14:23:01 -0600 Subject: [PATCH 16/24] conversation now shown in gui when manually refreshed. --- .../prompt_box/prompt_box_widget.dart | 5 +- mobile/app/lib/util/server.dart | 64 +++++++++++-------- 2 files changed, 39 insertions(+), 30 deletions(-) diff --git a/mobile/app/lib/components/prompt_box/prompt_box_widget.dart b/mobile/app/lib/components/prompt_box/prompt_box_widget.dart index 5870e62..d149eea 100644 --- a/mobile/app/lib/components/prompt_box/prompt_box_widget.dart +++ b/mobile/app/lib/components/prompt_box/prompt_box_widget.dart @@ -235,8 +235,7 @@ class _PromptBoxWidgetState extends State { } void updateFromLatestInput(String input) { - Server s = Server(); - s.setPrompt(input); - print(s.getResponse()); + Server().getResponseWhenReady(input); + // TODO: Refresh the page with the prompt & response. } } diff --git a/mobile/app/lib/util/server.dart b/mobile/app/lib/util/server.dart index dd8646e..8a82d7b 100644 --- a/mobile/app/lib/util/server.dart +++ b/mobile/app/lib/util/server.dart @@ -5,17 +5,33 @@ import 'package:app/components/conversation_thread/chat.dart'; import 'package:app/util/speech_to_text.dart'; +// Uses Singleton design pattern to ensure that any server references across the app are using the same instance. class Server { + + // STT variables + static final SpeechToText stt = SpeechToText(); + static late bool _speechEnabled; + + static final Server _single_server = Server._internal(); + + // TODO: Verify all different components of STT, TTS, LLM, etc have been initialized. + // TODO: Handshake w/ server to validate identity + factory Server() { + // Speech-To-Text engine activation. + _speechEnabled = stt.isSpeechEnabled(); + + return _single_server; + } + + Server._internal(); + // TODO: One-time process to establish server connection is required for easy setup/maintenance. // Server variables - final url = "http://10.0.2.2:11434"; + final url = "http://10.0.2.2:11434/api/"; + String endpoint = ""; Map headers = {'Content-Type': 'application/json'}; - - // Model variables - final SpeechToText stt = SpeechToText(); - + // Status variables - late bool _speechEnabled; bool _historyEnabled = true; // Context variables @@ -29,14 +45,6 @@ class Server { String response = ""; int conversationID = 0; - Server() { - // Speech-To-Text engine activation. - _speechEnabled = stt.isSpeechEnabled(); - - // TODO: Initialize all different components of STT, TTS, LLM, etc. - // TODO: Handshake w/ server to validate identity - } - List getConversationByID(conversationID) { // TODO: Implement conversation IDs & retrieval from server this.conversationID = conversationID; @@ -61,7 +69,7 @@ class Server { // TODO: In case on-device STT is unavailable, use server-side STT service. if (!_speechEnabled) "Unable to process Speech-To-Text on-device."; - setPrompt(stt.getTextWhenReady() as String); + prompt = stt.getTextWhenReady() as String; httpSendRequest(); return response; @@ -73,9 +81,6 @@ class Server { return response; } - // Arbitrary prompt, used for standard text interaction rather than STT. - void setPrompt(String p) => prompt = p; - // Get the last response or request a response from the server. String getResponse() { httpSendRequest(); @@ -94,6 +99,7 @@ class Server { if(_historyEnabled) { chats = [...chats, Chat(prompt, true)]; + endpoint = "chat"; List> messageList = convertMessages(); @@ -104,6 +110,7 @@ class Server { }; } else { + endpoint = "generate"; data = { "model": "llama3", "prompt": prompt, @@ -118,11 +125,7 @@ class Server { // Send the HTTP request to the server. void httpSendRequest() async { String json = constructPrompt(); - print("\n\n$json\n\n"); - - String endpoint = "/api/"; - if(_historyEnabled) endpoint += "chat"; - else endpoint += "generate"; + print("\n\nJSON:\n$json\n\n"); // Send the HTTP POST request final serverResponse = await http.post( @@ -133,10 +136,17 @@ class Server { if (serverResponse.statusCode == 200) { Map re = jsonDecode(serverResponse.body); - response = re["response"]!; - } else { - response = ('Error: ${serverResponse.statusCode}'); + if(_historyEnabled) { + response = re["message"]["content"]; + chats = [...chats, Chat(response, false)]; + } else { + response = re["response"]; + } + } + print("\n\nresponse:\n$response"); + print("\nRaw chats variable:\n"); + for(Chat c in chats) { + print(c.content); } - chats.add(Chat(response, false)); } } \ No newline at end of file From 78e7f4b8960451f754e3718bc2dc7d6bd2cfb1a0 Mon Sep 17 00:00:00 2001 From: Joshua Date: Mon, 8 Jul 2024 19:48:14 -0600 Subject: [PATCH 17/24] dynamic chat bubbles implemented along with conversation support. currently only hard-coded dummy data. --- .../components/conversation_thread/chat.dart | 13 - .../conversation_thread/conversation.dart | 31 ++- .../conversation_thread_model.dart | 9 +- .../conversation_thread_widget.dart | 248 +++++++++--------- .../prompt_box/prompt_box_widget.dart | 10 +- mobile/app/lib/main.dart | 3 +- mobile/app/lib/pages/conversation_model.dart | 3 +- mobile/app/lib/pages/conversation_widget.dart | 3 +- .../lib/pages/conversations_list_model.dart | 2 - .../lib/pages/conversations_list_widget.dart | 78 +++--- mobile/app/lib/util/server.dart | 167 +++++++----- 11 files changed, 308 insertions(+), 259 deletions(-) diff --git a/mobile/app/lib/components/conversation_thread/chat.dart b/mobile/app/lib/components/conversation_thread/chat.dart index 4d2fbf4..ade2c2b 100644 --- a/mobile/app/lib/components/conversation_thread/chat.dart +++ b/mobile/app/lib/components/conversation_thread/chat.dart @@ -1,7 +1,3 @@ -import 'dart:convert'; -import 'package:json_annotation/json_annotation.dart'; - -@JsonSerializable() class Chat { final String content; final bool role; @@ -9,14 +5,5 @@ class Chat { // Most basic chat, text from server or from user. Chat(this.content, this.role); - String toJson() { - Map c = { - "content": content, - "role": role, - }; - - return json.encode(c); - } - // TODO: Overload constructors to allow for multimedia. } \ No newline at end of file diff --git a/mobile/app/lib/components/conversation_thread/conversation.dart b/mobile/app/lib/components/conversation_thread/conversation.dart index 5023210..ef0667c 100644 --- a/mobile/app/lib/components/conversation_thread/conversation.dart +++ b/mobile/app/lib/components/conversation_thread/conversation.dart @@ -1,15 +1,40 @@ import 'package:app/util/server.dart'; +import 'package:flutter/material.dart'; import 'chat.dart'; -class Conversation { +class Conversation with ChangeNotifier { int conversationID; + String conversationLabel; + String conversationSubLabel; List chats = []; - Conversation(this.conversationID) { - chats = Server().getConversationByID(conversationID); + Conversation(this.conversationID, this.conversationLabel, this.conversationSubLabel); + + Conversation.completeConversation(this.conversationID, this.conversationLabel, this.conversationSubLabel, this.chats); + + void add(Chat message) { + chats.add(message); + notifyListeners(); + } + + void loadChats() { + chats = Server.getChatsByConversationID(conversationID); + } + + void setLabel() { + // TODO: Need to implement + } + + void setSubLabel() { + // TODO: Need to implement } List getChats() { return chats; } + + void setLabels(String cLabel, String cSubLabel) { + conversationLabel = cLabel; + conversationSubLabel = cSubLabel; + } } \ No newline at end of file diff --git a/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart b/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart index 08eacc1..6b3197d 100644 --- a/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart +++ b/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart @@ -14,16 +14,15 @@ import 'package:cached_network_image/cached_network_image.dart'; class ConversationThreadModel extends FlutterFlowModel { final formKey = GlobalKey(); late PromptBoxModel promptBoxModel; - late int ?conversationID; late Conversation conversation; - // If there's a conversation ID, retrieve the conversation history. - // Otherwise, start a new conversation with user defaults. - ConversationThreadModel(this.conversationID); + void updateConversation() { + conversation = Server.getLoadedConversation(); + } @override void initState(BuildContext context) { - conversation = Conversation(conversationID!); + conversation = Server.getLoadedConversation(); promptBoxModel = createModel(context, () => PromptBoxModel()); } diff --git a/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart b/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart index 6668fd6..f096d00 100644 --- a/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart +++ b/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart @@ -1,4 +1,6 @@ import 'package:app/components/conversation_thread/conversation.dart'; +import 'package:app/util/server.dart'; +import 'package:provider/provider.dart'; import '/components/conversation_thread/chat_bubble_widget.dart'; import 'package:app/components/conversation_thread/conversation_thread_model.dart'; @@ -32,7 +34,7 @@ class _ConversationThreadWidgetState extends State { @override void initState() { super.initState(); - _model = createModel(context, () => ConversationThreadModel(0)); // TODO + _model = createModel(context, () => ConversationThreadModel()); } @override @@ -44,132 +46,140 @@ class _ConversationThreadWidgetState extends State { @override Widget build(BuildContext context) { - Conversation c = _model.conversation; - return Container( - width: double.infinity, - height: double.infinity, - decoration: const BoxDecoration( - color: AppTheme.primaryBackground, - ), - child: Column( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: ListView.builder( - itemCount: c.chats.length, - padding: const EdgeInsets.fromLTRB(0, 12, 0, 24,), - reverse: false, - scrollDirection: Axis.vertical, - itemBuilder: (BuildContext context, int index) { - return ChatBubbleWidget(c: c.chats[index]); - }, + return Container( + width: double.infinity, + height: double.infinity, + decoration: const BoxDecoration( + color: AppTheme.primaryBackground, + ), + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: ListenableBuilder( + listenable: _model.conversation, + builder: (BuildContext context, Widget? child) { + return ListView.builder( + itemCount: Server.getLoadedConversation().chats.length, + padding: const EdgeInsets.fromLTRB(0, 12, 0, 24,), + reverse: false, + scrollDirection: Axis.vertical, + itemBuilder: (BuildContext context, int index) { + if(Server.getLoadedConversation().chats.last.role) { + return ChatBubbleWidget(c: Server.getLoadedConversation().chats[index]); + } else { + + } + }, + ); + }, + ), ), - ), - Container( - width: double.infinity, - decoration: const BoxDecoration( - color: AppTheme.secondaryBackground, - boxShadow: [ - BoxShadow( - blurRadius: 3, - color: Color(0x33000000), - offset: Offset( - 0, - -2, - ), - ) - ], - ), - // TODO - child: Column( - mainAxisSize: MainAxisSize.max, - children: [ - Row( - mainAxisSize: MainAxisSize.max, - children: [ - Expanded( - child: Padding( - padding: const EdgeInsetsDirectional.fromSTEB(0, 12, 0, 0), - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Row( - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // FlutterFlowMediaDisplay( - // path: '', - // imageBuilder: (path) => ClipRRect( - // borderRadius: BorderRadius.circular(8), - // child: CachedNetworkImage( - // fadeInDuration: const Duration(milliseconds: 500), - // fadeOutDuration: - // const Duration(milliseconds: 500), - // imageUrl: path, - // width: 120, - // height: 100, - // fit: BoxFit.cover, - // ), - // ), - // TODO - // videoPlayerBuilder: (path) => - // FlutterFlowVideoPlayer( - // path: path, - // width: 300, - // autoPlay: false, - // looping: true, - // showControls: true, - // allowFullScreen: true, - // allowPlaybackSpeedMenu: false, - // ), - // ), - Align( - alignment: const AlignmentDirectional(-1, -1), - // TODO: Make the delete button conditional upon media upload. - child: FlutterFlowIconButton( - borderColor: - AppTheme.error, - borderRadius: 20, - borderWidth: 2, - buttonSize: 40, - fillColor: AppTheme.primaryBackground, - icon: const Icon( - Icons.delete_outline_rounded, - color: AppTheme.error, - size: 24, + Container( + width: double.infinity, + decoration: const BoxDecoration( + color: AppTheme.secondaryBackground, + boxShadow: [ + BoxShadow( + blurRadius: 3, + color: Color(0x33000000), + offset: Offset( + 0, + -2, + ), + ) + ], + ), + // TODO + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Expanded( + child: Padding( + padding: const EdgeInsetsDirectional.fromSTEB(0, 12, 0, 0), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // FlutterFlowMediaDisplay( + // path: '', + // imageBuilder: (path) => ClipRRect( + // borderRadius: BorderRadius.circular(8), + // child: CachedNetworkImage( + // fadeInDuration: const Duration(milliseconds: 500), + // fadeOutDuration: + // const Duration(milliseconds: 500), + // imageUrl: path, + // width: 120, + // height: 100, + // fit: BoxFit.cover, + // ), + // ), + // TODO + // videoPlayerBuilder: (path) => + // FlutterFlowVideoPlayer( + // path: path, + // width: 300, + // autoPlay: false, + // looping: true, + // showControls: true, + // allowFullScreen: true, + // allowPlaybackSpeedMenu: false, + // ), + // ), + Align( + alignment: const AlignmentDirectional(-1, -1), + // TODO: Make the delete button conditional upon media upload. + child: FlutterFlowIconButton( + borderColor: + AppTheme.error, + borderRadius: 20, + borderWidth: 2, + buttonSize: 40, + fillColor: AppTheme.primaryBackground, + icon: const Icon( + Icons.delete_outline_rounded, + color: AppTheme.error, + size: 24, + ), + onPressed: () { + print('IconButton pressed ...'); + }, ), - onPressed: () { - print('IconButton pressed ...'); - }, ), - ), - ] - .divide(const SizedBox(width: 8)) - .addToStart(const SizedBox(width: 16)) - .addToEnd(const SizedBox(width: 16)), + ] + .divide(const SizedBox(width: 8)) + .addToStart(const SizedBox(width: 16)) + .addToEnd(const SizedBox(width: 16)), + ), ), ), ), - ), - ], - ), - Form( - key: _model.formKey, - autovalidateMode: AutovalidateMode.disabled, - child: Padding( - padding: const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 8), - child: wrapWithModel( - model: _model.promptBoxModel, - updateCallback: () => setState(() {}), - child: const PromptBoxWidget(), + ], + ), + Form( + key: _model.formKey, + autovalidateMode: AutovalidateMode.disabled, + child: Padding( + padding: const EdgeInsetsDirectional.fromSTEB(0, 0, 0, 8), + child: wrapWithModel( + model: _model.promptBoxModel, + updateCallback: () => setState(() {}), + child: const PromptBoxWidget(), + ), ), ), - ), - ], + ], + ), ), - ), - ], - ), - ); + ], + ), + ); } } diff --git a/mobile/app/lib/components/prompt_box/prompt_box_widget.dart b/mobile/app/lib/components/prompt_box/prompt_box_widget.dart index d149eea..3ab8ee1 100644 --- a/mobile/app/lib/components/prompt_box/prompt_box_widget.dart +++ b/mobile/app/lib/components/prompt_box/prompt_box_widget.dart @@ -2,8 +2,6 @@ import 'package:app/util/server.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:easy_debounce/easy_debounce.dart'; import 'package:flutter/material.dart'; -import 'package:google_fonts/google_fonts.dart'; -import 'package:provider/provider.dart'; import 'package:app/theme.dart'; @@ -221,7 +219,8 @@ class _PromptBoxWidgetState extends State { validator: _model.textControllerValidator.asValidator(context), textInputAction: TextInputAction.send, // "Send" on keyboard onFieldSubmitted: (text) { - updateFromLatestInput(text); // TODO: Connect with multimedia + // TODO: Clear text box + Server.respondWhenReady(text); // TODO: Connect with multimedia }, ), ), @@ -233,9 +232,4 @@ class _PromptBoxWidgetState extends State { ), ); } - - void updateFromLatestInput(String input) { - Server().getResponseWhenReady(input); - // TODO: Refresh the page with the prompt & response. - } } diff --git a/mobile/app/lib/main.dart b/mobile/app/lib/main.dart index 30672db..ebc8243 100644 --- a/mobile/app/lib/main.dart +++ b/mobile/app/lib/main.dart @@ -53,13 +53,12 @@ void main() { class MyApp extends ConsumerWidget { MyApp({super.key}); - final selectedIndexProvider = StateProvider((_) => 2); // Initial state + final selectedIndexProvider = StateProvider((_) => 1); // Initial state // This list holds the pages to navigate between final List _pages = [ const Center(child: SettingsWidget()), const Center(child: ConversationsListWidget()), - const Center(child: ConversationWidget()), ]; // This widget is the root of your application. diff --git a/mobile/app/lib/pages/conversation_model.dart b/mobile/app/lib/pages/conversation_model.dart index 4566fad..115bbf0 100644 --- a/mobile/app/lib/pages/conversation_model.dart +++ b/mobile/app/lib/pages/conversation_model.dart @@ -15,8 +15,7 @@ class ConversationModel extends FlutterFlowModel { @override void initState(BuildContext context) { - conversationThreadModel = - createModel(context, () => ConversationThreadModel(0)); // TODO + conversationThreadModel = createModel(context, () => ConversationThreadModel()); // TODO modelItemModel = createModel(context, () => ModelItemModel()); } diff --git a/mobile/app/lib/pages/conversation_widget.dart b/mobile/app/lib/pages/conversation_widget.dart index a22bbba..556478b 100644 --- a/mobile/app/lib/pages/conversation_widget.dart +++ b/mobile/app/lib/pages/conversation_widget.dart @@ -19,8 +19,9 @@ class ConversationWidget extends StatefulWidget { class _ConversationWidgetState extends State { late ConversationModel _model; - final scaffoldKey = GlobalKey(); + _ConversationWidgetState(); + final scaffoldKey = GlobalKey(); @override void initState() { super.initState(); diff --git a/mobile/app/lib/pages/conversations_list_model.dart b/mobile/app/lib/pages/conversations_list_model.dart index 23245b2..c4ad379 100644 --- a/mobile/app/lib/pages/conversations_list_model.dart +++ b/mobile/app/lib/pages/conversations_list_model.dart @@ -1,8 +1,6 @@ import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'conversations_list_widget.dart' show ConversationsListWidget; import 'package:flutter/material.dart'; -import 'package:google_fonts/google_fonts.dart'; -import 'package:provider/provider.dart'; class ConversationsListModel extends FlutterFlowModel { diff --git a/mobile/app/lib/pages/conversations_list_widget.dart b/mobile/app/lib/pages/conversations_list_widget.dart index 4f48bce..7df996a 100644 --- a/mobile/app/lib/pages/conversations_list_widget.dart +++ b/mobile/app/lib/pages/conversations_list_widget.dart @@ -1,7 +1,9 @@ +import 'package:app/components/conversation_thread/conversation.dart'; import 'package:app/theme.dart'; import 'package:app/pages/conversation_widget.dart'; import 'package:app/components/conversation_options/conversation_options_widget.dart'; +import 'package:app/util/server.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:flutter/material.dart'; @@ -10,19 +12,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'conversations_list_model.dart'; export 'conversations_list_model.dart'; -final conversationIndex = ValueNotifier(0); - -// State controller using flutter_riverpod API. -// This handles changing between different conversations. -class MyState with ChangeNotifier { - int get selectedIndex => conversationIndex.value; - - set selectedIndex(int value) { - conversationIndex.value = value; - notifyListeners(); - } -} - class ConversationsListWidget extends StatefulWidget { const ConversationsListWidget({super.key}); @@ -35,17 +24,24 @@ class _ConversationsListWidgetState extends State { late ConversationsListModel _model; final selectedIndexProvider = StateProvider((_) => 0); // Initial state - final List conversations = [ - // TODO: Dynamically generate list of pages based upon existing conversations. - // This requires retrievable conversations from the server side. - const Center(child: ConversationWidget()), - ]; + late List conversations = []; + // = [ + // // TODO: Dynamically generate list of pages based upon existing conversations. + // // This requires retrievable conversations from the server side. + // const Center(child: ConversationWidget()), + // ]; final scaffoldKey = GlobalKey(); @override void initState() { super.initState(); + Server().init(); + List convos = Server.conversations; + for(Conversation c in convos) { + conversations.add(const ConversationWidget()); + } + _model = createModel(context, () => ConversationsListModel()); } @@ -57,6 +53,7 @@ class _ConversationsListWidgetState extends State { @override Widget build(BuildContext context) { + List conversations = Server.getConversationsList(); return GestureDetector( onTap: () => _model.unfocusNode.canRequestFocus ? FocusScope.of(context).requestFocus(_model.unfocusNode) @@ -77,12 +74,13 @@ class _ConversationsListWidgetState extends State { Expanded( child: Align( alignment: const AlignmentDirectional(0, 0), - child: ListView( + child: ListView.builder( + itemCount: conversations.length, padding: EdgeInsets.zero, reverse: true, scrollDirection: Axis.vertical, - children: [ - Align( + itemBuilder: (context, index) { + return Align( alignment: const AlignmentDirectional(0, 0), child: InkWell( splashColor: Colors.transparent, @@ -101,9 +99,7 @@ class _ConversationsListWidgetState extends State { Container( width: 100, height: 100, - decoration: const BoxDecoration( - color: AppTheme.secondaryBackground, - ), + decoration: const BoxDecoration(color: AppTheme.secondaryBackground,), child: const Align( alignment: AlignmentDirectional(0, 0), child: Icon( @@ -120,28 +116,24 @@ class _ConversationsListWidgetState extends State { width: 100, height: 100, constraints: BoxConstraints( - minWidth: - MediaQuery.sizeOf(context).width, + minWidth: MediaQuery.sizeOf(context).width, ), decoration: const BoxDecoration( color: AppTheme.secondaryBackground, shape: BoxShape.rectangle, ), - child: const Column( + child: Column( mainAxisSize: MainAxisSize.max, - mainAxisAlignment: - MainAxisAlignment.spaceEvenly, - crossAxisAlignment: - CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.center, children: [ Align( - alignment: - AlignmentDirectional(-1, 0), + alignment: const AlignmentDirectional(-1, 0), child: Row( mainAxisSize: MainAxisSize.max, children: [ Text( - 'Hello World', // TODO: Dynamically obtain conversation title from server. + conversations[index].conversationLabel, style: AppTheme.headlineLarge, ), ], @@ -149,13 +141,13 @@ class _ConversationsListWidgetState extends State { ), Align( alignment: - AlignmentDirectional(-1, 0), + const AlignmentDirectional(-1, 0), child: Row( mainAxisSize: MainAxisSize.max, children: [ Expanded( child: Text( - 'Hello World', // TODO: Summary or list of models used + conversations[index].conversationSubLabel, style: AppTheme.bodyMedium, ), ), @@ -183,15 +175,23 @@ class _ConversationsListWidgetState extends State { ), onTap: () => { // TODO: Need to implement. - print("opening existing conversation") + // Get the conversation object based on the index + // Use Navigator to push a new ConversationWidget with conversation data + Server.loadConversation(index), + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const ConversationWidget(), + ), + ) }, onLongPress: () => { // TODO: Need to implement. print("opening options") }, ), - ), - ], + ); + } ), ), ), diff --git a/mobile/app/lib/util/server.dart b/mobile/app/lib/util/server.dart index 8a82d7b..00fcf6e 100644 --- a/mobile/app/lib/util/server.dart +++ b/mobile/app/lib/util/server.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'package:app/components/conversation_thread/conversation.dart'; import 'package:http/http.dart' as http; import 'package:app/components/conversation_thread/chat.dart'; @@ -7,12 +8,34 @@ import 'package:app/util/speech_to_text.dart'; // Uses Singleton design pattern to ensure that any server references across the app are using the same instance. class Server { + // SERVER VARIABLES + static final Server _single_server = Server._internal(); + Server._internal(); + // TODO: One-time process to establish server connection is required for easy setup/maintenance. + static final url = "http://10.0.2.2:11434/api/"; + static String endpoint = ""; + static final Map headers = {'Content-Type': 'application/json'}; - // STT variables + + + // AI VARIABLES static final SpeechToText stt = SpeechToText(); static late bool _speechEnabled; - static final Server _single_server = Server._internal(); + + + // PRIVACY VARIABLES + static bool _historyEnabled = true; + static late int conversationID; + static List conversations = []; + static List loadedChats = []; + + + + // RUNTIME & STATUS VARIABLES + static String prompt = ""; + static String response = ""; + static bool initiated = false; // TODO: Verify all different components of STT, TTS, LLM, etc have been initialized. // TODO: Handshake w/ server to validate identity @@ -20,41 +43,83 @@ class Server { // Speech-To-Text engine activation. _speechEnabled = stt.isSpeechEnabled(); + // loadedChats = conversations[conversationID].chats; + return _single_server; } - Server._internal(); + void init() { + initiated = true; - // TODO: One-time process to establish server connection is required for easy setup/maintenance. - // Server variables - final url = "http://10.0.2.2:11434/api/"; - String endpoint = ""; - Map headers = {'Content-Type': 'application/json'}; - - // Status variables - bool _historyEnabled = true; + // TODO: Dynamically obtain conversations. + conversations.add( + Conversation.completeConversation( + 0, + "Test label", + "Test sub-label", + [ + (Chat("This is just a test. Only reply Yes.", true)), + (Chat("Yes.", false)), + ] + ) + ); - // Context variables - List chats = [ - (Chat("This is just a test. Only reply Yes.", true)), - (Chat("Yes.", false)), - ]; - - // Runtime variables - String prompt = ""; - String response = ""; - int conversationID = 0; - - List getConversationByID(conversationID) { - // TODO: Implement conversation IDs & retrieval from server - this.conversationID = conversationID; - return chats; + conversations.add( + Conversation.completeConversation( + 1, + "Label test", + "Sub-label Test", + [ + (Chat("A different test. Only reply No.", true)), + (Chat("No.", false)), + ] + ) + ); } - // TODO: Integrate with getConversationByID() - List> convertMessages() { + + + // PRIMARY INTERFACE + static Future sttGetResponseWhenReady() async { + // TODO: In case on-device STT is unavailable, use server-side STT service. + if (!_speechEnabled) "Unable to process Speech-To-Text on-device."; + + prompt = stt.getTextWhenReady() as String; + conversations[conversationID].add(Chat(prompt, true)); + httpSendRequest(); + conversations[conversationID].add(Chat(response, false)); + + return response; + } + + static void respondWhenReady(String p) async { + prompt = p; + conversations[conversationID].add(Chat(prompt, true)); + httpSendRequest(); + } + + + + // CONVERSATION MANAGEMENT + static List getConversationsList() { return conversations; } + + static Conversation getLoadedConversation() { return conversations[conversationID]; } + + // Load conversation should be called prior to any other server requests. + static void loadConversation(id) { + conversationID = id; + loadedChats = conversations[conversationID].chats; + } + + static List getChatsByConversationID(id) { return conversations[id].chats; } + + + + // SERVER UTILS + static List> convertMessages() { + // TODO: Integrate with getConversationByID() List> messageList = []; - for (Chat c in chats) { + for (Chat c in loadedChats) { messageList.add({ "role": c.role ? "user" : "assistant", "content": c.content, @@ -64,41 +129,15 @@ class Server { return messageList; } - // Using native on-device Speech-To-Text capability, get the server response when ready. - Future sttGetResponseWhenReady() async { - // TODO: In case on-device STT is unavailable, use server-side STT service. - if (!_speechEnabled) "Unable to process Speech-To-Text on-device."; - - prompt = stt.getTextWhenReady() as String; - httpSendRequest(); - - return response; - } - - Future getResponseWhenReady(String p) async { - prompt = p; - httpSendRequest(); - return response; - } - - // Get the last response or request a response from the server. - String getResponse() { - httpSendRequest(); - return response; - } - - // Get the latest prompt. - String getPrompt() => prompt; - // Formats the prompt in JSON. - String constructPrompt() { + static String constructPrompt() { // TODO: Allow for additional flags, ie. continuous conversation, images, etc. // TODO: Allow for toggleable states between stream // TODO: Allow for different models to be dynamically selected. Map data; if(_historyEnabled) { - chats = [...chats, Chat(prompt, true)]; + loadedChats = [...loadedChats, Chat(prompt, true)]; endpoint = "chat"; List> messageList = convertMessages(); @@ -123,9 +162,8 @@ class Server { } // Send the HTTP request to the server. - void httpSendRequest() async { + static void httpSendRequest() async { String json = constructPrompt(); - print("\n\nJSON:\n$json\n\n"); // Send the HTTP POST request final serverResponse = await http.post( @@ -138,15 +176,14 @@ class Server { Map re = jsonDecode(serverResponse.body); if(_historyEnabled) { response = re["message"]["content"]; - chats = [...chats, Chat(response, false)]; + loadedChats = [...loadedChats, Chat(response, false)]; } else { response = re["response"]; } } - print("\n\nresponse:\n$response"); - print("\nRaw chats variable:\n"); - for(Chat c in chats) { - print(c.content); - } + conversations[conversationID].add(Chat(response, false)); + + prompt = ""; + response = ""; } } \ No newline at end of file From 097b99e0e65213a6a602564efe42087d85373418 Mon Sep 17 00:00:00 2001 From: Joshua Date: Tue, 9 Jul 2024 17:36:21 -0600 Subject: [PATCH 18/24] fixed bugs relating to ui and duplicating conversations. --- mobile/app/devtools_options.yaml | 3 + mobile/app/ios/Podfile.lock | 162 ++++++++++++++++++ .../app/ios/Runner.xcodeproj/project.pbxproj | 120 ++++++++++++- .../contents.xcworkspacedata | 3 + mobile/app/ios/Runner/AppDelegate.swift | 2 +- mobile/app/ios/Runner/Info.plist | 12 +- .../chat_bubble_widget.dart | 3 + .../conversation_thread_model.dart | 6 +- .../conversation_thread_widget.dart | 16 +- .../prompt_box/prompt_box_widget.dart | 1 + mobile/app/lib/main.dart | 6 +- mobile/app/lib/pages/conversation_model.dart | 8 +- mobile/app/lib/pages/conversation_widget.dart | 14 +- .../lib/pages/conversations_list_widget.dart | 25 ++- mobile/app/lib/util/server.dart | 33 ++-- 15 files changed, 352 insertions(+), 62 deletions(-) create mode 100644 mobile/app/devtools_options.yaml create mode 100644 mobile/app/ios/Podfile.lock diff --git a/mobile/app/devtools_options.yaml b/mobile/app/devtools_options.yaml new file mode 100644 index 0000000..fa0b357 --- /dev/null +++ b/mobile/app/devtools_options.yaml @@ -0,0 +1,3 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: diff --git a/mobile/app/ios/Podfile.lock b/mobile/app/ios/Podfile.lock new file mode 100644 index 0000000..a494a6c --- /dev/null +++ b/mobile/app/ios/Podfile.lock @@ -0,0 +1,162 @@ +PODS: + - apivideo_live_stream (0.0.1): + - ApiVideoLiveStream (= 1.2.2) + - Flutter + - HaishinKit (= 1.3.0) + - ApiVideoLiveStream (1.2.2): + - HaishinKit (= 1.3.0) + - DKImagePickerController/Core (4.3.9): + - DKImagePickerController/ImageDataManager + - DKImagePickerController/Resource + - DKImagePickerController/ImageDataManager (4.3.9) + - DKImagePickerController/PhotoGallery (4.3.9): + - DKImagePickerController/Core + - DKPhotoGallery + - DKImagePickerController/Resource (4.3.9) + - DKPhotoGallery (0.0.19): + - DKPhotoGallery/Core (= 0.0.19) + - DKPhotoGallery/Model (= 0.0.19) + - DKPhotoGallery/Preview (= 0.0.19) + - DKPhotoGallery/Resource (= 0.0.19) + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Core (0.0.19): + - DKPhotoGallery/Model + - DKPhotoGallery/Preview + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Model (0.0.19): + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Preview (0.0.19): + - DKPhotoGallery/Model + - DKPhotoGallery/Resource + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Resource (0.0.19): + - SDWebImage + - SwiftyGif + - file_picker (0.0.1): + - DKImagePickerController/PhotoGallery + - Flutter + - Flutter (1.0.0) + - flutter_credit_card (0.0.1): + - Flutter + - HaishinKit (1.3.0): + - Logboard (~> 2.3.0) + - Logboard (2.3.1) + - native_device_orientation (0.0.1): + - Flutter + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS + - pointer_interceptor_ios (0.0.1): + - Flutter + - rive_common (0.0.1): + - Flutter + - SDWebImage (5.19.4): + - SDWebImage/Core (= 5.19.4) + - SDWebImage/Core (5.19.4) + - shared_preferences_foundation (0.0.1): + - Flutter + - FlutterMacOS + - speech_to_text (0.0.1): + - Flutter + - Try + - sqflite (0.0.3): + - Flutter + - FlutterMacOS + - SwiftyGif (5.4.5) + - Try (2.1.1) + - url_launcher_ios (0.0.1): + - Flutter + - video_player_avfoundation (0.0.1): + - Flutter + - FlutterMacOS + - webview_flutter_wkwebview (0.0.1): + - Flutter + +DEPENDENCIES: + - apivideo_live_stream (from `.symlinks/plugins/apivideo_live_stream/ios`) + - file_picker (from `.symlinks/plugins/file_picker/ios`) + - Flutter (from `Flutter`) + - flutter_credit_card (from `.symlinks/plugins/flutter_credit_card/ios`) + - native_device_orientation (from `.symlinks/plugins/native_device_orientation/ios`) + - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) + - pointer_interceptor_ios (from `.symlinks/plugins/pointer_interceptor_ios/ios`) + - rive_common (from `.symlinks/plugins/rive_common/ios`) + - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) + - speech_to_text (from `.symlinks/plugins/speech_to_text/ios`) + - sqflite (from `.symlinks/plugins/sqflite/darwin`) + - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) + - video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/darwin`) + - webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/ios`) + +SPEC REPOS: + trunk: + - ApiVideoLiveStream + - DKImagePickerController + - DKPhotoGallery + - HaishinKit + - Logboard + - SDWebImage + - SwiftyGif + - Try + +EXTERNAL SOURCES: + apivideo_live_stream: + :path: ".symlinks/plugins/apivideo_live_stream/ios" + file_picker: + :path: ".symlinks/plugins/file_picker/ios" + Flutter: + :path: Flutter + flutter_credit_card: + :path: ".symlinks/plugins/flutter_credit_card/ios" + native_device_orientation: + :path: ".symlinks/plugins/native_device_orientation/ios" + path_provider_foundation: + :path: ".symlinks/plugins/path_provider_foundation/darwin" + pointer_interceptor_ios: + :path: ".symlinks/plugins/pointer_interceptor_ios/ios" + rive_common: + :path: ".symlinks/plugins/rive_common/ios" + shared_preferences_foundation: + :path: ".symlinks/plugins/shared_preferences_foundation/darwin" + speech_to_text: + :path: ".symlinks/plugins/speech_to_text/ios" + sqflite: + :path: ".symlinks/plugins/sqflite/darwin" + url_launcher_ios: + :path: ".symlinks/plugins/url_launcher_ios/ios" + video_player_avfoundation: + :path: ".symlinks/plugins/video_player_avfoundation/darwin" + webview_flutter_wkwebview: + :path: ".symlinks/plugins/webview_flutter_wkwebview/ios" + +SPEC CHECKSUMS: + apivideo_live_stream: 64fa696699cfc531e07f5d5d701c7e3cd5ce5e9d + ApiVideoLiveStream: 5842571806a2ed26671597dadd370ec38d9b9ba4 + DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c + DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60 + file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655 + Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 + flutter_credit_card: 48fb35065294868bc6a3e7f18a42f28929b84f48 + HaishinKit: 013229683accc4abe46630b04629da3ff90428b7 + Logboard: 3d98bb85de6a36b255ab637e8178eb5671c5c3a6 + native_device_orientation: 3b4cfc9565a7b879cc4fde282b3e27745e852d0d + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 + pointer_interceptor_ios: 508241697ff0947f853c061945a8b822463947c1 + rive_common: 3a4c254c6e4db7e4b9e05daeb3d1f47ae4f7bf76 + SDWebImage: 066c47b573f408f18caa467d71deace7c0f8280d + shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 + speech_to_text: b43a7d99aef037bd758ed8e45d79bbac035d2dfe + sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec + SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 + Try: 5ef669ae832617b3cee58cb2c6f99fb767a4ff96 + url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe + video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3 + webview_flutter_wkwebview: be0f0d33777f1bfd0c9fdcb594786704dbf65f36 + +PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796 + +COCOAPODS: 1.15.2 diff --git a/mobile/app/ios/Runner.xcodeproj/project.pbxproj b/mobile/app/ios/Runner.xcodeproj/project.pbxproj index 8aaafb2..5884ae8 100644 --- a/mobile/app/ios/Runner.xcodeproj/project.pbxproj +++ b/mobile/app/ios/Runner.xcodeproj/project.pbxproj @@ -8,6 +8,8 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 1A842A08FCF2E60F2DC38967 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 72D644A2E498C9BA5622C54F /* Pods_RunnerTests.framework */; }; + 23394E45189304C0C8B7B28E /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 56C8C7368AE78ACE8607CD9E /* Pods_Runner.framework */; }; 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 */; }; @@ -40,14 +42,21 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 0151E46117D4D747AD6AC5CA /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 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 = ""; }; + 2B088EE7CCB52F9F9F771147 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; 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 = ""; }; + 4A35B625628A126CB966D9D3 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + 55D68109A360AC60F8F453F3 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + 56C8C7368AE78ACE8607CD9E /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 72D644A2E498C9BA5622C54F /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 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 = ""; }; + 85190F9D33FFEE471AA1BBDF /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.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; }; @@ -55,6 +64,7 @@ 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 = ""; }; + E93E580DD876AE87CAF169F7 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -62,12 +72,34 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 23394E45189304C0C8B7B28E /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F46E2B0305A6E6E475DD5131 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 1A842A08FCF2E60F2DC38967 /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 1DADEF4AA78CE7076F315AFE /* Pods */ = { + isa = PBXGroup; + children = ( + 0151E46117D4D747AD6AC5CA /* Pods-Runner.debug.xcconfig */, + 2B088EE7CCB52F9F9F771147 /* Pods-Runner.release.xcconfig */, + 85190F9D33FFEE471AA1BBDF /* Pods-Runner.profile.xcconfig */, + 4A35B625628A126CB966D9D3 /* Pods-RunnerTests.debug.xcconfig */, + E93E580DD876AE87CAF169F7 /* Pods-RunnerTests.release.xcconfig */, + 55D68109A360AC60F8F453F3 /* Pods-RunnerTests.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; 331C8082294A63A400263BE5 /* RunnerTests */ = { isa = PBXGroup; children = ( @@ -94,6 +126,8 @@ 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, 331C8082294A63A400263BE5 /* RunnerTests */, + 1DADEF4AA78CE7076F315AFE /* Pods */, + A3D098F5CC733F52B6D01ACB /* Frameworks */, ); sourceTree = ""; }; @@ -121,6 +155,15 @@ path = Runner; sourceTree = ""; }; + A3D098F5CC733F52B6D01ACB /* Frameworks */ = { + isa = PBXGroup; + children = ( + 56C8C7368AE78ACE8607CD9E /* Pods_Runner.framework */, + 72D644A2E498C9BA5622C54F /* Pods_RunnerTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -128,8 +171,10 @@ isa = PBXNativeTarget; buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( + CF8C5EF2CFE445B8DBBA7041 /* [CP] Check Pods Manifest.lock */, 331C807D294A63A400263BE5 /* Sources */, 331C807F294A63A400263BE5 /* Resources */, + F46E2B0305A6E6E475DD5131 /* Frameworks */, ); buildRules = ( ); @@ -145,12 +190,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + 46398C21E369343F3454DAF3 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 86B1E22AB537DC186C27C01C /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -238,6 +285,45 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; + 46398C21E369343F3454DAF3 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 86B1E22AB537DC186C27C01C /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -253,6 +339,28 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; + CF8C5EF2CFE445B8DBBA7041 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -362,13 +470,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 6G5TWS2YU7; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.app; + PRODUCT_BUNDLE_IDENTIFIER = quaxlyqueen.dev.oneai; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -378,6 +487,7 @@ }; 331C8088294A63A400263BE5 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 4A35B625628A126CB966D9D3 /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -395,6 +505,7 @@ }; 331C8089294A63A400263BE5 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = E93E580DD876AE87CAF169F7 /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -410,6 +521,7 @@ }; 331C808A294A63A400263BE5 /* Profile */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 55D68109A360AC60F8F453F3 /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -541,13 +653,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 6G5TWS2YU7; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.app; + PRODUCT_BUNDLE_IDENTIFIER = quaxlyqueen.dev.oneai; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -563,13 +676,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 6G5TWS2YU7; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.app; + PRODUCT_BUNDLE_IDENTIFIER = quaxlyqueen.dev.oneai; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; diff --git a/mobile/app/ios/Runner.xcworkspace/contents.xcworkspacedata b/mobile/app/ios/Runner.xcworkspace/contents.xcworkspacedata index 1d526a1..21a3cc1 100644 --- a/mobile/app/ios/Runner.xcworkspace/contents.xcworkspacedata +++ b/mobile/app/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/mobile/app/ios/Runner/AppDelegate.swift b/mobile/app/ios/Runner/AppDelegate.swift index 9074fee..6266644 100644 --- a/mobile/app/ios/Runner/AppDelegate.swift +++ b/mobile/app/ios/Runner/AppDelegate.swift @@ -1,7 +1,7 @@ import Flutter import UIKit -@UIApplicationMain +@main @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, diff --git a/mobile/app/ios/Runner/Info.plist b/mobile/app/ios/Runner/Info.plist index c2e05c5..624ef01 100644 --- a/mobile/app/ios/Runner/Info.plist +++ b/mobile/app/ios/Runner/Info.plist @@ -2,6 +2,12 @@ + NSMicrophoneUsageDescription + Required for on-device speech recognition. + NSSpeechRecognitionUsageDescription + On-device speech-to-text. + CADisableMinimumFrameDurationOnPhone + CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName @@ -24,6 +30,8 @@ $(FLUTTER_BUILD_NUMBER) LSRequiresIPhoneOS + UIApplicationSupportsIndirectInputEvents + UILaunchStoryboardName LaunchScreen UIMainStoryboardFile @@ -41,9 +49,5 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight - CADisableMinimumFrameDurationOnPhone - - UIApplicationSupportsIndirectInputEvents - diff --git a/mobile/app/lib/components/conversation_thread/chat_bubble_widget.dart b/mobile/app/lib/components/conversation_thread/chat_bubble_widget.dart index 2477f96..fee84cf 100644 --- a/mobile/app/lib/components/conversation_thread/chat_bubble_widget.dart +++ b/mobile/app/lib/components/conversation_thread/chat_bubble_widget.dart @@ -8,10 +8,13 @@ class ChatBubbleWidget extends StatelessWidget{ @override Widget build(BuildContext context) { + double max_width = MediaQuery.of(context).size.width*0.65; + return Row( mainAxisAlignment: c.role ? MainAxisAlignment.end : MainAxisAlignment.start, children: [ Container( + width: max_width, padding: const EdgeInsets.all(16.0), margin: const EdgeInsets.all(8.0), decoration: BoxDecoration( diff --git a/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart b/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart index 6b3197d..6230189 100644 --- a/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart +++ b/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart @@ -14,11 +14,9 @@ import 'package:cached_network_image/cached_network_image.dart'; class ConversationThreadModel extends FlutterFlowModel { final formKey = GlobalKey(); late PromptBoxModel promptBoxModel; - late Conversation conversation; + Conversation conversation; - void updateConversation() { - conversation = Server.getLoadedConversation(); - } + ConversationThreadModel(this.conversation); @override void initState(BuildContext context) { diff --git a/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart b/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart index f096d00..8a42f02 100644 --- a/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart +++ b/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart @@ -15,15 +15,19 @@ import 'conversation_thread_model.dart'; export 'conversation_thread_model.dart'; class ConversationThreadWidget extends StatefulWidget { - const ConversationThreadWidget({super.key}); + final Conversation conversation; + const ConversationThreadWidget({super.key, required this.conversation}); @override State createState() => - _ConversationThreadWidgetState(); + _ConversationThreadWidgetState(conversation); } class _ConversationThreadWidgetState extends State { late ConversationThreadModel _model; + Conversation conversation; + + _ConversationThreadWidgetState(this.conversation); @override void setState(VoidCallback callback) { @@ -34,7 +38,7 @@ class _ConversationThreadWidgetState extends State { @override void initState() { super.initState(); - _model = createModel(context, () => ConversationThreadModel()); + _model = createModel(context, () => ConversationThreadModel(conversation)); } @override @@ -66,11 +70,7 @@ class _ConversationThreadWidgetState extends State { reverse: false, scrollDirection: Axis.vertical, itemBuilder: (BuildContext context, int index) { - if(Server.getLoadedConversation().chats.last.role) { - return ChatBubbleWidget(c: Server.getLoadedConversation().chats[index]); - } else { - - } + return ChatBubbleWidget(c: Server.getLoadedConversation().chats[index]); }, ); }, diff --git a/mobile/app/lib/components/prompt_box/prompt_box_widget.dart b/mobile/app/lib/components/prompt_box/prompt_box_widget.dart index 3ab8ee1..f80e70e 100644 --- a/mobile/app/lib/components/prompt_box/prompt_box_widget.dart +++ b/mobile/app/lib/components/prompt_box/prompt_box_widget.dart @@ -221,6 +221,7 @@ class _PromptBoxWidgetState extends State { onFieldSubmitted: (text) { // TODO: Clear text box Server.respondWhenReady(text); // TODO: Connect with multimedia + _model.textController?.clear(); // Clear the text field }, ), ), diff --git a/mobile/app/lib/main.dart b/mobile/app/lib/main.dart index ebc8243..f354c5c 100644 --- a/mobile/app/lib/main.dart +++ b/mobile/app/lib/main.dart @@ -31,10 +31,6 @@ BottomNavigationBarItem _buildNavigationItem(int index) { label = 'Home'; iconData = Icons.home; break; - case 2: - label = 'Chat'; - iconData = Icons.bubble_chart; - break; } return BottomNavigationBarItem( icon: Icon(iconData), @@ -56,7 +52,7 @@ class MyApp extends ConsumerWidget { final selectedIndexProvider = StateProvider((_) => 1); // Initial state // This list holds the pages to navigate between - final List _pages = [ + static final List _pages = [ const Center(child: SettingsWidget()), const Center(child: ConversationsListWidget()), ]; diff --git a/mobile/app/lib/pages/conversation_model.dart b/mobile/app/lib/pages/conversation_model.dart index 115bbf0..cd203e0 100644 --- a/mobile/app/lib/pages/conversation_model.dart +++ b/mobile/app/lib/pages/conversation_model.dart @@ -1,3 +1,5 @@ +import 'package:app/components/conversation_thread/conversation.dart'; + import '/components/conversation_thread/conversation_thread_widget.dart'; import '/components/model_item/model_item_widget.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; @@ -13,9 +15,13 @@ class ConversationModel extends FlutterFlowModel { // Model for ModelItem component. late ModelItemModel modelItemModel; + late Conversation conversation; + + ConversationModel(this.conversation); + @override void initState(BuildContext context) { - conversationThreadModel = createModel(context, () => ConversationThreadModel()); // TODO + conversationThreadModel = createModel(context, () => ConversationThreadModel(conversation)); // TODO modelItemModel = createModel(context, () => ModelItemModel()); } diff --git a/mobile/app/lib/pages/conversation_widget.dart b/mobile/app/lib/pages/conversation_widget.dart index 556478b..d1ebce3 100644 --- a/mobile/app/lib/pages/conversation_widget.dart +++ b/mobile/app/lib/pages/conversation_widget.dart @@ -1,3 +1,5 @@ +import 'package:app/components/conversation_thread/conversation.dart'; + import '/components/conversation_details_overlay/conversation_details_overlay_widget.dart'; import '/components/conversation_thread/conversation_thread_widget.dart'; import '/components/model_item/model_item_widget.dart'; @@ -10,22 +12,24 @@ import 'conversation_model.dart'; export 'conversation_model.dart'; class ConversationWidget extends StatefulWidget { - const ConversationWidget({super.key}); + final Conversation conversation; + const ConversationWidget({super.key, required this.conversation}); @override - State createState() => _ConversationWidgetState(); + State createState() => _ConversationWidgetState(conversation); } class _ConversationWidgetState extends State { late ConversationModel _model; + Conversation conversation; - _ConversationWidgetState(); + _ConversationWidgetState(this.conversation); final scaffoldKey = GlobalKey(); @override void initState() { super.initState(); - _model = createModel(context, () => ConversationModel()); + _model = createModel(context, () => ConversationModel(conversation)); } @override @@ -124,7 +128,7 @@ class _ConversationWidgetState extends State { child: wrapWithModel( model: _model.conversationThreadModel, updateCallback: () => setState(() {}), - child: const ConversationThreadWidget(), + child: ConversationThreadWidget(conversation: conversation), ), ), ), diff --git a/mobile/app/lib/pages/conversations_list_widget.dart b/mobile/app/lib/pages/conversations_list_widget.dart index 7df996a..aacf29e 100644 --- a/mobile/app/lib/pages/conversations_list_widget.dart +++ b/mobile/app/lib/pages/conversations_list_widget.dart @@ -2,7 +2,6 @@ import 'package:app/components/conversation_thread/conversation.dart'; import 'package:app/theme.dart'; import 'package:app/pages/conversation_widget.dart'; -import 'package:app/components/conversation_options/conversation_options_widget.dart'; import 'package:app/util/server.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; @@ -22,14 +21,8 @@ class ConversationsListWidget extends StatefulWidget { class _ConversationsListWidgetState extends State { late ConversationsListModel _model; - final selectedIndexProvider = StateProvider((_) => 0); // Initial state - late List conversations = []; - // = [ - // // TODO: Dynamically generate list of pages based upon existing conversations. - // // This requires retrievable conversations from the server side. - // const Center(child: ConversationWidget()), - // ]; + static List conversations = []; final scaffoldKey = GlobalKey(); @@ -37,9 +30,12 @@ class _ConversationsListWidgetState extends State { void initState() { super.initState(); Server().init(); - List convos = Server.conversations; - for(Conversation c in convos) { - conversations.add(const ConversationWidget()); + + if(conversations.isEmpty) { + List convoys = Server.conversations; + for(Conversation c in convoys) { + conversations.add(ConversationWidget(conversation: c,)); + } } _model = createModel(context, () => ConversationsListModel()); @@ -53,7 +49,6 @@ class _ConversationsListWidgetState extends State { @override Widget build(BuildContext context) { - List conversations = Server.getConversationsList(); return GestureDetector( onTap: () => _model.unfocusNode.canRequestFocus ? FocusScope.of(context).requestFocus(_model.unfocusNode) @@ -133,7 +128,7 @@ class _ConversationsListWidgetState extends State { mainAxisSize: MainAxisSize.max, children: [ Text( - conversations[index].conversationLabel, + conversations[index].conversation.conversationLabel, style: AppTheme.headlineLarge, ), ], @@ -147,7 +142,7 @@ class _ConversationsListWidgetState extends State { children: [ Expanded( child: Text( - conversations[index].conversationSubLabel, + conversations[index].conversation.conversationSubLabel, style: AppTheme.bodyMedium, ), ), @@ -181,7 +176,7 @@ class _ConversationsListWidgetState extends State { Navigator.push( context, MaterialPageRoute( - builder: (context) => const ConversationWidget(), + builder: (context) => ConversationWidget(conversation: Server.getLoadedConversation(),), ), ) }, diff --git a/mobile/app/lib/util/server.dart b/mobile/app/lib/util/server.dart index 00fcf6e..ff280d0 100644 --- a/mobile/app/lib/util/server.dart +++ b/mobile/app/lib/util/server.dart @@ -12,7 +12,8 @@ class Server { static final Server _single_server = Server._internal(); Server._internal(); // TODO: One-time process to establish server connection is required for easy setup/maintenance. - static final url = "http://10.0.2.2:11434/api/"; + static final url = "http://10.0.2.2:11434/api/"; // For emulated device in testing + //static final url = "http://192.168.1.68:11434/api"; // For local access to home server static String endpoint = ""; static final Map headers = {'Content-Type': 'application/json'}; @@ -55,26 +56,26 @@ class Server { conversations.add( Conversation.completeConversation( 0, - "Test label", - "Test sub-label", + "Test Conversation", + "Testing Functionality", [ - (Chat("This is just a test. Only reply Yes.", true)), - (Chat("Yes.", false)), + (Chat("Hello there! You are part of a prototype and are running locally on my Macbook Air. Please keep your responses brief as every prompt freezes my computer.", true)), + (Chat("Understood! How can I help?", false)), ] ) ); - conversations.add( - Conversation.completeConversation( - 1, - "Label test", - "Sub-label Test", - [ - (Chat("A different test. Only reply No.", true)), - (Chat("No.", false)), - ] - ) - ); + // conversations.add( + // Conversation.completeConversation( + // 1, + // "Label test", + // "Sub-label Test", + // [ + // (Chat("A different test. Only reply No.", true)), + // (Chat("No.", false)), + // ] + // ) + // ); } From 711991c7bfacb995577073e59a328c25557b7417 Mon Sep 17 00:00:00 2001 From: Joshua Date: Wed, 10 Jul 2024 13:44:16 -0600 Subject: [PATCH 19/24] added send functionality to icon. --- .../chat_bubble_widget.dart | 8 +- .../conversation_thread/conversation.dart | 2 +- .../conversation_thread_model.dart | 2 +- .../conversation_thread_widget.dart | 146 +++++----- .../model_item/model_item_widget.dart | 23 +- .../prompt_box/prompt_box_widget.dart | 53 +++- mobile/app/lib/main.dart | 15 +- mobile/app/lib/pages/conversation_widget.dart | 11 +- .../lib/pages/conversations_list_widget.dart | 275 ++++++++++-------- mobile/app/lib/theme.dart | 52 ++-- mobile/app/lib/util/model.dart | 5 + mobile/app/lib/util/server.dart | 34 ++- mobile/app/lib/util/speech_to_text.dart | 18 +- 13 files changed, 363 insertions(+), 281 deletions(-) create mode 100644 mobile/app/lib/util/model.dart diff --git a/mobile/app/lib/components/conversation_thread/chat_bubble_widget.dart b/mobile/app/lib/components/conversation_thread/chat_bubble_widget.dart index fee84cf..2233eb2 100644 --- a/mobile/app/lib/components/conversation_thread/chat_bubble_widget.dart +++ b/mobile/app/lib/components/conversation_thread/chat_bubble_widget.dart @@ -1,3 +1,4 @@ +import 'package:app/theme.dart'; import 'package:flutter/material.dart'; import 'chat.dart'; @@ -26,9 +27,12 @@ class ChatBubbleWidget extends StatelessWidget{ bottomRight: c.role ? const Radius.circular(12.0) : const Radius .circular(0.0), ), - color: c.role ? Colors.blue[200] : Colors.grey[200], + color: c.role ? AppTheme.darkest : AppTheme.dark, + ), + child: Text( + c.content, + style: AppTheme.bodyMedium, ), - child: Text(c.content), ), ], ); diff --git a/mobile/app/lib/components/conversation_thread/conversation.dart b/mobile/app/lib/components/conversation_thread/conversation.dart index ef0667c..3c883d3 100644 --- a/mobile/app/lib/components/conversation_thread/conversation.dart +++ b/mobile/app/lib/components/conversation_thread/conversation.dart @@ -18,7 +18,7 @@ class Conversation with ChangeNotifier { } void loadChats() { - chats = Server.getChatsByConversationID(conversationID); + chats = Backend.getChatsByConversationID(conversationID); } void setLabel() { diff --git a/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart b/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart index 6230189..f7fc1d2 100644 --- a/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart +++ b/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart @@ -20,7 +20,7 @@ class ConversationThreadModel extends FlutterFlowModel @override void initState(BuildContext context) { - conversation = Server.getLoadedConversation(); + conversation = Backend.getLoadedConversation(); promptBoxModel = createModel(context, () => PromptBoxModel()); } diff --git a/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart b/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart index 8a42f02..4492a07 100644 --- a/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart +++ b/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart @@ -53,9 +53,7 @@ class _ConversationThreadWidgetState extends State { return Container( width: double.infinity, height: double.infinity, - decoration: const BoxDecoration( - color: AppTheme.primaryBackground, - ), + child: Column( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -65,12 +63,12 @@ class _ConversationThreadWidgetState extends State { listenable: _model.conversation, builder: (BuildContext context, Widget? child) { return ListView.builder( - itemCount: Server.getLoadedConversation().chats.length, + itemCount: Backend.getLoadedConversation().chats.length, padding: const EdgeInsets.fromLTRB(0, 12, 0, 24,), reverse: false, scrollDirection: Axis.vertical, itemBuilder: (BuildContext context, int index) { - return ChatBubbleWidget(c: Server.getLoadedConversation().chats[index]); + return ChatBubbleWidget(c: Backend.getLoadedConversation().chats[index]); }, ); }, @@ -79,7 +77,7 @@ class _ConversationThreadWidgetState extends State { Container( width: double.infinity, decoration: const BoxDecoration( - color: AppTheme.secondaryBackground, + color: AppTheme.medium, boxShadow: [ BoxShadow( blurRadius: 3, @@ -95,74 +93,74 @@ class _ConversationThreadWidgetState extends State { child: Column( mainAxisSize: MainAxisSize.max, children: [ - Row( - mainAxisSize: MainAxisSize.max, - children: [ - Expanded( - child: Padding( - padding: const EdgeInsetsDirectional.fromSTEB(0, 12, 0, 0), - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Row( - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // FlutterFlowMediaDisplay( - // path: '', - // imageBuilder: (path) => ClipRRect( - // borderRadius: BorderRadius.circular(8), - // child: CachedNetworkImage( - // fadeInDuration: const Duration(milliseconds: 500), - // fadeOutDuration: - // const Duration(milliseconds: 500), - // imageUrl: path, - // width: 120, - // height: 100, - // fit: BoxFit.cover, - // ), - // ), - // TODO - // videoPlayerBuilder: (path) => - // FlutterFlowVideoPlayer( - // path: path, - // width: 300, - // autoPlay: false, - // looping: true, - // showControls: true, - // allowFullScreen: true, - // allowPlaybackSpeedMenu: false, - // ), - // ), - Align( - alignment: const AlignmentDirectional(-1, -1), - // TODO: Make the delete button conditional upon media upload. - child: FlutterFlowIconButton( - borderColor: - AppTheme.error, - borderRadius: 20, - borderWidth: 2, - buttonSize: 40, - fillColor: AppTheme.primaryBackground, - icon: const Icon( - Icons.delete_outline_rounded, - color: AppTheme.error, - size: 24, - ), - onPressed: () { - print('IconButton pressed ...'); - }, - ), - ), - ] - .divide(const SizedBox(width: 8)) - .addToStart(const SizedBox(width: 16)) - .addToEnd(const SizedBox(width: 16)), - ), - ), - ), - ), - ], - ), + // Row( + // mainAxisSize: MainAxisSize.max, + // children: [ + // Expanded( + // child: Padding( + // padding: const EdgeInsetsDirectional.fromSTEB(0, 12, 0, 0), + // child: SingleChildScrollView( + // scrollDirection: Axis.horizontal, + // child: Row( + // mainAxisSize: MainAxisSize.max, + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // FlutterFlowMediaDisplay( + // path: '', + // imageBuilder: (path) => ClipRRect( + // borderRadius: BorderRadius.circular(8), + // child: CachedNetworkImage( + // fadeInDuration: const Duration(milliseconds: 500), + // fadeOutDuration: + // const Duration(milliseconds: 500), + // imageUrl: path, + // width: 120, + // height: 100, + // fit: BoxFit.cover, + // ), + // ), + // // TODO + // videoPlayerBuilder: (path) => + // FlutterFlowVideoPlayer( + // path: path, + // width: 300, + // autoPlay: false, + // looping: true, + // showControls: true, + // allowFullScreen: true, + // allowPlaybackSpeedMenu: false, + // ), + // ), + // Align( + // alignment: const AlignmentDirectional(-1, -1), + // // TODO: Make the delete button conditional upon media upload. + // child: FlutterFlowIconButton( + // borderColor: + // AppTheme.error, + // borderRadius: 20, + // borderWidth: 2, + // buttonSize: 40, + // fillColor: AppTheme.primaryBackground, + // icon: const Icon( + // Icons.delete_outline_rounded, + // color: AppTheme.error, + // size: 24, + // ), + // onPressed: () { + // print('IconButton pressed ...'); + // }, + // ), + // ), + // ] + // .divide(const SizedBox(width: 8)) + // .addToStart(const SizedBox(width: 16)) + // .addToEnd(const SizedBox(width: 16)), + // ), + // ), + // ), + // ), + // ], + // ), Form( key: _model.formKey, autovalidateMode: AutovalidateMode.disabled, diff --git a/mobile/app/lib/components/model_item/model_item_widget.dart b/mobile/app/lib/components/model_item/model_item_widget.dart index 56656f7..71acbd1 100644 --- a/mobile/app/lib/components/model_item/model_item_widget.dart +++ b/mobile/app/lib/components/model_item/model_item_widget.dart @@ -1,3 +1,4 @@ +import 'package:app/components/conversation_thread/conversation.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; @@ -9,14 +10,18 @@ import 'model_item_model.dart'; export 'model_item_model.dart'; class ModelItemWidget extends StatefulWidget { - const ModelItemWidget({super.key}); + final Conversation conversation; + const ModelItemWidget({super.key, required this.conversation}); @override - State createState() => _ModelItemWidgetState(); + State createState() => _ModelItemWidgetState(conversation); } class _ModelItemWidgetState extends State { late ModelItemModel _model; + late Conversation conversation; + + _ModelItemWidgetState(this.conversation); @override void setState(VoidCallback callback) { @@ -78,22 +83,22 @@ class _ModelItemWidgetState extends State { ), ), ), - const Expanded( + Expanded( child: Padding( - padding: EdgeInsetsDirectional.fromSTEB(12, 0, 8, 0), + padding: const EdgeInsetsDirectional.fromSTEB(12, 0, 8, 0), child: Column( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - 'Randy Peterson', + Text( // Label + conversation.conversationLabel, style: AppTheme.bodyMedium, ), Padding( - padding: EdgeInsetsDirectional.fromSTEB(0, 4, 0, 0), - child: Text( - 'name@domainname.com', + padding: const EdgeInsetsDirectional.fromSTEB(0, 4, 0, 0), + child: Text( // Sub-label + conversation.conversationSubLabel, style: AppTheme.bodySmall, ), ), diff --git a/mobile/app/lib/components/prompt_box/prompt_box_widget.dart b/mobile/app/lib/components/prompt_box/prompt_box_widget.dart index f80e70e..c7a9082 100644 --- a/mobile/app/lib/components/prompt_box/prompt_box_widget.dart +++ b/mobile/app/lib/components/prompt_box/prompt_box_widget.dart @@ -17,6 +17,7 @@ class PromptBoxWidget extends StatefulWidget { class _PromptBoxWidgetState extends State { late PromptBoxModel _model; + late String entry_text; @override void setState(VoidCallback callback) { @@ -69,7 +70,7 @@ class _PromptBoxWidgetState extends State { buttonSize: 40, icon: const Icon( Icons.file_upload_outlined, - color: AppTheme.secondaryText, + color: AppTheme.darkest, size: 30, ), onPressed: () { @@ -88,7 +89,7 @@ class _PromptBoxWidgetState extends State { buttonSize: 40, icon: const Icon( Icons.photo_outlined, - color: AppTheme.secondaryText, + color: AppTheme.darkest, size: 30, ), onPressed: () { @@ -105,13 +106,28 @@ class _PromptBoxWidgetState extends State { borderRadius: 30, borderWidth: 1, buttonSize: 40, - icon: const Icon( - Icons.mic_none, - color: AppTheme.secondaryText, + icon: // Backend.stt.speechToText.isListening ? + // const Icon( + // Icons.mic_none, + // color: AppTheme.error, + // size: 30, + // ) : + const Icon( + Icons.mic_off_outlined, + color: AppTheme.darkest, size: 30, ), onPressed: () { - print('IconButton pressed ...'); + if(Backend.stt.isSpeechEnabled()) { + if(Backend.stt.speechToText.isListening) { + print('De-activating STT.'); + Backend.stt.stopListening(); + } + print('Activating STT.'); + entry_text = Backend.stt.startListening() as String; + } else { + print('Microphone is unavailable for STT'); + } }, ), ), @@ -126,7 +142,7 @@ class _PromptBoxWidgetState extends State { buttonSize: 40, icon: const Icon( Icons.videocam_outlined, - color: AppTheme.secondaryText, + color: AppTheme.darkest, size: 30, ), onPressed: () { @@ -145,7 +161,7 @@ class _PromptBoxWidgetState extends State { buttonSize: 40, icon: const Icon( Icons.camera_alt_outlined, - color: AppTheme.secondaryText, + color: AppTheme.darkest, size: 30, ), onPressed: () { @@ -206,21 +222,28 @@ class _PromptBoxWidgetState extends State { ), filled: true, fillColor: AppTheme.alternate, - suffixIcon: const Icon( - Icons.send, - color: AppTheme.primary, - size: 30, + suffixIcon: GestureDetector( + onTap: () { + // TODO: Connect with multimedia + entry_text = _model.textController.text; + Backend.respondWhenReady(entry_text); + _model.textController?.clear(); // Clear the text field + }, + child: const Icon( + Icons.send, + color: AppTheme.primary, + size: 30, + ), ), ), - style: AppTheme.bodyMedium, + style: AppTheme.displayMedium, textAlign: TextAlign.start, maxLines: 5, minLines: 1, validator: _model.textControllerValidator.asValidator(context), textInputAction: TextInputAction.send, // "Send" on keyboard onFieldSubmitted: (text) { - // TODO: Clear text box - Server.respondWhenReady(text); // TODO: Connect with multimedia + Backend.respondWhenReady(text); // TODO: Connect with multimedia _model.textController?.clear(); // Clear the text field }, ), diff --git a/mobile/app/lib/main.dart b/mobile/app/lib/main.dart index f354c5c..697b98a 100644 --- a/mobile/app/lib/main.dart +++ b/mobile/app/lib/main.dart @@ -28,13 +28,14 @@ BottomNavigationBarItem _buildNavigationItem(int index) { iconData = Icons.settings; break; case 1: - label = 'Home'; - iconData = Icons.home; + label = 'Chats'; + iconData = Icons.chat; break; } return BottomNavigationBarItem( icon: Icon(iconData), label: label, + backgroundColor: AppTheme.lightest, ); } @@ -63,15 +64,15 @@ class MyApp extends ConsumerWidget { final selectedIndex = ref.watch(selectedIndexProvider); return MaterialApp( - title: 'App', + title: 'One AI', home: Scaffold( - backgroundColor: AppTheme.secondaryBackground, + backgroundColor: AppTheme.medium, appBar: AppBar( backgroundColor: AppTheme.secondaryBackground, automaticallyImplyLeading: false, title: const Text( - 'App', - style: AppTheme.headlineLarge, + 'One AI', + style: AppTheme.displayLarge, ), actions: const [], centerTitle: false, @@ -79,6 +80,8 @@ class MyApp extends ConsumerWidget { ), body: _pages[selectedIndex], bottomNavigationBar: BottomNavigationBar( + backgroundColor: AppTheme.darkest, + selectedItemColor: AppTheme.lightest, // Set the number of items to the length of the pages list items: List.generate(_pages.length, (index) => _buildNavigationItem(index)), currentIndex: selectedIndex, diff --git a/mobile/app/lib/pages/conversation_widget.dart b/mobile/app/lib/pages/conversation_widget.dart index d1ebce3..accc610 100644 --- a/mobile/app/lib/pages/conversation_widget.dart +++ b/mobile/app/lib/pages/conversation_widget.dart @@ -59,7 +59,7 @@ class _ConversationWidgetState extends State { buttonSize: 60, icon: const Icon( Icons.arrow_back_rounded, - color: AppTheme.primaryTextColor, + color: AppTheme.lightest, size: 30, ), onPressed: () async { @@ -80,21 +80,16 @@ class _ConversationWidgetState extends State { title: wrapWithModel( model: _model.modelItemModel, updateCallback: () => setState(() {}), - child: const ModelItemWidget(), + child: ModelItemWidget(conversation: conversation,), ), actions: [ Padding( padding: const EdgeInsetsDirectional.fromSTEB(0, 8, 16, 8), // ---------- Options Button child: FlutterFlowIconButton( - borderColor: AppTheme.alternate, - borderRadius: 12, - borderWidth: 2, - buttonSize: 40, - fillColor: AppTheme.primaryBackground, icon: const Icon( Icons.more_vert, - color: AppTheme.primaryTextColor, + color: AppTheme.lightest, size: 24, ), onPressed: () async { diff --git a/mobile/app/lib/pages/conversations_list_widget.dart b/mobile/app/lib/pages/conversations_list_widget.dart index aacf29e..0eeda67 100644 --- a/mobile/app/lib/pages/conversations_list_widget.dart +++ b/mobile/app/lib/pages/conversations_list_widget.dart @@ -6,7 +6,6 @@ import 'package:app/util/server.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'conversations_list_model.dart'; export 'conversations_list_model.dart'; @@ -29,10 +28,10 @@ class _ConversationsListWidgetState extends State { @override void initState() { super.initState(); - Server().init(); + Backend().init(); if(conversations.isEmpty) { - List convoys = Server.conversations; + List convoys = Backend.conversations; for(Conversation c in convoys) { conversations.add(ConversationWidget(conversation: c,)); } @@ -55,138 +54,162 @@ class _ConversationsListWidgetState extends State { : FocusScope.of(context).unfocus(), child: SafeArea( top: true, - child: Column( - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.start, + child: Stack( children: [ - const Padding( - padding: EdgeInsetsDirectional.fromSTEB(16, 0, 0, 0), - child: Text( - 'Conversations', - style: AppTheme.labelMedium, - ), - ), - Expanded( - child: Align( - alignment: const AlignmentDirectional(0, 0), - child: ListView.builder( - itemCount: conversations.length, - padding: EdgeInsets.zero, - reverse: true, - scrollDirection: Axis.vertical, - itemBuilder: (context, index) { - return Align( - alignment: const AlignmentDirectional(0, 0), - child: InkWell( - splashColor: Colors.transparent, - focusColor: Colors.transparent, - hoverColor: Colors.transparent, - highlightColor: Colors.transparent, - // TODO - // onTap: () async { - // context.pushNamed('Conversation'); - // }, - child: Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - width: 100, - height: 100, - decoration: const BoxDecoration(color: AppTheme.secondaryBackground,), - child: const Align( - alignment: AlignmentDirectional(0, 0), - child: Icon( - Icons.settings_outlined, // TODO: Dynamically generate or allow users to color code/tag conversations. - color: AppTheme.secondaryText, - size: 48, + Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Align( + alignment: const AlignmentDirectional(0, 0), + child: ListView.builder( + itemCount: conversations.length, + padding: EdgeInsets.zero, + reverse: true, + scrollDirection: Axis.vertical, + itemBuilder: (context, index) { + return Align( + alignment: const AlignmentDirectional(0, 0), + child: InkWell( + splashColor: AppTheme.accent, + focusColor: AppTheme.lightest, + hoverColor: AppTheme.dark, + highlightColor: AppTheme.darkest, + // TODO + // onTap: () async { + // context.pushNamed('Conversation'); + // }, + child: Container( + decoration: const BoxDecoration( + //color: AppTheme.darkest, + border: Border( + top: BorderSide(width: 2.0, color: AppTheme.darkest), + ), ), - ), - ), - Expanded( - child: Align( - alignment: const AlignmentDirectional(-1, 0), - child: Container( - width: 100, - height: 100, - constraints: BoxConstraints( - minWidth: MediaQuery.sizeOf(context).width, - ), - decoration: const BoxDecoration( - color: AppTheme.secondaryBackground, - shape: BoxShape.rectangle, - ), - child: Column( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Align( + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + + children: [ + Container( + width: 100, + height: 100, + decoration: const BoxDecoration(color: AppTheme.lightest,), + child: const Align( + alignment: AlignmentDirectional(0, 0), + child: Icon( + Icons.settings_outlined, // TODO: Dynamically generate or allow users to color code/tag conversations. + color: AppTheme.secondaryText, + size: 48, + ), + ), + ), + Expanded( + child: Align( alignment: const AlignmentDirectional(-1, 0), - child: Row( - mainAxisSize: MainAxisSize.max, - children: [ - Text( - conversations[index].conversation.conversationLabel, - style: AppTheme.headlineLarge, - ), - ], - ), - ), - Align( - alignment: - const AlignmentDirectional(-1, 0), - child: Row( - mainAxisSize: MainAxisSize.max, - children: [ - Expanded( - child: Text( - conversations[index].conversation.conversationSubLabel, - style: AppTheme.bodyMedium, + child: Container( + width: 100, + height: 100, + constraints: BoxConstraints( + minWidth: MediaQuery.sizeOf(context).width, + ), + decoration: const BoxDecoration( + color: AppTheme.lightest, + shape: BoxShape.rectangle, + ), + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Align( + alignment: const AlignmentDirectional(-1, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Text( + conversations[index].conversation.conversationLabel, + style: AppTheme.labelLarge, + ), + ], + ), ), - ), - ], + Align( + alignment: + const AlignmentDirectional(-1, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Expanded( + child: Text( + conversations[index].conversation.conversationSubLabel, + style: AppTheme.labelSmall, + ), + ), + ], + ), + ), + ], + ), ), ), - ], - ), + ), + Container( + width: 100, + height: 100, + decoration: const BoxDecoration( + color: AppTheme.lightest, + ), + child: const Icon( + Icons.chevron_right_rounded, + color: AppTheme.secondaryText, + size: 48, + ), + ), + ], ), ), + onTap: () => { + Backend.loadConversation(index), + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ConversationWidget(conversation: Backend.getLoadedConversation(),), + ), + ) + }, + onLongPress: () => { + // TODO: Need to implement. + print("opening options") + }, ), - Container( - width: 100, - height: 100, - decoration: const BoxDecoration( - color: AppTheme.secondaryBackground, - ), - child: const Icon( - Icons.chevron_right_rounded, - color: AppTheme.secondaryText, - size: 48, - ), - ), - ], - ), - onTap: () => { - // TODO: Need to implement. - // Get the conversation object based on the index - // Use Navigator to push a new ConversationWidget with conversation data - Server.loadConversation(index), - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => ConversationWidget(conversation: Server.getLoadedConversation(),), - ), - ) - }, - onLongPress: () => { - // TODO: Need to implement. - print("opening options") - }, - ), - ); - } + ); + } + ), + ), + ), + ], + ), + Positioned( + bottom: 20.0, // Adjust positioning as needed + right: 20.0, // Adjust positioning as needed + child: FloatingActionButton( + onPressed: () => { + Backend.loadConversation(Backend.maxConversationID), // TODO: Dynamically get the next ID + conversations.add(ConversationWidget(conversation: Backend.getLoadedConversation())), + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ConversationWidget(conversation: Backend.getLoadedConversation(),), + ), + ), // Handle the button press + }, + backgroundColor: AppTheme.darkest, + child: const Icon( + Icons.bubble_chart, + color: AppTheme.lightest, ), ), ), diff --git a/mobile/app/lib/theme.dart b/mobile/app/lib/theme.dart index 1d38ccd..3818e8f 100644 --- a/mobile/app/lib/theme.dart +++ b/mobile/app/lib/theme.dart @@ -1,37 +1,45 @@ import 'package:flutter/material.dart'; class AppTheme { - static const primary = Colors.deepPurple; - static const secondary = Colors.deepPurpleAccent; - static const tertiary = Colors.white; - static const alternate = Colors.deepPurple; + static const darkest = Color.fromARGB(255, 45, 45, 145); + static const dark = Color.fromARGB(255, 81, 81, 196); + static const medium = Color.fromARGB(255, 107, 107, 201); + static const light = Color.fromARGB(255, 155, 155, 255); + static const lightest = Color.fromARGB(255, 172, 175, 255); + static const accent = Color.fromARGB(255, 255, 113, 0); + static const accent2 = Color.fromARGB(255, 255, 164, 79); - static const accent1 = Colors.amber; - static const accent4 = Colors.amber; + static const primaryBackground = darkest; + static const secondaryBackground = medium; + static const primaryTextColor = darkest; + static const primaryText = TextStyle(color: primaryTextColor, fontSize: 16); + static const secondaryText = dark; - static const primaryBackground = Colors.deepPurpleAccent; - static const secondaryBackground = Colors.deepPurple; + static const accent1 = lightest; + static const accent4 = accent2; + static const primary = dark; + static const secondary = medium; + static const tertiary = light; + static const alternate = lightest; - static const primaryTextColor = Colors.white; - static const primaryText = TextStyle(color: primaryTextColor, fontSize: 12); - static const secondaryText = Colors.white; - - static const bodyLarge = TextStyle(color: Colors.white, fontSize: 24); - static const bodyMedium = TextStyle(color: Colors.white, fontSize: 12); - static const bodySmall = TextStyle(color: Colors.white, fontSize: 10); + static const bodyLarge = TextStyle(color: light, fontSize: 24, fontWeight: FontWeight.bold); + static const bodyMedium = TextStyle(color: lightest, fontSize: 16); + static const bodySmall = TextStyle(color: light, fontSize: 12); static const error = Colors.red; static const info = Colors.grey; - static const displaySmall = TextStyle(color: Colors.white, fontSize: 12); + static const displayLarge = TextStyle(color: darkest, fontSize: 24, fontWeight: FontWeight.bold); + static const displayMedium = TextStyle(color: dark, fontSize: 16, fontWeight: FontWeight.bold); + static const displaySmall = TextStyle(color: lightest, fontSize: 12, fontWeight: FontWeight.bold); - static const labelLarge = TextStyle(color: Colors.black, fontSize: 24); - static const labelMedium = TextStyle(color: Colors.grey, fontSize: 16); - static const labelSmall = TextStyle(color: Colors.grey, fontSize: 12); + static const labelLarge = TextStyle(color: darkest, fontSize: 24, fontWeight: FontWeight.bold); + static const labelMedium = TextStyle(color: dark, fontSize: 16, fontWeight: FontWeight.bold); + static const labelSmall = TextStyle(color: medium, fontSize: 12, fontWeight: FontWeight.bold); - static const headlineLarge = TextStyle(color: Colors.blue, fontWeight: FontWeight.bold); - static const headlineMedium = TextStyle(color: Colors.blue, fontWeight: FontWeight.bold); - static const headlineSmall = TextStyle(color: Colors.blue, fontWeight: FontWeight.bold); + static const headlineLarge = TextStyle(color: medium, fontWeight: FontWeight.bold); + static const headlineMedium = TextStyle(color: light, fontWeight: FontWeight.bold); + static const headlineSmall = TextStyle(color: lightest, fontWeight: FontWeight.bold); static const titleLarge = TextStyle(fontFamily: 'Outfit', letterSpacing: 0, fontSize: 24); static const titleSmall = TextStyle(fontFamily: 'Outfit', letterSpacing: 0, fontSize: 16); diff --git a/mobile/app/lib/util/model.dart b/mobile/app/lib/util/model.dart new file mode 100644 index 0000000..cad0ddf --- /dev/null +++ b/mobile/app/lib/util/model.dart @@ -0,0 +1,5 @@ +class Model { + late String name; + + Model(this.name); +} \ No newline at end of file diff --git a/mobile/app/lib/util/server.dart b/mobile/app/lib/util/server.dart index ff280d0..942336b 100644 --- a/mobile/app/lib/util/server.dart +++ b/mobile/app/lib/util/server.dart @@ -6,11 +6,13 @@ import 'package:app/components/conversation_thread/chat.dart'; import 'package:app/util/speech_to_text.dart'; +import 'model.dart'; + // Uses Singleton design pattern to ensure that any server references across the app are using the same instance. -class Server { +class Backend { // SERVER VARIABLES - static final Server _single_server = Server._internal(); - Server._internal(); + static final Backend _single_backend = Backend._internal(); + Backend._internal(); // TODO: One-time process to establish server connection is required for easy setup/maintenance. static final url = "http://10.0.2.2:11434/api/"; // For emulated device in testing //static final url = "http://192.168.1.68:11434/api"; // For local access to home server @@ -28,30 +30,36 @@ class Server { // PRIVACY VARIABLES static bool _historyEnabled = true; static late int conversationID; + static late int maxConversationID; static List conversations = []; static List loadedChats = []; // RUNTIME & STATUS VARIABLES + static late Model model; static String prompt = ""; static String response = ""; static bool initiated = false; // TODO: Verify all different components of STT, TTS, LLM, etc have been initialized. // TODO: Handshake w/ server to validate identity - factory Server() { + factory Backend() { // Speech-To-Text engine activation. _speechEnabled = stt.isSpeechEnabled(); // loadedChats = conversations[conversationID].chats; - return _single_server; + return _single_backend; } void init() { initiated = true; + // TODO: Dynamically get available models and models that can be pulled. + // TODO: Dynamically get the user's default model + model = Model("llama3"); // Primarily used llama3, testing gemma2 + // TODO: Dynamically obtain conversations. conversations.add( Conversation.completeConversation( @@ -65,6 +73,8 @@ class Server { ) ); + maxConversationID = conversations.length; + // conversations.add( // Conversation.completeConversation( // 1, @@ -81,9 +91,12 @@ class Server { // PRIMARY INTERFACE + @Deprecated("Use STT interface directly and use respondWhenReady().") static Future sttGetResponseWhenReady() async { // TODO: In case on-device STT is unavailable, use server-side STT service. - if (!_speechEnabled) "Unable to process Speech-To-Text on-device."; + if (!_speechEnabled) { + return "Unable to process Speech-To-Text on-device."; + } prompt = stt.getTextWhenReady() as String; conversations[conversationID].add(Chat(prompt, true)); @@ -109,6 +122,11 @@ class Server { // Load conversation should be called prior to any other server requests. static void loadConversation(id) { conversationID = id; + if(maxConversationID == id) { + conversations.add(Conversation(conversationID, "Conversation $id", "Another AI conversation")); + maxConversationID++; + } + loadedChats = conversations[conversationID].chats; } @@ -144,7 +162,7 @@ class Server { List> messageList = convertMessages(); data = { - "model": "llama3", + "model": model.name, "messages": messageList, "stream": false, }; @@ -152,7 +170,7 @@ class Server { else { endpoint = "generate"; data = { - "model": "llama3", + "model": model.name, "prompt": prompt, "stream": false, }; diff --git a/mobile/app/lib/util/speech_to_text.dart b/mobile/app/lib/util/speech_to_text.dart index 27f9d81..60e3630 100644 --- a/mobile/app/lib/util/speech_to_text.dart +++ b/mobile/app/lib/util/speech_to_text.dart @@ -3,15 +3,15 @@ import 'package:speech_to_text/speech_to_text.dart' as stt; /// Handles standard Speech-To-Text using on-device processing. class SpeechToText { - final stt.SpeechToText _speechToText = stt.SpeechToText(); - bool _speechEnabled = false; + final stt.SpeechToText speechToText = stt.SpeechToText(); + bool speechEnabled = false; String text = ""; // Check if speech recognition is enabled. - bool isSpeechEnabled() => _speechEnabled; + bool isSpeechEnabled() => speechEnabled; Future getTextWhenReady() async { - await _speechToText.stop(); + await speechToText.stop(); return text; } @@ -22,22 +22,22 @@ class SpeechToText { void reset() => text = ""; /// Start a speech recognition session - void _startListening() async => await _speechToText.listen(onResult: _onSpeechResult); + void startListening() async => await speechToText.listen(onResult: onSpeechResult); /// Manually stop the active speech recognition session /// Note that there are also timeouts that each platform enforces /// and the SpeechToText plugin supports setting timeouts on the /// listen method. - void _stopListening() async => await _speechToText.stop(); + void stopListening() async => await speechToText.stop(); SpeechToText() { - _initSpeech(); + initSpeech(); } /// This has to happen only once per app - void _initSpeech() async => _speechEnabled = await _speechToText.initialize(); + void initSpeech() async => speechEnabled = await speechToText.initialize(); /// This is the callback that the SpeechToText plugin calls when /// the platform returns recognized words. - void _onSpeechResult(SpeechRecognitionResult result) => text = result.recognizedWords; + String onSpeechResult(SpeechRecognitionResult result) => (text = result.recognizedWords); } \ No newline at end of file From 5d2e162da8026595afd24dcd4f15496b73b7b0b9 Mon Sep 17 00:00:00 2001 From: Joshua Date: Wed, 10 Jul 2024 13:57:10 -0600 Subject: [PATCH 20/24] removed on-device STT, as android and ios already have on-device STT built into the keyboard. --- .../prompt_box/prompt_box_widget.dart | 34 ------------------- mobile/app/lib/util/speech_to_text.dart | 2 +- 2 files changed, 1 insertion(+), 35 deletions(-) diff --git a/mobile/app/lib/components/prompt_box/prompt_box_widget.dart b/mobile/app/lib/components/prompt_box/prompt_box_widget.dart index c7a9082..ad188df 100644 --- a/mobile/app/lib/components/prompt_box/prompt_box_widget.dart +++ b/mobile/app/lib/components/prompt_box/prompt_box_widget.dart @@ -98,40 +98,6 @@ class _PromptBoxWidgetState extends State { ), ), ), - Expanded( // TODO: Microphone context upload -> or STT?? - child: Padding( - padding: const EdgeInsets.all(14), - child: FlutterFlowIconButton( - borderColor: Colors.transparent, - borderRadius: 30, - borderWidth: 1, - buttonSize: 40, - icon: // Backend.stt.speechToText.isListening ? - // const Icon( - // Icons.mic_none, - // color: AppTheme.error, - // size: 30, - // ) : - const Icon( - Icons.mic_off_outlined, - color: AppTheme.darkest, - size: 30, - ), - onPressed: () { - if(Backend.stt.isSpeechEnabled()) { - if(Backend.stt.speechToText.isListening) { - print('De-activating STT.'); - Backend.stt.stopListening(); - } - print('Activating STT.'); - entry_text = Backend.stt.startListening() as String; - } else { - print('Microphone is unavailable for STT'); - } - }, - ), - ), - ), Expanded( // TODO: Video context upload child: Padding( padding: const EdgeInsets.all(14), diff --git a/mobile/app/lib/util/speech_to_text.dart b/mobile/app/lib/util/speech_to_text.dart index 60e3630..4ec5e2f 100644 --- a/mobile/app/lib/util/speech_to_text.dart +++ b/mobile/app/lib/util/speech_to_text.dart @@ -39,5 +39,5 @@ class SpeechToText { /// This is the callback that the SpeechToText plugin calls when /// the platform returns recognized words. - String onSpeechResult(SpeechRecognitionResult result) => (text = result.recognizedWords); + void onSpeechResult(SpeechRecognitionResult result) => (text = result.recognizedWords); } \ No newline at end of file From e5b69aa2b6585bef37ed7bdb944641984da00e74 Mon Sep 17 00:00:00 2001 From: Joshua Date: Wed, 10 Jul 2024 15:36:55 -0600 Subject: [PATCH 21/24] implemented basic settings into the backend, renamed files, fixed bugs, and added back button functionality --- .../conversation_thread/conversation.dart | 15 +++- .../conversation_thread_model.dart | 2 +- .../conversation_thread_widget.dart | 2 +- .../lib/components/popup/popup_widget.dart | 8 +- .../prompt_box/prompt_box_widget.dart | 5 +- mobile/app/lib/pages/conversation_widget.dart | 15 +--- .../lib/pages/conversations_list_widget.dart | 37 ++++++++- mobile/app/lib/pages/settings_widget.dart | 1 + .../lib/util/{server.dart => backend.dart} | 83 ++++++++++--------- 9 files changed, 104 insertions(+), 64 deletions(-) rename mobile/app/lib/util/{server.dart => backend.dart} (71%) diff --git a/mobile/app/lib/components/conversation_thread/conversation.dart b/mobile/app/lib/components/conversation_thread/conversation.dart index 3c883d3..7d79cde 100644 --- a/mobile/app/lib/components/conversation_thread/conversation.dart +++ b/mobile/app/lib/components/conversation_thread/conversation.dart @@ -1,4 +1,4 @@ -import 'package:app/util/server.dart'; +import 'package:app/util/backend.dart'; import 'package:flutter/material.dart'; import 'chat.dart'; @@ -6,19 +6,30 @@ class Conversation with ChangeNotifier { int conversationID; String conversationLabel; String conversationSubLabel; + bool history = true; + // History is disabled by default. This means chats are auto-deleted once the user exits that conversation. + bool conversationContext = true; // The prior prompts and responses are used as additional context for the next prompt and response. + late bool stream = false; // Update the UI with a stream of characters as received from the server, or as one text. List chats = []; Conversation(this.conversationID, this.conversationLabel, this.conversationSubLabel); Conversation.completeConversation(this.conversationID, this.conversationLabel, this.conversationSubLabel, this.chats); + void toggleHistory() => history = !history; + + void toggleConversationContext() => conversationContext = !conversationContext; + + void toggleStream() => stream = !stream; + void add(Chat message) { chats.add(message); notifyListeners(); } void loadChats() { - chats = Backend.getChatsByConversationID(conversationID); + Backend.loadConversation; + chats = Backend.loadedChats; } void setLabel() { diff --git a/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart b/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart index f7fc1d2..c95fa3a 100644 --- a/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart +++ b/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart @@ -1,7 +1,7 @@ import 'package:app/components/conversation_thread/chat_bubble_model.dart'; import 'package:app/components/conversation_thread/chat.dart'; import 'package:app/components/conversation_thread/conversation.dart'; -import 'package:app/util/server.dart'; +import 'package:app/util/backend.dart'; import 'package:app/components/prompt_box/prompt_box_widget.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; diff --git a/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart b/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart index 4492a07..a5cf391 100644 --- a/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart +++ b/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart @@ -1,5 +1,5 @@ import 'package:app/components/conversation_thread/conversation.dart'; -import 'package:app/util/server.dart'; +import 'package:app/util/backend.dart'; import 'package:provider/provider.dart'; import '/components/conversation_thread/chat_bubble_widget.dart'; diff --git a/mobile/app/lib/components/popup/popup_widget.dart b/mobile/app/lib/components/popup/popup_widget.dart index fdf314b..9ecb668 100644 --- a/mobile/app/lib/components/popup/popup_widget.dart +++ b/mobile/app/lib/components/popup/popup_widget.dart @@ -84,7 +84,7 @@ class _PopupWidgetState extends State { child: Padding( padding: EdgeInsetsDirectional.fromSTEB(0, 0, 12, 0), child: Text( - 'Refine the components modal.', + 'A popup', style: AppTheme.headlineMedium, ), ), @@ -106,12 +106,12 @@ class _PopupWidgetState extends State { ], ), const Divider( - height: 24, + height: 12, thickness: 2, color: Color(0xFFF1F4F8), ), const Text( - 'FlutterFlow is a visual development platform that allows you to easily create beautiful and responsive user interfaces for your mobile and web applications. With its drag-and-drop interface and pre-built components, you can quickly prototype and build your app without writing any code. \nAdditionally, FlutterFlow\'s real-time preview feature allows you to see your changes in real-time and make adjustments on the fly.', + 'Test', style: AppTheme.labelMedium, ), Padding( @@ -147,7 +147,7 @@ class _PopupWidgetState extends State { onPressed: () { print('Button pressed ...'); }, - text: 'Create Task', + text: 'Action', options: FFButtonOptions( width: 130, height: 40, diff --git a/mobile/app/lib/components/prompt_box/prompt_box_widget.dart b/mobile/app/lib/components/prompt_box/prompt_box_widget.dart index ad188df..0a13904 100644 --- a/mobile/app/lib/components/prompt_box/prompt_box_widget.dart +++ b/mobile/app/lib/components/prompt_box/prompt_box_widget.dart @@ -1,4 +1,4 @@ -import 'package:app/util/server.dart'; +import 'package:app/util/backend.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:easy_debounce/easy_debounce.dart'; import 'package:flutter/material.dart'; @@ -209,7 +209,8 @@ class _PromptBoxWidgetState extends State { validator: _model.textControllerValidator.asValidator(context), textInputAction: TextInputAction.send, // "Send" on keyboard onFieldSubmitted: (text) { - Backend.respondWhenReady(text); // TODO: Connect with multimedia + entry_text = _model.textController.text; + Backend.respondWhenReady(entry_text); // TODO: Connect with multimedia _model.textController?.clear(); // Clear the text field }, ), diff --git a/mobile/app/lib/pages/conversation_widget.dart b/mobile/app/lib/pages/conversation_widget.dart index accc610..26d2e4d 100644 --- a/mobile/app/lib/pages/conversation_widget.dart +++ b/mobile/app/lib/pages/conversation_widget.dart @@ -5,6 +5,7 @@ import '/components/conversation_thread/conversation_thread_widget.dart'; import '/components/model_item/model_item_widget.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:app/theme.dart'; @@ -64,17 +65,9 @@ class _ConversationWidgetState extends State { ), onPressed: () async { print("something pressed in conversation_widget.dart"); - // TODO - // context.goNamed( - // 'ConversationsList', - // extra: { - // kTransitionInfoKey: TransitionInfo( - // hasTransition: true, - // transitionType: PageTransitionType.leftToRight, - // duration: const Duration(milliseconds: 250), - // ), - // }, - // ); + // TODO: Smooth out the animation to prevent the conversations list from loading half-way up the screen. + SystemChannels.textInput.invokeMethod('TextInput.hide'); + Navigator.pop(context); }, ), title: wrapWithModel( diff --git a/mobile/app/lib/pages/conversations_list_widget.dart b/mobile/app/lib/pages/conversations_list_widget.dart index 0eeda67..0a38f42 100644 --- a/mobile/app/lib/pages/conversations_list_widget.dart +++ b/mobile/app/lib/pages/conversations_list_widget.dart @@ -2,11 +2,12 @@ import 'package:app/components/conversation_thread/conversation.dart'; import 'package:app/theme.dart'; import 'package:app/pages/conversation_widget.dart'; -import 'package:app/util/server.dart'; +import 'package:app/util/backend.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:flutter/material.dart'; +import 'package:app/components/options_dialog/options_dialog_widget.dart'; import 'conversations_list_model.dart'; export 'conversations_list_model.dart'; @@ -48,6 +49,20 @@ class _ConversationsListWidgetState extends State { @override Widget build(BuildContext context) { + // Clean conversation list prior to building widgets, to prevent bloating UI with empty conversations. + // Additionally, delete conversations that have history disabled. + for(int i = 0; i < conversations.length; i++) { + if( + conversations[i].conversation.getChats().isEmpty || + !conversations[i].conversation.history + ) { + Backend.conversations.removeAt(i); + conversations.removeAt(i); + Backend.maxConversationID--; // maxConversationID will eventually be deprecated with better ID system. + i--; + } + } + return GestureDetector( onTap: () => _model.unfocusNode.canRequestFocus ? FocusScope.of(context).requestFocus(_model.unfocusNode) @@ -181,6 +196,24 @@ class _ConversationsListWidgetState extends State { ) }, onLongPress: () => { + showModalBottomSheet( + isScrollControlled: true, + backgroundColor: + Colors.transparent, + enableDrag: false, + useSafeArea: true, + context: context, + builder: (context) { + return Padding( + padding: + MediaQuery.viewInsetsOf( + context), + child: const PopupWidget(), + ); + }, + ).then((value) => + safeSetState(() {})), + // TODO: Need to implement. print("opening options") }, @@ -197,7 +230,7 @@ class _ConversationsListWidgetState extends State { right: 20.0, // Adjust positioning as needed child: FloatingActionButton( onPressed: () => { - Backend.loadConversation(Backend.maxConversationID), // TODO: Dynamically get the next ID + Backend.loadConversation(Backend.createConversation()), // TODO: Dynamically get the next ID conversations.add(ConversationWidget(conversation: Backend.getLoadedConversation())), Navigator.push( context, diff --git a/mobile/app/lib/pages/settings_widget.dart b/mobile/app/lib/pages/settings_widget.dart index 5c62546..8876a81 100644 --- a/mobile/app/lib/pages/settings_widget.dart +++ b/mobile/app/lib/pages/settings_widget.dart @@ -1169,6 +1169,7 @@ class _SettingsWidgetState extends State focusColor: Colors.transparent, hoverColor: Colors.transparent, highlightColor: Colors.transparent, + // TODO: Should open in a browser rather than in-app? onTap: () async { await showModalBottomSheet( isScrollControlled: true, diff --git a/mobile/app/lib/util/server.dart b/mobile/app/lib/util/backend.dart similarity index 71% rename from mobile/app/lib/util/server.dart rename to mobile/app/lib/util/backend.dart index 942336b..ccd2f7b 100644 --- a/mobile/app/lib/util/server.dart +++ b/mobile/app/lib/util/backend.dart @@ -11,10 +11,10 @@ import 'model.dart'; // Uses Singleton design pattern to ensure that any server references across the app are using the same instance. class Backend { // SERVER VARIABLES - static final Backend _single_backend = Backend._internal(); + static final Backend singleBackend = Backend._internal(); Backend._internal(); // TODO: One-time process to establish server connection is required for easy setup/maintenance. - static final url = "http://10.0.2.2:11434/api/"; // For emulated device in testing + static const url = "http://10.0.2.2:11434/api/"; // For emulated device in testing //static final url = "http://192.168.1.68:11434/api"; // For local access to home server static String endpoint = ""; static final Map headers = {'Content-Type': 'application/json'}; @@ -27,16 +27,16 @@ class Backend { - // PRIVACY VARIABLES - static bool _historyEnabled = true; - static late int conversationID; - static late int maxConversationID; + // PRIVACY-FOCUSED VARIABLES + // TODO: Reorder based on most recent usage. static List conversations = []; static List loadedChats = []; - + static late Conversation loadedConversation; // RUNTIME & STATUS VARIABLES + static late int conversationID; + static late int maxConversationID; static late Model model; static String prompt = ""; static String response = ""; @@ -50,7 +50,7 @@ class Backend { // loadedChats = conversations[conversationID].chats; - return _single_backend; + return singleBackend; } void init() { @@ -61,31 +61,19 @@ class Backend { model = Model("llama3"); // Primarily used llama3, testing gemma2 // TODO: Dynamically obtain conversations. - conversations.add( - Conversation.completeConversation( - 0, - "Test Conversation", - "Testing Functionality", - [ - (Chat("Hello there! You are part of a prototype and are running locally on my Macbook Air. Please keep your responses brief as every prompt freezes my computer.", true)), - (Chat("Understood! How can I help?", false)), - ] - ) - ); + // conversations.add( + // Conversation.completeConversation( + // 0, + // "Test Conversation", + // "Testing Functionality", + // [ + // (Chat("Hello there! You are part of a prototype and are running locally on my Macbook Air. Please keep your responses brief as every prompt freezes my computer.", true)), + // (Chat("Understood! How can I help?", false)), + // ] + // ) + // ); maxConversationID = conversations.length; - - // conversations.add( - // Conversation.completeConversation( - // 1, - // "Label test", - // "Sub-label Test", - // [ - // (Chat("A different test. Only reply No.", true)), - // (Chat("No.", false)), - // ] - // ) - // ); } @@ -115,21 +103,32 @@ class Backend { // CONVERSATION MANAGEMENT + @Deprecated("") static List getConversationsList() { return conversations; } - static Conversation getLoadedConversation() { return conversations[conversationID]; } + static Conversation getLoadedConversation() { return loadedConversation; } // Load conversation should be called prior to any other server requests. static void loadConversation(id) { conversationID = id; - if(maxConversationID == id) { - conversations.add(Conversation(conversationID, "Conversation $id", "Another AI conversation")); - maxConversationID++; - } - loadedChats = conversations[conversationID].chats; + loadedConversation = conversations[id]; + loadedChats = loadedConversation.chats; } + // TODO: Better conversation ID management is needed. Preferably randomized IDs to not indicate the number of conversations. + static int createConversation() { + int id = conversations.length; + conversations.add(Conversation(id, "Conversation $id", "Another AI conversation")); + return id; + } + + static void deleteConversation(id) { + loadedChats = conversations[conversationID].chats; // TODO: Temporarily saving the chats for "undo" popup. + conversations.removeAt(id); + } + + @Deprecated('Just directly get loadedChats list. Unless multiple chats can be opened simultaneously?') static List getChatsByConversationID(id) { return conversations[id].chats; } @@ -155,7 +154,7 @@ class Backend { // TODO: Allow for different models to be dynamically selected. Map data; - if(_historyEnabled) { + if(conversations[conversationID].conversationContext) { loadedChats = [...loadedChats, Chat(prompt, true)]; endpoint = "chat"; @@ -164,7 +163,7 @@ class Backend { data = { "model": model.name, "messages": messageList, - "stream": false, + "stream": loadedConversation.stream, }; } else { @@ -172,7 +171,7 @@ class Backend { data = { "model": model.name, "prompt": prompt, - "stream": false, + "stream": loadedConversation.stream, }; } @@ -182,6 +181,8 @@ class Backend { // Send the HTTP request to the server. static void httpSendRequest() async { + // TODO: Add AES256 encryption to the prompt/messages thread. + // TODO: Add SHA256 checksum to be sent and verified to prevent/notify in case of data loss. String json = constructPrompt(); // Send the HTTP POST request @@ -193,7 +194,7 @@ class Backend { if (serverResponse.statusCode == 200) { Map re = jsonDecode(serverResponse.body); - if(_historyEnabled) { + if(conversations[conversationID].conversationContext) { response = re["message"]["content"]; loadedChats = [...loadedChats, Chat(response, false)]; } else { From cbd040f57b6a3cf8f47186e1c2e554c29acb382f Mon Sep 17 00:00:00 2001 From: Joshua Date: Wed, 10 Jul 2024 19:15:03 -0600 Subject: [PATCH 22/24] added multimedia support and STT. STT and photos are have bugs but are functional. file upload is conceptualized but will require additional backend work. video is on hold for now. --- mobile/app/README.md | 2 + mobile/app/android/app/build.gradle | 4 +- mobile/app/ios/Podfile.lock | 12 + mobile/app/ios/Runner/Info.plist | 10 +- .../conversation_model.dart | 0 .../components/conversation_thread/chat.dart | 27 +- .../chat_bubble_widget.dart | 21 +- .../conversation_thread/conversation.dart | 12 +- .../conversation_thread_model.dart | 2 +- .../conversation_thread_widget.dart | 3 +- .../conversation_widget.dart | 6 +- .../conversations_list_model.dart | 0 .../conversations_list_widget.dart | 13 +- .../image_details_model.dart | 0 .../image_details_widget.dart | 0 mobile/app/lib/components/no_chats_model.dart | 13 - .../app/lib/components/no_chats_widget.dart | 8 - .../prompt_box/prompt_box_widget.dart | 141 +++++--- .../{pages => components}/settings_model.dart | 0 .../settings_widget.dart | 0 .../{pages => components}/setup_model.dart | 0 .../{pages => components}/setup_widget.dart | 0 .../support_form_model.dart | 0 .../support_form_widget.dart | 4 +- .../lib/{ => components}/util/backend.dart | 84 +++-- .../app/lib/{ => components}/util/model.dart | 0 .../{ => components}/util/speech_to_text.dart | 18 +- .../app/lib/components/util/usage_queue.dart | 317 ++++++++++++++++++ mobile/app/lib/main.dart | 22 +- .../flutter/generated_plugin_registrant.cc | 4 + .../app/linux/flutter/generated_plugins.cmake | 1 + .../Flutter/GeneratedPluginRegistrant.swift | 2 + mobile/app/pubspec.lock | 146 +++++++- mobile/app/pubspec.yaml | 3 + .../flutter/generated_plugin_registrant.cc | 3 + .../windows/flutter/generated_plugins.cmake | 1 + 36 files changed, 742 insertions(+), 137 deletions(-) rename mobile/app/lib/{pages => components}/conversation_model.dart (100%) rename mobile/app/lib/{pages => components}/conversation_widget.dart (96%) rename mobile/app/lib/{pages => components}/conversations_list_model.dart (100%) rename mobile/app/lib/{pages => components}/conversations_list_widget.dart (97%) rename mobile/app/lib/{pages => components}/image_details_model.dart (100%) rename mobile/app/lib/{pages => components}/image_details_widget.dart (100%) delete mode 100644 mobile/app/lib/components/no_chats_model.dart rename mobile/app/lib/{pages => components}/settings_model.dart (100%) rename mobile/app/lib/{pages => components}/settings_widget.dart (100%) rename mobile/app/lib/{pages => components}/setup_model.dart (100%) rename mobile/app/lib/{pages => components}/setup_widget.dart (100%) rename mobile/app/lib/{pages => components}/support_form_model.dart (100%) rename mobile/app/lib/{pages => components}/support_form_widget.dart (99%) rename mobile/app/lib/{ => components}/util/backend.dart (72%) rename mobile/app/lib/{ => components}/util/model.dart (100%) rename mobile/app/lib/{ => components}/util/speech_to_text.dart (68%) create mode 100644 mobile/app/lib/components/util/usage_queue.dart diff --git a/mobile/app/README.md b/mobile/app/README.md index b73cfb0..d67227f 100644 --- a/mobile/app/README.md +++ b/mobile/app/README.md @@ -14,3 +14,5 @@ A few resources to get you started if this is your first Flutter project: 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. + +# TODO \ No newline at end of file diff --git a/mobile/app/android/app/build.gradle b/mobile/app/android/app/build.gradle index d277963..1ce74cc 100644 --- a/mobile/app/android/app/build.gradle +++ b/mobile/app/android/app/build.gradle @@ -24,7 +24,7 @@ if (flutterVersionName == null) { } android { - namespace = "com.example.app" + namespace = "quaxlyqueen.dev.oneai" compileSdk = flutter.compileSdkVersion ndkVersion = flutter.ndkVersion @@ -35,7 +35,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId = "com.example.app" + applicationId = "quaxlyqueen.dev.oneai" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. minSdk = flutter.minSdkVersion diff --git a/mobile/app/ios/Podfile.lock b/mobile/app/ios/Podfile.lock index a494a6c..c9f0587 100644 --- a/mobile/app/ios/Podfile.lock +++ b/mobile/app/ios/Podfile.lock @@ -5,6 +5,8 @@ PODS: - HaishinKit (= 1.3.0) - ApiVideoLiveStream (1.2.2): - HaishinKit (= 1.3.0) + - camera_avfoundation (0.0.1): + - Flutter - DKImagePickerController/Core (4.3.9): - DKImagePickerController/ImageDataManager - DKImagePickerController/Resource @@ -44,6 +46,8 @@ PODS: - Flutter - HaishinKit (1.3.0): - Logboard (~> 2.3.0) + - image_picker_ios (0.0.1): + - Flutter - Logboard (2.3.1) - native_device_orientation (0.0.1): - Flutter @@ -78,9 +82,11 @@ PODS: DEPENDENCIES: - apivideo_live_stream (from `.symlinks/plugins/apivideo_live_stream/ios`) + - camera_avfoundation (from `.symlinks/plugins/camera_avfoundation/ios`) - file_picker (from `.symlinks/plugins/file_picker/ios`) - Flutter (from `Flutter`) - flutter_credit_card (from `.symlinks/plugins/flutter_credit_card/ios`) + - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - native_device_orientation (from `.symlinks/plugins/native_device_orientation/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - pointer_interceptor_ios (from `.symlinks/plugins/pointer_interceptor_ios/ios`) @@ -106,12 +112,16 @@ SPEC REPOS: EXTERNAL SOURCES: apivideo_live_stream: :path: ".symlinks/plugins/apivideo_live_stream/ios" + camera_avfoundation: + :path: ".symlinks/plugins/camera_avfoundation/ios" file_picker: :path: ".symlinks/plugins/file_picker/ios" Flutter: :path: Flutter flutter_credit_card: :path: ".symlinks/plugins/flutter_credit_card/ios" + image_picker_ios: + :path: ".symlinks/plugins/image_picker_ios/ios" native_device_orientation: :path: ".symlinks/plugins/native_device_orientation/ios" path_provider_foundation: @@ -136,12 +146,14 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: apivideo_live_stream: 64fa696699cfc531e07f5d5d701c7e3cd5ce5e9d ApiVideoLiveStream: 5842571806a2ed26671597dadd370ec38d9b9ba4 + camera_avfoundation: dd002b0330f4981e1bbcb46ae9b62829237459a4 DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60 file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 flutter_credit_card: 48fb35065294868bc6a3e7f18a42f28929b84f48 HaishinKit: 013229683accc4abe46630b04629da3ff90428b7 + image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1 Logboard: 3d98bb85de6a36b255ab637e8178eb5671c5c3a6 native_device_orientation: 3b4cfc9565a7b879cc4fde282b3e27745e852d0d path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 diff --git a/mobile/app/ios/Runner/Info.plist b/mobile/app/ios/Runner/Info.plist index 624ef01..835f899 100644 --- a/mobile/app/ios/Runner/Info.plist +++ b/mobile/app/ios/Runner/Info.plist @@ -2,8 +2,14 @@ - NSMicrophoneUsageDescription - Required for on-device speech recognition. + NSCameraUsageDescription + This app needs access to camera when used + NSPhotoLibraryUsageDescription + TODO: Better description + LSApplicationCategoryType + Productivity + NSMicrophoneUsageDescription + Required for on-device speech recognition. NSSpeechRecognitionUsageDescription On-device speech-to-text. CADisableMinimumFrameDurationOnPhone diff --git a/mobile/app/lib/pages/conversation_model.dart b/mobile/app/lib/components/conversation_model.dart similarity index 100% rename from mobile/app/lib/pages/conversation_model.dart rename to mobile/app/lib/components/conversation_model.dart diff --git a/mobile/app/lib/components/conversation_thread/chat.dart b/mobile/app/lib/components/conversation_thread/chat.dart index ade2c2b..cde1f9c 100644 --- a/mobile/app/lib/components/conversation_thread/chat.dart +++ b/mobile/app/lib/components/conversation_thread/chat.dart @@ -1,9 +1,32 @@ class Chat { final String content; final bool role; + int chatType = 0; // Most basic chat, text from server or from user. - Chat(this.content, this.role); + Chat(this.content, this.role, this.chatType); - // TODO: Overload constructors to allow for multimedia. + // A chat with an included image. + Chat.image(this.content, this.role, this.chatType); + + // A chat with an included file. + Chat.file(this.content, this.role, this.chatType); +} + +// TODO: Add proper filetype support. File should either be parsed on-device, or uploaded to the server. +// TODO: Implement dynamic pathing based upon filetypes. +enum Filetype { + pdf, + doc, + docx, + pages, + note, + todo, + cal, + calendar, + event, + contact, + mp3, + mp4, + wav, } \ No newline at end of file diff --git a/mobile/app/lib/components/conversation_thread/chat_bubble_widget.dart b/mobile/app/lib/components/conversation_thread/chat_bubble_widget.dart index 2233eb2..4517a55 100644 --- a/mobile/app/lib/components/conversation_thread/chat_bubble_widget.dart +++ b/mobile/app/lib/components/conversation_thread/chat_bubble_widget.dart @@ -1,3 +1,6 @@ +import 'dart:convert'; + +import 'package:app/components/util/backend.dart'; import 'package:app/theme.dart'; import 'package:flutter/material.dart'; import 'chat.dart'; @@ -29,10 +32,20 @@ class ChatBubbleWidget extends StatelessWidget{ ), color: c.role ? AppTheme.darkest : AppTheme.dark, ), - child: Text( - c.content, - style: AppTheme.bodyMedium, - ), + child: + // TODO: Properly display filetypes in the thread. + c.chatType == 0 ? Text( + c.content, + style: AppTheme.bodyMedium, + ) + : c.chatType == 1 ? Image(image: Backend.convertFromBase64(c.content)) + : c.chatType == 2 ? const Icon( + Icons.file_copy, + ) : const Text("null"), + // Text( + // c.content, + // style: AppTheme.bodyMedium, + // ), ), ], ); diff --git a/mobile/app/lib/components/conversation_thread/conversation.dart b/mobile/app/lib/components/conversation_thread/conversation.dart index 7d79cde..9a8c047 100644 --- a/mobile/app/lib/components/conversation_thread/conversation.dart +++ b/mobile/app/lib/components/conversation_thread/conversation.dart @@ -1,4 +1,4 @@ -import 'package:app/util/backend.dart'; +import 'package:app/components/util/backend.dart'; import 'package:flutter/material.dart'; import 'chat.dart'; @@ -6,10 +6,12 @@ class Conversation with ChangeNotifier { int conversationID; String conversationLabel; String conversationSubLabel; - bool history = true; - // History is disabled by default. This means chats are auto-deleted once the user exits that conversation. + // TODO: Fix history, very, verry strange behavior when disabled. + bool history = true; // History is disabled by default. This means chats are auto-deleted once the user exits that conversation.\ bool conversationContext = true; // The prior prompts and responses are used as additional context for the next prompt and response. late bool stream = false; // Update the UI with a stream of characters as received from the server, or as one text. + var lastAccessTime = DateTime.timestamp(); // Used to sort conversations based on most recent usage. + List chats = []; Conversation(this.conversationID, this.conversationLabel, this.conversationSubLabel); @@ -23,11 +25,13 @@ class Conversation with ChangeNotifier { void toggleStream() => stream = !stream; void add(Chat message) { + lastAccessTime = DateTime.timestamp(); chats.add(message); notifyListeners(); } void loadChats() { + lastAccessTime = DateTime.timestamp(); Backend.loadConversation; chats = Backend.loadedChats; } @@ -41,10 +45,12 @@ class Conversation with ChangeNotifier { } List getChats() { + lastAccessTime = DateTime.timestamp(); return chats; } void setLabels(String cLabel, String cSubLabel) { + lastAccessTime = DateTime.timestamp(); conversationLabel = cLabel; conversationSubLabel = cSubLabel; } diff --git a/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart b/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart index c95fa3a..a706d35 100644 --- a/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart +++ b/mobile/app/lib/components/conversation_thread/conversation_thread_model.dart @@ -1,7 +1,7 @@ import 'package:app/components/conversation_thread/chat_bubble_model.dart'; import 'package:app/components/conversation_thread/chat.dart'; import 'package:app/components/conversation_thread/conversation.dart'; -import 'package:app/util/backend.dart'; +import 'package:app/components/util/backend.dart'; import 'package:app/components/prompt_box/prompt_box_widget.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; diff --git a/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart b/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart index a5cf391..27b3ddb 100644 --- a/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart +++ b/mobile/app/lib/components/conversation_thread/conversation_thread_widget.dart @@ -1,5 +1,5 @@ import 'package:app/components/conversation_thread/conversation.dart'; -import 'package:app/util/backend.dart'; +import 'package:app/components/util/backend.dart'; import 'package:provider/provider.dart'; import '/components/conversation_thread/chat_bubble_widget.dart'; @@ -50,6 +50,7 @@ class _ConversationThreadWidgetState extends State { @override Widget build(BuildContext context) { + // TODO: Add a scrollbar return Container( width: double.infinity, height: double.infinity, diff --git a/mobile/app/lib/pages/conversation_widget.dart b/mobile/app/lib/components/conversation_widget.dart similarity index 96% rename from mobile/app/lib/pages/conversation_widget.dart rename to mobile/app/lib/components/conversation_widget.dart index 26d2e4d..f6cf3ec 100644 --- a/mobile/app/lib/pages/conversation_widget.dart +++ b/mobile/app/lib/components/conversation_widget.dart @@ -9,8 +9,8 @@ import 'package:flutter/services.dart'; import 'package:app/theme.dart'; -import 'conversation_model.dart'; -export 'conversation_model.dart'; +import '../components/conversation_model.dart'; +export '../components/conversation_model.dart'; class ConversationWidget extends StatefulWidget { final Conversation conversation; @@ -64,8 +64,8 @@ class _ConversationWidgetState extends State { size: 30, ), onPressed: () async { - print("something pressed in conversation_widget.dart"); // TODO: Smooth out the animation to prevent the conversations list from loading half-way up the screen. + // Note: that only applies if the input was already selected. SystemChannels.textInput.invokeMethod('TextInput.hide'); Navigator.pop(context); }, diff --git a/mobile/app/lib/pages/conversations_list_model.dart b/mobile/app/lib/components/conversations_list_model.dart similarity index 100% rename from mobile/app/lib/pages/conversations_list_model.dart rename to mobile/app/lib/components/conversations_list_model.dart diff --git a/mobile/app/lib/pages/conversations_list_widget.dart b/mobile/app/lib/components/conversations_list_widget.dart similarity index 97% rename from mobile/app/lib/pages/conversations_list_widget.dart rename to mobile/app/lib/components/conversations_list_widget.dart index 0a38f42..6018d93 100644 --- a/mobile/app/lib/pages/conversations_list_widget.dart +++ b/mobile/app/lib/components/conversations_list_widget.dart @@ -1,15 +1,15 @@ import 'package:app/components/conversation_thread/conversation.dart'; import 'package:app/theme.dart'; -import 'package:app/pages/conversation_widget.dart'; -import 'package:app/util/backend.dart'; +import 'package:app/components/conversation_widget.dart'; +import 'package:app/components/util/backend.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:flutter/material.dart'; import 'package:app/components/options_dialog/options_dialog_widget.dart'; -import 'conversations_list_model.dart'; -export 'conversations_list_model.dart'; +import '../components/conversations_list_model.dart'; +export '../components/conversations_list_model.dart'; class ConversationsListWidget extends StatefulWidget { const ConversationsListWidget({super.key}); @@ -213,9 +213,6 @@ class _ConversationsListWidgetState extends State { }, ).then((value) => safeSetState(() {})), - - // TODO: Need to implement. - print("opening options") }, ), ); @@ -235,7 +232,7 @@ class _ConversationsListWidgetState extends State { Navigator.push( context, MaterialPageRoute( - builder: (context) => ConversationWidget(conversation: Backend.getLoadedConversation(),), + builder: (context) => ConversationWidget(conversation: Backend.loadedConversation,), ), ), // Handle the button press }, diff --git a/mobile/app/lib/pages/image_details_model.dart b/mobile/app/lib/components/image_details_model.dart similarity index 100% rename from mobile/app/lib/pages/image_details_model.dart rename to mobile/app/lib/components/image_details_model.dart diff --git a/mobile/app/lib/pages/image_details_widget.dart b/mobile/app/lib/components/image_details_widget.dart similarity index 100% rename from mobile/app/lib/pages/image_details_widget.dart rename to mobile/app/lib/components/image_details_widget.dart diff --git a/mobile/app/lib/components/no_chats_model.dart b/mobile/app/lib/components/no_chats_model.dart deleted file mode 100644 index d50872d..0000000 --- a/mobile/app/lib/components/no_chats_model.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:flutterflow_ui/flutterflow_ui.dart'; -import 'no_chats_widget.dart' show NoChatsWidget; -import 'package:flutter/material.dart'; -import 'package:google_fonts/google_fonts.dart'; -import 'package:provider/provider.dart'; - -class NoChatsModel extends FlutterFlowModel { - @override - void initState(BuildContext context) {} - - @override - void dispose() {} -} diff --git a/mobile/app/lib/components/no_chats_widget.dart b/mobile/app/lib/components/no_chats_widget.dart index 3fca32d..8116461 100644 --- a/mobile/app/lib/components/no_chats_widget.dart +++ b/mobile/app/lib/components/no_chats_widget.dart @@ -5,9 +5,6 @@ import 'package:provider/provider.dart'; import 'package:app/theme.dart'; -import 'no_chats_model.dart'; -export 'no_chats_model.dart'; - class NoChatsWidget extends StatefulWidget { const NoChatsWidget({ super.key, @@ -26,24 +23,19 @@ class NoChatsWidget extends StatefulWidget { } class _NoChatsWidgetState extends State { - late NoChatsModel _model; @override void setState(VoidCallback callback) { super.setState(callback); - _model.onUpdate(); } @override void initState() { super.initState(); - _model = createModel(context, () => NoChatsModel()); } @override void dispose() { - _model.maybeDispose(); - super.dispose(); } diff --git a/mobile/app/lib/components/prompt_box/prompt_box_widget.dart b/mobile/app/lib/components/prompt_box/prompt_box_widget.dart index 0a13904..6704614 100644 --- a/mobile/app/lib/components/prompt_box/prompt_box_widget.dart +++ b/mobile/app/lib/components/prompt_box/prompt_box_widget.dart @@ -1,10 +1,17 @@ -import 'package:app/util/backend.dart'; +import 'dart:io'; + +import 'package:app/components/util/backend.dart'; +import 'package:camera/camera.dart'; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/services.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:easy_debounce/easy_debounce.dart'; import 'package:flutter/material.dart'; import 'package:app/theme.dart'; +import 'package:image_picker/image_picker.dart'; +import '../conversation_thread/chat.dart'; import 'prompt_box_model.dart'; export 'prompt_box_model.dart'; @@ -17,7 +24,7 @@ class PromptBoxWidget extends StatefulWidget { class _PromptBoxWidgetState extends State { late PromptBoxModel _model; - late String entry_text; + String entryText = ""; @override void setState(VoidCallback callback) { @@ -43,6 +50,7 @@ class _PromptBoxWidgetState extends State { @override Widget build(BuildContext context) { + const double buttonPadding = 4; return SafeArea( child: Container( width: double.infinity, @@ -62,50 +70,59 @@ class _PromptBoxWidgetState extends State { children: [ Expanded( // TODO: File context upload child: Padding( - padding: const EdgeInsets.all(14), - child: FlutterFlowIconButton( - borderColor: Colors.transparent, - borderRadius: 30, - borderWidth: 1, - buttonSize: 40, + padding: const EdgeInsets.all(buttonPadding), + child: IconButton( icon: const Icon( Icons.file_upload_outlined, color: AppTheme.darkest, size: 30, ), - onPressed: () { - print('IconButton pressed ...'); + onPressed: () async { + final picker = FilePicker.platform; + final result = await picker.pickFiles(allowMultiple: true); // Allow multiple files + if (result != null) { + for (final platformFile in result.files) { + // TODO: Upload file to server + final file = File(platformFile.path!); + } + } else { + print('User canceled file selection'); + } }, ), ), ), - Expanded( // TODO: Photo context upload + Expanded( // TODO: Gallery context upload child: Padding( - padding: const EdgeInsets.all(14), - child: FlutterFlowIconButton( - borderColor: Colors.transparent, - borderRadius: 30, - borderWidth: 1, - buttonSize: 40, + padding: const EdgeInsets.all(buttonPadding), + child: IconButton( icon: const Icon( Icons.photo_outlined, color: AppTheme.darkest, size: 30, ), - onPressed: () { - print('IconButton pressed ...'); + onPressed: () async { + final picker = ImagePicker(); + final image = await picker.pickImage(source: ImageSource.gallery); + + // TODO: Allow for multiple pictures/photos to be selected. + // final images = await picker.pickMultiImage(); + if (image != null) { + + // TODO: Upload to server and add to conversation.chats + String i = Backend.convertToBase64(File(image.path)).toString(); + Backend.loadedConversation.add(Chat.image(i, true, 1)); + } else { + print('User canceled image selection'); + } }, ), ), ), Expanded( // TODO: Video context upload child: Padding( - padding: const EdgeInsets.all(14), - child: FlutterFlowIconButton( - borderColor: Colors.transparent, - borderRadius: 30, - borderWidth: 1, - buttonSize: 40, + padding: const EdgeInsets.all(buttonPadding), + child: IconButton( icon: const Icon( Icons.videocam_outlined, color: AppTheme.darkest, @@ -119,19 +136,48 @@ class _PromptBoxWidgetState extends State { ), Expanded( // TODO: Camera context upload child: Padding( - padding: const EdgeInsets.all(14), - child: FlutterFlowIconButton( - borderColor: Colors.transparent, - borderRadius: 30, - borderWidth: 1, - buttonSize: 40, + padding: const EdgeInsets.all(buttonPadding), + child: IconButton( icon: const Icon( Icons.camera_alt_outlined, color: AppTheme.darkest, size: 30, ), + onPressed: () async { + final picker = ImagePicker(); + final image = await picker.pickImage(source: ImageSource.camera); + + if (image != null) { + // TODO: Upload to server and add to conversation.chats + final i = Backend.convertToBase64(File(image.path)); + Backend.loadedConversation.add(Chat.image(i.toString(), true, 1)); + } else { + print('User canceled photo capture'); + } + }, + ), + ), + ), + Expanded( + child: Padding( + padding: const EdgeInsets.all(buttonPadding), + child: IconButton( + icon: Icon( + Backend.stt.speechToText.isNotListening ? Icons.mic_off_outlined : Icons.mic_none, + color: AppTheme.darkest, + size: 30, + ), onPressed: () { - print('IconButton pressed ...'); + // TODO: Audio input is still buggy, it appears to only enter the text field once the button is pressed again to stop. + if(Backend.stt.speechToText.isNotListening) { + print('STT engine starting ...'); + Backend.stt.startListening(); + updateText(); + } else { + print('STT engine stopping'); + Backend.stt.stopListening(); + updateText(); + } }, ), ), @@ -190,10 +236,13 @@ class _PromptBoxWidgetState extends State { fillColor: AppTheme.alternate, suffixIcon: GestureDetector( onTap: () { - // TODO: Connect with multimedia - entry_text = _model.textController.text; - Backend.respondWhenReady(entry_text); - _model.textController?.clear(); // Clear the text field + if(_model.textController.text != "") { + // TODO: Connect with multimedia + entryText = _model.textController.text; + Backend.respondWhenReady(entryText); + _model.textController?.clear(); // Clear the text field + SystemChannels.textInput.invokeMethod('TextInput.hide'); + } }, child: const Icon( Icons.send, @@ -206,12 +255,14 @@ class _PromptBoxWidgetState extends State { textAlign: TextAlign.start, maxLines: 5, minLines: 1, - validator: _model.textControllerValidator.asValidator(context), textInputAction: TextInputAction.send, // "Send" on keyboard onFieldSubmitted: (text) { - entry_text = _model.textController.text; - Backend.respondWhenReady(entry_text); // TODO: Connect with multimedia - _model.textController?.clear(); // Clear the text field + // Don't take any action unless there's an actual input. + if(_model.textController.text != "") { + entryText = _model.textController.text; + Backend.respondWhenReady(entryText); // TODO: Connect with multimedia + _model.textController?.clear(); // Clear the text field + } }, ), ), @@ -223,4 +274,12 @@ class _PromptBoxWidgetState extends State { ), ); } -} + + void updateText() { + _model.textController.text = Backend.stt.text; + entryText = _model.textController.text; + print(entryText); + print(_model.textController.text); + Backend.stt.reset(); + } +} \ No newline at end of file diff --git a/mobile/app/lib/pages/settings_model.dart b/mobile/app/lib/components/settings_model.dart similarity index 100% rename from mobile/app/lib/pages/settings_model.dart rename to mobile/app/lib/components/settings_model.dart diff --git a/mobile/app/lib/pages/settings_widget.dart b/mobile/app/lib/components/settings_widget.dart similarity index 100% rename from mobile/app/lib/pages/settings_widget.dart rename to mobile/app/lib/components/settings_widget.dart diff --git a/mobile/app/lib/pages/setup_model.dart b/mobile/app/lib/components/setup_model.dart similarity index 100% rename from mobile/app/lib/pages/setup_model.dart rename to mobile/app/lib/components/setup_model.dart diff --git a/mobile/app/lib/pages/setup_widget.dart b/mobile/app/lib/components/setup_widget.dart similarity index 100% rename from mobile/app/lib/pages/setup_widget.dart rename to mobile/app/lib/components/setup_widget.dart diff --git a/mobile/app/lib/pages/support_form_model.dart b/mobile/app/lib/components/support_form_model.dart similarity index 100% rename from mobile/app/lib/pages/support_form_model.dart rename to mobile/app/lib/components/support_form_model.dart diff --git a/mobile/app/lib/pages/support_form_widget.dart b/mobile/app/lib/components/support_form_widget.dart similarity index 99% rename from mobile/app/lib/pages/support_form_widget.dart rename to mobile/app/lib/components/support_form_widget.dart index 5037056..cfe090a 100644 --- a/mobile/app/lib/pages/support_form_widget.dart +++ b/mobile/app/lib/components/support_form_widget.dart @@ -4,8 +4,8 @@ import 'package:flutter_animate/flutter_animate.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; -import 'support_form_model.dart'; -export 'support_form_model.dart'; +import '../components/support_form_model.dart'; +export '../components/support_form_model.dart'; import 'package:app/theme.dart'; diff --git a/mobile/app/lib/util/backend.dart b/mobile/app/lib/components/util/backend.dart similarity index 72% rename from mobile/app/lib/util/backend.dart rename to mobile/app/lib/components/util/backend.dart index ccd2f7b..638bc6a 100644 --- a/mobile/app/lib/util/backend.dart +++ b/mobile/app/lib/components/util/backend.dart @@ -1,10 +1,14 @@ import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; +import 'package:flutter/widgets.dart'; import 'package:app/components/conversation_thread/conversation.dart'; +import 'package:camera/camera.dart'; import 'package:http/http.dart' as http; import 'package:app/components/conversation_thread/chat.dart'; -import 'package:app/util/speech_to_text.dart'; +import 'package:app/components/util/speech_to_text.dart'; import 'model.dart'; @@ -18,6 +22,7 @@ class Backend { //static final url = "http://192.168.1.68:11434/api"; // For local access to home server static String endpoint = ""; static final Map headers = {'Content-Type': 'application/json'}; + static final bool UI_TESTING = true; @@ -27,11 +32,13 @@ class Backend { - // PRIVACY-FOCUSED VARIABLES + // PRIVACY-NECESSARY VARIABLES // TODO: Reorder based on most recent usage. static List conversations = []; static List loadedChats = []; static late Conversation loadedConversation; + static late List cameras; + static late CameraController controller; // RUNTIME & STATUS VARIABLES @@ -45,40 +52,35 @@ class Backend { // TODO: Verify all different components of STT, TTS, LLM, etc have been initialized. // TODO: Handshake w/ server to validate identity factory Backend() { - // Speech-To-Text engine activation. - _speechEnabled = stt.isSpeechEnabled(); - // loadedChats = conversations[conversationID].chats; return singleBackend; } - void init() { - initiated = true; + void init() async { + // Prevent multiple initiations + if(!initiated) { + initiated = true; + // Speech-To-Text engine activation. + _speechEnabled = stt.isSpeechEnabled(); + + model = Model("llama3"); // Primarily used llama3, testing gemma2 + maxConversationID = conversations.length; + + cameras = await availableCameras(); + controller = CameraController(cameras[0], ResolutionPreset.max); + controller.initialize(); // TODO: Add error catching on camera + } // TODO: Dynamically get available models and models that can be pulled. // TODO: Dynamically get the user's default model - model = Model("llama3"); // Primarily used llama3, testing gemma2 - // TODO: Dynamically obtain conversations. - // conversations.add( - // Conversation.completeConversation( - // 0, - // "Test Conversation", - // "Testing Functionality", - // [ - // (Chat("Hello there! You are part of a prototype and are running locally on my Macbook Air. Please keep your responses brief as every prompt freezes my computer.", true)), - // (Chat("Understood! How can I help?", false)), - // ] - // ) - // ); - - maxConversationID = conversations.length; + // TODO: Obtain all user permissions at once. } - // PRIMARY INTERFACE + // PRIMARY INTERFACES @Deprecated("Use STT interface directly and use respondWhenReady().") static Future sttGetResponseWhenReady() async { // TODO: In case on-device STT is unavailable, use server-side STT service. @@ -87,19 +89,38 @@ class Backend { } prompt = stt.getTextWhenReady() as String; - conversations[conversationID].add(Chat(prompt, true)); - httpSendRequest(); - conversations[conversationID].add(Chat(response, false)); + conversations[conversationID].add(Chat(prompt, true, 0)); + if(!UI_TESTING) { + httpSendRequest(); + } + conversations[conversationID].add(Chat(response, false, 0)); return response; } static void respondWhenReady(String p) async { prompt = p; - conversations[conversationID].add(Chat(prompt, true)); - httpSendRequest(); + conversations[conversationID].add(Chat(prompt, true, 0)); + if(!UI_TESTING) { + httpSendRequest(); + } } + static ImageProvider convertFromBase64(img) { + Uint8List imageBytes = base64Decode(img); + + return Image.memory(imageBytes).image; + } + + // Converts image to base64 encoded string for data transfer. + static String convertToBase64(f) { + List imageBytes = f.readAsBytesSync(); + String img = base64Encode(imageBytes); + + // TODO: Add special handling for images included in prompts. + conversations[conversationID].add(Chat.image(img, true, 1)); + return img; + } // CONVERSATION MANAGEMENT @@ -155,7 +176,7 @@ class Backend { Map data; if(conversations[conversationID].conversationContext) { - loadedChats = [...loadedChats, Chat(prompt, true)]; + loadedChats = [...loadedChats, Chat(prompt, true, 0)]; endpoint = "chat"; List> messageList = convertMessages(); @@ -196,12 +217,13 @@ class Backend { Map re = jsonDecode(serverResponse.body); if(conversations[conversationID].conversationContext) { response = re["message"]["content"]; - loadedChats = [...loadedChats, Chat(response, false)]; + // TODO: Modify in case of different response types. + loadedChats = [...loadedChats, Chat(response, false, 0)]; } else { response = re["response"]; } } - conversations[conversationID].add(Chat(response, false)); + conversations[conversationID].add(Chat(response, false, 0)); prompt = ""; response = ""; diff --git a/mobile/app/lib/util/model.dart b/mobile/app/lib/components/util/model.dart similarity index 100% rename from mobile/app/lib/util/model.dart rename to mobile/app/lib/components/util/model.dart diff --git a/mobile/app/lib/util/speech_to_text.dart b/mobile/app/lib/components/util/speech_to_text.dart similarity index 68% rename from mobile/app/lib/util/speech_to_text.dart rename to mobile/app/lib/components/util/speech_to_text.dart index 4ec5e2f..0b7e634 100644 --- a/mobile/app/lib/util/speech_to_text.dart +++ b/mobile/app/lib/components/util/speech_to_text.dart @@ -1,9 +1,16 @@ import 'package:speech_to_text/speech_recognition_result.dart'; import 'package:speech_to_text/speech_to_text.dart' as stt; +import 'package:speech_to_text/speech_to_text.dart'; /// Handles standard Speech-To-Text using on-device processing. class SpeechToText { final stt.SpeechToText speechToText = stt.SpeechToText(); + // TODO: User customizable STT?? + final SpeechListenOptions options = stt.SpeechListenOptions( + partialResults: true, + onDevice: true, + ); + bool speechEnabled = false; String text = ""; @@ -16,13 +23,22 @@ class SpeechToText { } // Retrieve the text. + @Deprecated("TODO: Deprecated message for speechToText.getText") String getText() => text; // Reset either after saving the text or prior to using STT again. void reset() => text = ""; /// Start a speech recognition session - void startListening() async => await speechToText.listen(onResult: onSpeechResult); + void startListening() async { + await speechToText.listen( + onResult: onSpeechResult, + pauseFor: const Duration(seconds: 3), + + // TODO: I think this is a bug with the speech_to_text library? The docs correctly identify the named parameter of SpeechListenOptions, but it's not recognized. + // SpeechListenOptions: options, + ); + } /// Manually stop the active speech recognition session /// Note that there are also timeouts that each platform enforces diff --git a/mobile/app/lib/components/util/usage_queue.dart b/mobile/app/lib/components/util/usage_queue.dart new file mode 100644 index 0000000..267c7fc --- /dev/null +++ b/mobile/app/lib/components/util/usage_queue.dart @@ -0,0 +1,317 @@ +import 'dart:math'; + +class UsageQueue implements List { + @override + var first; + + @override + var last; + + @override + late int length; + + @override + List operator +(List other) { + // TODO: implement + + throw UnimplementedError(); + } + + @override + operator [](int index) { + // TODO: implement [] + throw UnimplementedError(); + } + + @override + void operator []=(int index, value) { + // TODO: implement []= + } + + @override + void add(value) { + // TODO: implement add + } + + @override + void addAll(Iterable iterable) { + // TODO: implement addAll + } + + @override + bool any(bool Function(dynamic element) test) { + // TODO: implement any + throw UnimplementedError(); + } + + @override + Map asMap() { + // TODO: implement asMap + throw UnimplementedError(); + } + + @override + List cast() { + // TODO: implement cast + throw UnimplementedError(); + } + + @override + void clear() { + // TODO: implement clear + } + + @override + bool contains(Object? element) { + // TODO: implement contains + throw UnimplementedError(); + } + + @override + elementAt(int index) { + // TODO: implement elementAt + throw UnimplementedError(); + } + + @override + bool every(bool Function(dynamic element) test) { + // TODO: implement every + throw UnimplementedError(); + } + + @override + Iterable expand(Iterable Function(dynamic element) toElements) { + // TODO: implement expand + throw UnimplementedError(); + } + + @override + void fillRange(int start, int end, [fillValue]) { + // TODO: implement fillRange + } + + @override + firstWhere(bool Function(dynamic element) test, {Function()? orElse}) { + // TODO: implement firstWhere + throw UnimplementedError(); + } + + @override + T fold(T initialValue, T Function(T previousValue, dynamic element) combine) { + // TODO: implement fold + throw UnimplementedError(); + } + + @override + Iterable followedBy(Iterable other) { + // TODO: implement followedBy + throw UnimplementedError(); + } + + @override + void forEach(void Function(dynamic element) action) { + // TODO: implement forEach + } + + @override + Iterable getRange(int start, int end) { + // TODO: implement getRange + throw UnimplementedError(); + } + + @override + int indexOf(element, [int start = 0]) { + // TODO: implement indexOf + throw UnimplementedError(); + } + + @override + int indexWhere(bool Function(dynamic element) test, [int start = 0]) { + // TODO: implement indexWhere + throw UnimplementedError(); + } + + @override + void insert(int index, element) { + // TODO: implement insert + } + + @override + void insertAll(int index, Iterable iterable) { + // TODO: implement insertAll + } + + @override + // TODO: implement isEmpty + bool get isEmpty => throw UnimplementedError(); + + @override + // TODO: implement isNotEmpty + bool get isNotEmpty => throw UnimplementedError(); + + @override + // TODO: implement iterator + Iterator get iterator => throw UnimplementedError(); + + @override + String join([String separator = ""]) { + // TODO: implement join + throw UnimplementedError(); + } + + @override + int lastIndexOf(element, [int? start]) { + // TODO: implement lastIndexOf + throw UnimplementedError(); + } + + @override + int lastIndexWhere(bool Function(dynamic element) test, [int? start]) { + // TODO: implement lastIndexWhere + throw UnimplementedError(); + } + + @override + lastWhere(bool Function(dynamic element) test, {Function()? orElse}) { + // TODO: implement lastWhere + throw UnimplementedError(); + } + + @override + Iterable map(T Function(dynamic e) toElement) { + // TODO: implement map + throw UnimplementedError(); + } + + @override + reduce(Function(dynamic value, dynamic element) combine) { + // TODO: implement reduce + throw UnimplementedError(); + } + + @override + bool remove(Object? value) { + // TODO: implement remove + throw UnimplementedError(); + } + + @override + removeAt(int index) { + // TODO: implement removeAt + throw UnimplementedError(); + } + + @override + removeLast() { + // TODO: implement removeLast + throw UnimplementedError(); + } + + @override + void removeRange(int start, int end) { + // TODO: implement removeRange + } + + @override + void removeWhere(bool Function(dynamic element) test) { + // TODO: implement removeWhere + } + + @override + void replaceRange(int start, int end, Iterable replacements) { + // TODO: implement replaceRange + } + + @override + void retainWhere(bool Function(dynamic element) test) { + // TODO: implement retainWhere + } + + @override + // TODO: implement reversed + Iterable get reversed => throw UnimplementedError(); + + @override + void setAll(int index, Iterable iterable) { + // TODO: implement setAll + } + + @override + void setRange(int start, int end, Iterable iterable, [int skipCount = 0]) { + // TODO: implement setRange + } + + @override + void shuffle([Random? random]) { + // TODO: implement shuffle + } + + @override + // TODO: implement single + get single => throw UnimplementedError(); + + @override + singleWhere(bool Function(dynamic element) test, {Function()? orElse}) { + // TODO: implement singleWhere + throw UnimplementedError(); + } + + @override + Iterable skip(int count) { + // TODO: implement skip + throw UnimplementedError(); + } + + @override + Iterable skipWhile(bool Function(dynamic value) test) { + // TODO: implement skipWhile + throw UnimplementedError(); + } + + @override + void sort([int Function(dynamic a, dynamic b)? compare]) { + // TODO: implement sort + } + + @override + List sublist(int start, [int? end]) { + // TODO: implement sublist + throw UnimplementedError(); + } + + @override + Iterable take(int count) { + // TODO: implement take + throw UnimplementedError(); + } + + @override + Iterable takeWhile(bool Function(dynamic value) test) { + // TODO: implement takeWhile + throw UnimplementedError(); + } + + @override + List toList({bool growable = true}) { + // TODO: implement toList + throw UnimplementedError(); + } + + @override + Set toSet() { + // TODO: implement toSet + throw UnimplementedError(); + } + + @override + Iterable where(bool Function(dynamic element) test) { + // TODO: implement where + throw UnimplementedError(); + } + + @override + Iterable whereType() { + // TODO: implement whereType + throw UnimplementedError(); + } + +} \ No newline at end of file diff --git a/mobile/app/lib/main.dart b/mobile/app/lib/main.dart index 697b98a..c1ac660 100644 --- a/mobile/app/lib/main.dart +++ b/mobile/app/lib/main.dart @@ -1,6 +1,6 @@ -import 'package:app/pages/conversation_widget.dart'; -import 'package:app/pages/settings_widget.dart'; -import 'package:app/pages/conversations_list_widget.dart'; +import 'package:app/components/conversation_widget.dart'; +import 'package:app/components/settings_widget.dart'; +import 'package:app/components/conversations_list_widget.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:provider/provider.dart'; @@ -36,6 +36,7 @@ BottomNavigationBarItem _buildNavigationItem(int index) { icon: Icon(iconData), label: label, backgroundColor: AppTheme.lightest, + ); } @@ -67,21 +68,14 @@ class MyApp extends ConsumerWidget { title: 'One AI', home: Scaffold( backgroundColor: AppTheme.medium, - appBar: AppBar( - backgroundColor: AppTheme.secondaryBackground, - automaticallyImplyLeading: false, - title: const Text( - 'One AI', - style: AppTheme.displayLarge, - ), - actions: const [], - centerTitle: false, - elevation: 0, - ), body: _pages[selectedIndex], bottomNavigationBar: BottomNavigationBar( backgroundColor: AppTheme.darkest, selectedItemColor: AppTheme.lightest, + showUnselectedLabels: false, + showSelectedLabels: false, + iconSize: 30, + // Set the number of items to the length of the pages list items: List.generate(_pages.length, (index) => _buildNavigationItem(index)), currentIndex: selectedIndex, diff --git a/mobile/app/linux/flutter/generated_plugin_registrant.cc b/mobile/app/linux/flutter/generated_plugin_registrant.cc index f6f23bf..7299b5c 100644 --- a/mobile/app/linux/flutter/generated_plugin_registrant.cc +++ b/mobile/app/linux/flutter/generated_plugin_registrant.cc @@ -6,9 +6,13 @@ #include "generated_plugin_registrant.h" +#include #include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); + file_selector_plugin_register_with_registrar(file_selector_linux_registrar); g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); diff --git a/mobile/app/linux/flutter/generated_plugins.cmake b/mobile/app/linux/flutter/generated_plugins.cmake index f16b4c3..786ff5c 100644 --- a/mobile/app/linux/flutter/generated_plugins.cmake +++ b/mobile/app/linux/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + file_selector_linux url_launcher_linux ) diff --git a/mobile/app/macos/Flutter/GeneratedPluginRegistrant.swift b/mobile/app/macos/Flutter/GeneratedPluginRegistrant.swift index 7bc02f3..54bfa47 100644 --- a/mobile/app/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/mobile/app/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,7 @@ import FlutterMacOS import Foundation +import file_selector_macos import path_provider_foundation import rive_common import shared_preferences_foundation @@ -14,6 +15,7 @@ import url_launcher_macos import video_player_avfoundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) RivePlugin.register(with: registry.registrar(forPlugin: "RivePlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) diff --git a/mobile/app/pubspec.lock b/mobile/app/pubspec.lock index 7721e71..b09a339 100644 --- a/mobile/app/pubspec.lock +++ b/mobile/app/pubspec.lock @@ -89,6 +89,46 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + camera: + dependency: "direct main" + description: + name: camera + sha256: "2170a943dcb67be2af2c6bcda8775e74b41d4c02d6a4eb10bdc832ee185c4eea" + url: "https://pub.dev" + source: hosted + version: "0.11.0+1" + camera_android_camerax: + dependency: transitive + description: + name: camera_android_camerax + sha256: "7c03940cb8c92eb5b184952674a07cc4a73c6ba2b3568aad70255ad4cb913660" + url: "https://pub.dev" + source: hosted + version: "0.6.7+1" + camera_avfoundation: + dependency: transitive + description: + name: camera_avfoundation + sha256: "95c2f40b4d06cdb0fd2ad893c762d1f2c931a5e370793ec34c939eb4fcbf96bb" + url: "https://pub.dev" + source: hosted + version: "0.9.17" + camera_platform_interface: + dependency: transitive + description: + name: camera_platform_interface + sha256: b3ede1f171532e0d83111fe0980b46d17f1aa9788a07a2fbed07366bbdbb9061 + url: "https://pub.dev" + source: hosted + version: "2.8.0" + camera_web: + dependency: transitive + description: + name: camera_web + sha256: b9235ec0a2ce949daec546f1f3d86f05c3921ed31c7d9ab6b7c03214d152fc2d + url: "https://pub.dev" + source: hosted + version: "0.3.4" carousel_slider: dependency: transitive description: @@ -234,13 +274,45 @@ packages: source: hosted version: "7.0.0" file_picker: - dependency: transitive + dependency: "direct main" description: name: file_picker sha256: "824f5b9f389bfc4dddac3dea76cd70c51092d9dff0b2ece7ef4f53db8547d258" url: "https://pub.dev" source: hosted version: "8.0.6" + file_selector_linux: + dependency: transitive + description: + name: file_selector_linux + sha256: "045d372bf19b02aeb69cacf8b4009555fb5f6f0b7ad8016e5f46dd1387ddd492" + url: "https://pub.dev" + source: hosted + version: "0.9.2+1" + file_selector_macos: + dependency: transitive + description: + name: file_selector_macos + sha256: f42eacb83b318e183b1ae24eead1373ab1334084404c8c16e0354f9a3e55d385 + url: "https://pub.dev" + source: hosted + version: "0.9.4" + file_selector_platform_interface: + dependency: transitive + description: + name: file_selector_platform_interface + sha256: a3994c26f10378a039faa11de174d7b78eb8f79e4dd0af2a451410c1a5c3f66b + url: "https://pub.dev" + source: hosted + version: "2.6.2" + file_selector_windows: + dependency: transitive + description: + name: file_selector_windows + sha256: d3547240c20cabf205c7c7f01a50ecdbc413755814d6677f3cb366f04abcead0 + url: "https://pub.dev" + source: hosted + version: "0.9.3+1" fixnum: dependency: transitive description: @@ -480,6 +552,70 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" + image_picker: + dependency: "direct main" + description: + name: image_picker + sha256: "021834d9c0c3de46bf0fe40341fa07168407f694d9b2bb18d532dc1261867f7a" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + image_picker_android: + dependency: transitive + description: + name: image_picker_android + sha256: ff39a10ab4f48f4ac70776d0494a97bf073cd2570892cd46bc8a5cac162c25db + url: "https://pub.dev" + source: hosted + version: "0.8.12+4" + image_picker_for_web: + dependency: transitive + description: + name: image_picker_for_web + sha256: "5d6eb13048cd47b60dbf1a5495424dea226c5faf3950e20bf8120a58efb5b5f3" + url: "https://pub.dev" + source: hosted + version: "3.0.4" + image_picker_ios: + dependency: transitive + description: + name: image_picker_ios + sha256: "6703696ad49f5c3c8356d576d7ace84d1faf459afb07accbb0fae780753ff447" + url: "https://pub.dev" + source: hosted + version: "0.8.12" + image_picker_linux: + dependency: transitive + description: + name: image_picker_linux + sha256: "4ed1d9bb36f7cd60aa6e6cd479779cc56a4cb4e4de8f49d487b1aaad831300fa" + url: "https://pub.dev" + source: hosted + version: "0.2.1+1" + image_picker_macos: + dependency: transitive + description: + name: image_picker_macos + sha256: "3f5ad1e8112a9a6111c46d0b57a7be2286a9a07fc6e1976fdf5be2bd31d4ff62" + url: "https://pub.dev" + source: hosted + version: "0.2.1+1" + image_picker_platform_interface: + dependency: transitive + description: + name: image_picker_platform_interface + sha256: "9ec26d410ff46f483c5519c29c02ef0e02e13a543f882b152d4bfd2f06802f80" + url: "https://pub.dev" + source: hosted + version: "2.10.0" + image_picker_windows: + dependency: transitive + description: + name: image_picker_windows + sha256: "6ad07afc4eb1bc25f3a01084d28520496c4a3bb0cb13685435838167c9dcedeb" + url: "https://pub.dev" + source: hosted + version: "0.2.1+1" intl: dependency: transitive description: @@ -600,6 +736,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.14.0" + mime: + dependency: transitive + description: + name: mime + sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" + url: "https://pub.dev" + source: hosted + version: "1.0.5" mime_type: dependency: transitive description: diff --git a/mobile/app/pubspec.yaml b/mobile/app/pubspec.yaml index 1d4867d..85f8a47 100644 --- a/mobile/app/pubspec.yaml +++ b/mobile/app/pubspec.yaml @@ -46,6 +46,9 @@ dependencies: video_player: ^2.9.1 flutter_riverpod: ^2.5.1 speech_to_text: ^6.6.0 + camera: ^0.11.0+1 + image_picker: ^1.1.2 + file_picker: ^8.0.6 dev_dependencies: flutter_test: sdk: flutter diff --git a/mobile/app/windows/flutter/generated_plugin_registrant.cc b/mobile/app/windows/flutter/generated_plugin_registrant.cc index 72ff4b7..6c32c2f 100644 --- a/mobile/app/windows/flutter/generated_plugin_registrant.cc +++ b/mobile/app/windows/flutter/generated_plugin_registrant.cc @@ -6,10 +6,13 @@ #include "generated_plugin_registrant.h" +#include #include #include void RegisterPlugins(flutter::PluginRegistry* registry) { + FileSelectorWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FileSelectorWindows")); RivePluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("RivePlugin")); UrlLauncherWindowsRegisterWithRegistrar( diff --git a/mobile/app/windows/flutter/generated_plugins.cmake b/mobile/app/windows/flutter/generated_plugins.cmake index e141d51..e9a4302 100644 --- a/mobile/app/windows/flutter/generated_plugins.cmake +++ b/mobile/app/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + file_selector_windows rive_common url_launcher_windows ) From dfcddd11d4f9699532e72de1df62881307b75c5f Mon Sep 17 00:00:00 2001 From: Joshua Date: Thu, 11 Jul 2024 15:24:38 -0600 Subject: [PATCH 23/24] while there's still improvements to be made, time to move onto the backend. --- .../components/conversation_thread/chat.dart | 1 + .../lib/components/conversation_widget.dart | 4 +- .../components/conversations_list_widget.dart | 14 ++--- .../prompt_box/prompt_box_widget.dart | 54 +++++++++---------- mobile/app/lib/components/util/backend.dart | 34 +++++++++--- mobile/app/lib/main.dart | 7 ++- mobile/app/lib/theme.dart | 53 ++++++++++-------- 7 files changed, 98 insertions(+), 69 deletions(-) diff --git a/mobile/app/lib/components/conversation_thread/chat.dart b/mobile/app/lib/components/conversation_thread/chat.dart index cde1f9c..f6dbada 100644 --- a/mobile/app/lib/components/conversation_thread/chat.dart +++ b/mobile/app/lib/components/conversation_thread/chat.dart @@ -14,6 +14,7 @@ class Chat { } // TODO: Add proper filetype support. File should either be parsed on-device, or uploaded to the server. +// TODO: See https://github.com/adithya-s-k/omniparse // TODO: Implement dynamic pathing based upon filetypes. enum Filetype { pdf, diff --git a/mobile/app/lib/components/conversation_widget.dart b/mobile/app/lib/components/conversation_widget.dart index f6cf3ec..801e22a 100644 --- a/mobile/app/lib/components/conversation_widget.dart +++ b/mobile/app/lib/components/conversation_widget.dart @@ -60,7 +60,7 @@ class _ConversationWidgetState extends State { buttonSize: 60, icon: const Icon( Icons.arrow_back_rounded, - color: AppTheme.lightest, + color: AppTheme.lighterest, size: 30, ), onPressed: () async { @@ -82,7 +82,7 @@ class _ConversationWidgetState extends State { child: FlutterFlowIconButton( icon: const Icon( Icons.more_vert, - color: AppTheme.lightest, + color: AppTheme.lighterest, size: 24, ), onPressed: () async { diff --git a/mobile/app/lib/components/conversations_list_widget.dart b/mobile/app/lib/components/conversations_list_widget.dart index 6018d93..f6f72fa 100644 --- a/mobile/app/lib/components/conversations_list_widget.dart +++ b/mobile/app/lib/components/conversations_list_widget.dart @@ -87,10 +87,6 @@ class _ConversationsListWidgetState extends State { return Align( alignment: const AlignmentDirectional(0, 0), child: InkWell( - splashColor: AppTheme.accent, - focusColor: AppTheme.lightest, - hoverColor: AppTheme.dark, - highlightColor: AppTheme.darkest, // TODO // onTap: () async { // context.pushNamed('Conversation'); @@ -111,7 +107,7 @@ class _ConversationsListWidgetState extends State { Container( width: 100, height: 100, - decoration: const BoxDecoration(color: AppTheme.lightest,), + decoration: const BoxDecoration(color: AppTheme.medium,), child: const Align( alignment: AlignmentDirectional(0, 0), child: Icon( @@ -131,7 +127,7 @@ class _ConversationsListWidgetState extends State { minWidth: MediaQuery.sizeOf(context).width, ), decoration: const BoxDecoration( - color: AppTheme.lightest, + color: AppTheme.medium, shape: BoxShape.rectangle, ), child: Column( @@ -175,7 +171,7 @@ class _ConversationsListWidgetState extends State { width: 100, height: 100, decoration: const BoxDecoration( - color: AppTheme.lightest, + color: AppTheme.medium, ), child: const Icon( Icons.chevron_right_rounded, @@ -236,10 +232,10 @@ class _ConversationsListWidgetState extends State { ), ), // Handle the button press }, - backgroundColor: AppTheme.darkest, + backgroundColor: AppTheme.accent, child: const Icon( Icons.bubble_chart, - color: AppTheme.lightest, + color: AppTheme.light, ), ), ), diff --git a/mobile/app/lib/components/prompt_box/prompt_box_widget.dart b/mobile/app/lib/components/prompt_box/prompt_box_widget.dart index 6704614..c95499e 100644 --- a/mobile/app/lib/components/prompt_box/prompt_box_widget.dart +++ b/mobile/app/lib/components/prompt_box/prompt_box_widget.dart @@ -68,26 +68,17 @@ class _PromptBoxWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.end, // TODO: Implement the context upload buttons children: [ - Expanded( // TODO: File context upload + Expanded( // TODO: Video context upload child: Padding( padding: const EdgeInsets.all(buttonPadding), child: IconButton( icon: const Icon( - Icons.file_upload_outlined, - color: AppTheme.darkest, + Icons.videocam_outlined, + color: AppTheme.accent2, size: 30, ), - onPressed: () async { - final picker = FilePicker.platform; - final result = await picker.pickFiles(allowMultiple: true); // Allow multiple files - if (result != null) { - for (final platformFile in result.files) { - // TODO: Upload file to server - final file = File(platformFile.path!); - } - } else { - print('User canceled file selection'); - } + onPressed: () { + print('IconButton pressed ...'); }, ), ), @@ -98,7 +89,7 @@ class _PromptBoxWidgetState extends State { child: IconButton( icon: const Icon( Icons.photo_outlined, - color: AppTheme.darkest, + color: AppTheme.accent2, size: 30, ), onPressed: () async { @@ -119,28 +110,37 @@ class _PromptBoxWidgetState extends State { ), ), ), - Expanded( // TODO: Video context upload + Expanded( // TODO: File context upload child: Padding( padding: const EdgeInsets.all(buttonPadding), child: IconButton( icon: const Icon( - Icons.videocam_outlined, - color: AppTheme.darkest, + Icons.file_upload_outlined, + color: AppTheme.accent2, size: 30, ), - onPressed: () { - print('IconButton pressed ...'); + onPressed: () async { + final picker = FilePicker.platform; + final result = await picker.pickFiles(allowMultiple: true); // Allow multiple files + if (result != null) { + for (final platformFile in result.files) { + // TODO: Upload file to server + final file = File(platformFile.path!); + } + } else { + print('User canceled file selection'); + } }, ), ), ), - Expanded( // TODO: Camera context upload + Expanded( child: Padding( padding: const EdgeInsets.all(buttonPadding), child: IconButton( icon: const Icon( Icons.camera_alt_outlined, - color: AppTheme.darkest, + color: AppTheme.accent2, size: 30, ), onPressed: () async { @@ -148,11 +148,10 @@ class _PromptBoxWidgetState extends State { final image = await picker.pickImage(source: ImageSource.camera); if (image != null) { - // TODO: Upload to server and add to conversation.chats final i = Backend.convertToBase64(File(image.path)); + + // TODO: Add to widget rather than display in conversation thread. Backend.loadedConversation.add(Chat.image(i.toString(), true, 1)); - } else { - print('User canceled photo capture'); } }, ), @@ -164,7 +163,7 @@ class _PromptBoxWidgetState extends State { child: IconButton( icon: Icon( Backend.stt.speechToText.isNotListening ? Icons.mic_off_outlined : Icons.mic_none, - color: AppTheme.darkest, + color: AppTheme.accent2, size: 30, ), onPressed: () { @@ -192,6 +191,7 @@ class _PromptBoxWidgetState extends State { child: Padding( padding: const EdgeInsetsDirectional.fromSTEB(12, 0, 12, 0), child: TextFormField( + keyboardAppearance: Brightness.dark, controller: _model.textController, focusNode: _model.textFieldFocusNode, onChanged: (_) => EasyDebounce.debounce( @@ -246,7 +246,7 @@ class _PromptBoxWidgetState extends State { }, child: const Icon( Icons.send, - color: AppTheme.primary, + color: AppTheme.accent, size: 30, ), ), diff --git a/mobile/app/lib/components/util/backend.dart b/mobile/app/lib/components/util/backend.dart index 638bc6a..c53e85e 100644 --- a/mobile/app/lib/components/util/backend.dart +++ b/mobile/app/lib/components/util/backend.dart @@ -173,19 +173,41 @@ class Backend { // TODO: Allow for additional flags, ie. continuous conversation, images, etc. // TODO: Allow for toggleable states between stream // TODO: Allow for different models to be dynamically selected. + + // TODO: Further adaption of API calls to allow for images to/from the AI model + // Example API call for image recognition + // curl http://localhost:11434/api/generate -d '{ + // "model": "llava", + // "prompt":"What is in this picture?", + // "stream": false, + // "images": ["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"] + // }' + Map data; - if(conversations[conversationID].conversationContext) { + if(loadedConversation.conversationContext) { loadedChats = [...loadedChats, Chat(prompt, true, 0)]; endpoint = "chat"; List> messageList = convertMessages(); - data = { - "model": model.name, - "messages": messageList, - "stream": loadedConversation.stream, - }; + // Last prompt was text + if(loadedChats.last.chatType == 0) { + data = { + "model": model.name, + "messages": messageList, + "stream": loadedConversation.stream, + }; + // Last prompt was image + } else { //if (loadedChats.last.chatType == 1) { + // TODO: Do some wizardry to pipe the output of llava as additional context for the user's actual prompt or something. + data = { + "model": "llava", + "prompt": "What is in this picture?", + "stream": loadedConversation.stream, + "images" : [loadedChats.last.content], + }; + } } else { endpoint = "generate"; diff --git a/mobile/app/lib/main.dart b/mobile/app/lib/main.dart index c1ac660..01f926e 100644 --- a/mobile/app/lib/main.dart +++ b/mobile/app/lib/main.dart @@ -35,8 +35,6 @@ BottomNavigationBarItem _buildNavigationItem(int index) { return BottomNavigationBarItem( icon: Icon(iconData), label: label, - backgroundColor: AppTheme.lightest, - ); } @@ -67,11 +65,12 @@ class MyApp extends ConsumerWidget { return MaterialApp( title: 'One AI', home: Scaffold( - backgroundColor: AppTheme.medium, + backgroundColor: AppTheme.dark, body: _pages[selectedIndex], bottomNavigationBar: BottomNavigationBar( backgroundColor: AppTheme.darkest, - selectedItemColor: AppTheme.lightest, + selectedItemColor: AppTheme.accent2, + unselectedItemColor: AppTheme.light, showUnselectedLabels: false, showSelectedLabels: false, iconSize: 30, diff --git a/mobile/app/lib/theme.dart b/mobile/app/lib/theme.dart index 3818e8f..f25964b 100644 --- a/mobile/app/lib/theme.dart +++ b/mobile/app/lib/theme.dart @@ -1,19 +1,30 @@ import 'package:flutter/material.dart'; +// TODO: Convert this to my .theme file format for cross-platform and cross-application compatability +// TODO: Create customization options (pre-built, auto-generated, AI-generated, or completely custom??) to settings class AppTheme { - static const darkest = Color.fromARGB(255, 45, 45, 145); - static const dark = Color.fromARGB(255, 81, 81, 196); - static const medium = Color.fromARGB(255, 107, 107, 201); - static const light = Color.fromARGB(255, 155, 155, 255); - static const lightest = Color.fromARGB(255, 172, 175, 255); - static const accent = Color.fromARGB(255, 255, 113, 0); - static const accent2 = Color.fromARGB(255, 255, 164, 79); + // static const darkest = Color.fromARGB(255, 45, 45, 145); + // static const dark = Color.fromARGB(255, 81, 81, 196); + // static const medium = Color.fromARGB(255, 107, 107, 201); + // static const light = Color.fromARGB(255, 155, 155, 255); + // static const lightest = Color.fromARGB(255, 172, 175, 255); + + static const darkest = Color.fromARGB(255, 18, 18, 18); + static const dark = Color.fromARGB(255, 27, 27, 27); + static const medium = Color.fromARGB(255, 36, 36, 36); + static const light = Color.fromARGB(255, 45, 45, 45); + static const lightest = Color.fromARGB(255, 54, 54, 54); + static const lighterest = Color.fromARGB(255, 145, 145, 145); + static const white = Colors.white; + + static const accent = Color.fromARGB(255, 155, 155, 255); + static const accent2 = Color.fromARGB(255, 172, 175, 255); static const primaryBackground = darkest; static const secondaryBackground = medium; - static const primaryTextColor = darkest; + static const primaryTextColor = white; static const primaryText = TextStyle(color: primaryTextColor, fontSize: 16); - static const secondaryText = dark; + static const secondaryText = lightest; static const accent1 = lightest; static const accent4 = accent2; @@ -22,24 +33,24 @@ class AppTheme { static const tertiary = light; static const alternate = lightest; - static const bodyLarge = TextStyle(color: light, fontSize: 24, fontWeight: FontWeight.bold); - static const bodyMedium = TextStyle(color: lightest, fontSize: 16); - static const bodySmall = TextStyle(color: light, fontSize: 12); + static const bodyLarge = TextStyle(color: white, fontSize: 24, fontWeight: FontWeight.bold); + static const bodyMedium = TextStyle(color: white, fontSize: 16); + static const bodySmall = TextStyle(color: white, fontSize: 12); static const error = Colors.red; static const info = Colors.grey; - static const displayLarge = TextStyle(color: darkest, fontSize: 24, fontWeight: FontWeight.bold); - static const displayMedium = TextStyle(color: dark, fontSize: 16, fontWeight: FontWeight.bold); - static const displaySmall = TextStyle(color: lightest, fontSize: 12, fontWeight: FontWeight.bold); + static const displayLarge = TextStyle(color: white, fontSize: 24, fontWeight: FontWeight.bold); + static const displayMedium = TextStyle(color: white, fontSize: 16, fontWeight: FontWeight.bold); + static const displaySmall = TextStyle(color: white, fontSize: 12, fontWeight: FontWeight.bold); - static const labelLarge = TextStyle(color: darkest, fontSize: 24, fontWeight: FontWeight.bold); - static const labelMedium = TextStyle(color: dark, fontSize: 16, fontWeight: FontWeight.bold); - static const labelSmall = TextStyle(color: medium, fontSize: 12, fontWeight: FontWeight.bold); + static const labelLarge = TextStyle(color: white, fontSize: 24, fontWeight: FontWeight.bold); + static const labelMedium = TextStyle(color: white, fontSize: 16, fontWeight: FontWeight.bold); + static const labelSmall = TextStyle(color: white, fontSize: 12, fontWeight: FontWeight.bold); - static const headlineLarge = TextStyle(color: medium, fontWeight: FontWeight.bold); - static const headlineMedium = TextStyle(color: light, fontWeight: FontWeight.bold); - static const headlineSmall = TextStyle(color: lightest, fontWeight: FontWeight.bold); + static const headlineLarge = TextStyle(color: white, fontWeight: FontWeight.bold); + static const headlineMedium = TextStyle(color: white, fontWeight: FontWeight.bold); + static const headlineSmall = TextStyle(color: white, fontWeight: FontWeight.bold); static const titleLarge = TextStyle(fontFamily: 'Outfit', letterSpacing: 0, fontSize: 24); static const titleSmall = TextStyle(fontFamily: 'Outfit', letterSpacing: 0, fontSize: 16); From d1e96b47e6c2839b82c540d97b2664776e719788 Mon Sep 17 00:00:00 2001 From: Joshua Date: Thu, 11 Jul 2024 15:25:55 -0600 Subject: [PATCH 24/24] removing deprecated gui --- one-ai-mobile | 1 - 1 file changed, 1 deletion(-) delete mode 160000 one-ai-mobile diff --git a/one-ai-mobile b/one-ai-mobile deleted file mode 160000 index dd29a00..0000000 --- a/one-ai-mobile +++ /dev/null @@ -1 +0,0 @@ -Subproject commit dd29a001e9eea051ccfc5b1061edbea077e8f723