├── linux ├── .gitignore ├── runner │ ├── main.cc │ ├── my_application.h │ └── CMakeLists.txt └── flutter │ ├── generated_plugin_registrant.h │ ├── generated_plugin_registrant.cc │ ├── generated_plugins.cmake │ └── CMakeLists.txt ├── assets ├── pin.png ├── fake-map.png ├── icon │ ├── icon.png │ ├── background.png │ ├── foreground.png │ ├── icon-mono.png │ └── logo-sqare.png ├── screenshots.jpg ├── background.svg ├── logo-foreground.svg ├── logo-sqare.svg ├── logo-round.svg └── logo-mono.svg ├── ios ├── Runner │ ├── Runner-Bridging-Header.h │ ├── Assets.xcassets │ │ ├── LaunchImage.imageset │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ ├── README.md │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-38x38@2x.png │ │ │ ├── Icon-App-38x38@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-50x50@1x.png │ │ │ ├── Icon-App-50x50@2x.png │ │ │ ├── Icon-App-57x57@1x.png │ │ │ ├── Icon-App-57x57@2x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-64x64@2x.png │ │ │ ├── Icon-App-64x64@3x.png │ │ │ ├── Icon-App-68x68@2x.png │ │ │ ├── Icon-App-72x72@1x.png │ │ │ ├── Icon-App-72x72@2x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ ├── Icon-App-Tinted-20x20@2x.png │ │ │ ├── Icon-App-Tinted-20x20@3x.png │ │ │ ├── Icon-App-Tinted-29x29@2x.png │ │ │ ├── Icon-App-Tinted-29x29@3x.png │ │ │ ├── Icon-App-Tinted-38x38@2x.png │ │ │ ├── Icon-App-Tinted-38x38@3x.png │ │ │ ├── Icon-App-Tinted-40x40@2x.png │ │ │ ├── Icon-App-Tinted-40x40@3x.png │ │ │ ├── Icon-App-Tinted-60x60@2x.png │ │ │ ├── Icon-App-Tinted-60x60@3x.png │ │ │ ├── Icon-App-Tinted-64x64@2x.png │ │ │ ├── Icon-App-Tinted-64x64@3x.png │ │ │ ├── Icon-App-Tinted-68x68@2x.png │ │ │ ├── Icon-App-Tinted-76x76@2x.png │ │ │ ├── Icon-App-Tinted-1024x1024@1x.png │ │ │ ├── Icon-App-Tinted-83.5x83.5@2x.png │ │ │ └── Contents.json │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.storyboard │ └── Info.plist ├── Flutter │ ├── Debug.xcconfig │ ├── Release.xcconfig │ └── AppFrameworkInfo.plist ├── Runner.xcodeproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── WorkspaceSettings.xcsettings │ │ └── IDEWorkspaceChecks.plist ├── RunnerTests │ └── RunnerTests.swift ├── PrivacyInfo.xcprivacy ├── .gitignore ├── Podfile └── Podfile.lock ├── macos ├── Runner │ ├── Configs │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ ├── Warnings.xcconfig │ │ └── AppInfo.xcconfig │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ ├── app_icon_128.png │ │ │ ├── app_icon_16.png │ │ │ ├── app_icon_256.png │ │ │ ├── app_icon_32.png │ │ │ ├── app_icon_512.png │ │ │ ├── app_icon_64.png │ │ │ ├── app_icon_1024.png │ │ │ └── Contents.json │ ├── Release.entitlements │ ├── AppDelegate.swift │ ├── DebugProfile.entitlements │ ├── MainFlutterWindow.swift │ └── Info.plist ├── .gitignore ├── Flutter │ ├── Flutter-Debug.xcconfig │ ├── Flutter-Release.xcconfig │ └── GeneratedPluginRegistrant.swift ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── Runner.xcodeproj │ ├── project.xcworkspace │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── RunnerTests │ └── RunnerTests.swift └── Podfile ├── windows ├── runner │ ├── resources │ │ └── app_icon.ico │ ├── resource.h │ ├── runner.exe.manifest │ ├── utils.h │ ├── flutter_window.h │ ├── main.cpp │ ├── CMakeLists.txt │ ├── utils.cpp │ ├── flutter_window.cpp │ ├── Runner.rc │ └── win32_window.h ├── .gitignore ├── flutter │ ├── generated_plugin_registrant.h │ ├── generated_plugin_registrant.cc │ ├── generated_plugins.cmake │ └── CMakeLists.txt └── CMakeLists.txt ├── .gitmodules ├── android ├── app │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── launcher_icon.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── launcher_icon.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── launcher_icon.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── launcher_icon.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── launcher_icon.png │ │ │ │ ├── drawable-hdpi │ │ │ │ │ ├── ic_launcher_background.png │ │ │ │ │ ├── ic_launcher_foreground.png │ │ │ │ │ └── ic_launcher_monochrome.png │ │ │ │ ├── drawable-mdpi │ │ │ │ │ ├── ic_launcher_background.png │ │ │ │ │ ├── ic_launcher_foreground.png │ │ │ │ │ └── ic_launcher_monochrome.png │ │ │ │ ├── drawable-xhdpi │ │ │ │ │ ├── ic_launcher_background.png │ │ │ │ │ ├── ic_launcher_foreground.png │ │ │ │ │ └── ic_launcher_monochrome.png │ │ │ │ ├── drawable-xxhdpi │ │ │ │ │ ├── ic_launcher_background.png │ │ │ │ │ ├── ic_launcher_foreground.png │ │ │ │ │ └── ic_launcher_monochrome.png │ │ │ │ ├── drawable-xxxhdpi │ │ │ │ │ ├── ic_launcher_background.png │ │ │ │ │ ├── ic_launcher_foreground.png │ │ │ │ │ └── ic_launcher_monochrome.png │ │ │ │ ├── drawable │ │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable-v21 │ │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ │ └── launcher_icon.xml │ │ │ │ ├── values │ │ │ │ │ └── styles.xml │ │ │ │ └── values-night │ │ │ │ │ └── styles.xml │ │ │ ├── kotlin │ │ │ │ └── app │ │ │ │ │ └── luogo │ │ │ │ │ └── app │ │ │ │ │ └── MainActivity.kt │ │ │ └── AndroidManifest.xml │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ └── profile │ │ │ └── AndroidManifest.xml │ └── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── .gitignore ├── build.gradle └── settings.gradle ├── ll ├── devtools_options.yaml ├── lib ├── cubit │ ├── map │ │ ├── invite_user_qr │ │ │ ├── invite_user_qr_state.dart │ │ │ └── invite_user_qr_cubit.dart │ │ ├── map_state.dart │ │ ├── map_overlay │ │ │ └── map_overlay_state.dart │ │ ├── group_sheet │ │ │ └── group_sheet_state.dart │ │ └── key_pair_qr │ │ │ ├── keypair_qr_state.dart │ │ │ └── keypair_qr_cubit.dart │ ├── home │ │ ├── settings │ │ │ ├── settings_state.dart │ │ │ └── settings_cubit.dart │ │ ├── home_state.dart │ │ ├── groups_drawer │ │ │ ├── groups_drawer_state.dart │ │ │ └── groups_drawer_cubit.dart │ │ └── home_cubit.dart │ ├── ask_for_location │ │ ├── ask_for_location_state.dart │ │ └── ask_for_location_cubit.dart │ ├── init_router │ │ ├── init_router_state.dart │ │ └── init_router_cubit.dart │ ├── widgets │ │ └── file_viewer │ │ │ ├── file_viewer_state.dart │ │ │ └── file_viewer_cubit.dart │ ├── create_profile │ │ ├── create_profile_state.dart │ │ └── create_profile_cubit.dart │ └── main │ │ ├── main_state.dart │ │ └── main_cubit.dart ├── hive │ ├── hive_adapters.dart │ ├── hive_adapters.g.yaml │ ├── hive_registrar.g.dart │ └── hive_adapters.g.dart ├── model │ ├── user_state.dart │ ├── hive_latlng.dart │ ├── group_info.dart │ ├── message_embed.dart │ └── group_settings.dart ├── utils │ ├── s5_logger.dart │ ├── check_s5_connectivity.dart │ └── mapping.dart ├── view │ ├── widgets │ │ ├── circle_avatar_styled_named.dart │ │ └── file_viewer.dart │ └── page │ │ ├── map │ │ ├── bottom_sheet_info_modal.dart │ │ └── invite_user_qr_dialog.dart │ │ ├── ask_for_location.dart │ │ ├── init_router.dart │ │ ├── home │ │ └── settings.dart │ │ └── map.dart └── main.dart ├── integration_test └── simple_test.dart ├── PRIVACY.md ├── flutter_launcher_icons.yaml ├── .gitignore ├── analysis_options.yaml ├── pubspec.yaml ├── .github └── workflows │ └── build_on_tag.yaml └── README.md /linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /assets/pin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/assets/pin.png -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /assets/fake-map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/assets/fake-map.png -------------------------------------------------------------------------------- /assets/icon/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/assets/icon/icon.png -------------------------------------------------------------------------------- /assets/screenshots.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/assets/screenshots.jpg -------------------------------------------------------------------------------- /assets/icon/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/assets/icon/background.png -------------------------------------------------------------------------------- /assets/icon/foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/assets/icon/foreground.png -------------------------------------------------------------------------------- /assets/icon/icon-mono.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/assets/icon/icon-mono.png -------------------------------------------------------------------------------- /assets/icon/logo-sqare.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/assets/icon/logo-sqare.png -------------------------------------------------------------------------------- /macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule ".flutter"] 2 | path = .flutter 3 | url = https://github.com/flutter/flutter.git 4 | branch = stable 5 | -------------------------------------------------------------------------------- /macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/dgph 7 | **/xcuserdata/ 8 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/android/app/src/main/res/mipmap-hdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/android/app/src/main/res/mipmap-mdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/android/app/src/main/res/drawable-hdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/android/app/src/main/res/drawable-hdpi/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/android/app/src/main/res/drawable-mdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/android/app/src/main/res/drawable-mdpi/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/android/app/src/main/res/drawable-xhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/android/app/src/main/res/drawable-xhdpi/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/android/app/src/main/res/drawable-xxhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/android/app/src/main/res/drawable-xxhdpi/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/ic_launcher_monochrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_monochrome.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-38x38@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-38x38@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-38x38@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-38x38@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-64x64@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-64x64@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-64x64@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-64x64@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-68x68@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-68x68@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /android/app/src/main/kotlin/app/luogo/app/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package app.luogo.app 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() 6 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-38x38@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-38x38@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-38x38@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-38x38@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-64x64@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-64x64@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-64x64@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-64x64@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-68x68@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-68x68@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-76x76@2x.png -------------------------------------------------------------------------------- /ll: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Runs flutterw while filtering out useless logs 3 | # "Less Logs" => ll 4 | ./flutterw "$@" | grep -v -E "openmls|Mbgl-HttpRequest|SimpleDecode|SseCodec|ImageReader|E/Mbgl" 5 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukehmcc/luogo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-83.5x83.5@2x.png -------------------------------------------------------------------------------- /devtools_options.yaml: -------------------------------------------------------------------------------- 1 | description: This file stores settings for Dart & Flutter DevTools. 2 | documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states 3 | extensions: 4 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/cubit/map/invite_user_qr/invite_user_qr_state.dart: -------------------------------------------------------------------------------- 1 | abstract class InviteUserQrState {} 2 | 3 | class InviteUserQrInital extends InviteUserQrState {} 4 | 5 | class InviteUserQrInviteCreated extends InviteUserQrState {} 6 | -------------------------------------------------------------------------------- /linux/runner/main.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | int main(int argc, char** argv) { 4 | g_autoptr(MyApplication) app = my_application_new(); 5 | return g_application_run(G_APPLICATION(app), argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/cubit/home/settings/settings_state.dart: -------------------------------------------------------------------------------- 1 | abstract class SettingsState {} 2 | 3 | class SettingsInitial extends SettingsState {} 4 | 5 | class SettingsNewNodeError extends SettingsState {} 6 | 7 | class SettingsNewNodeSucsess extends SettingsState {} 8 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip 6 | -------------------------------------------------------------------------------- /lib/cubit/home/home_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:luogo/model/group_info.dart'; 2 | 3 | abstract class HomeState {} 4 | 5 | class HomeInitial extends HomeState {} 6 | 7 | class HomeGroupSelected extends HomeState { 8 | final GroupInfo? group; 9 | HomeGroupSelected(this.group); 10 | } 11 | -------------------------------------------------------------------------------- /macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /lib/hive/hive_adapters.dart: -------------------------------------------------------------------------------- 1 | import 'package:hive_ce/hive.dart'; 2 | import 'package:luogo/model/hive_latlng.dart'; 3 | import 'package:luogo/model/user_state.dart'; 4 | 5 | // Put any new hive ready class here 6 | @GenerateAdapters([AdapterSpec(), AdapterSpec()]) 7 | part 'hive_adapters.g.dart'; 8 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/to/reference-keystore 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /lib/cubit/ask_for_location/ask_for_location_state.dart: -------------------------------------------------------------------------------- 1 | part of 'ask_for_location_cubit.dart'; 2 | 3 | abstract class AskForLocationState {} 4 | 5 | class AskForLocationInitial extends AskForLocationState {} 6 | 7 | class AskForLocationDenied extends AskForLocationState {} 8 | 9 | class AskForLocationApproved extends AskForLocationState {} 10 | -------------------------------------------------------------------------------- /ios/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /macos/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /lib/cubit/map/map_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:luogo/model/user_state.dart'; 2 | 3 | abstract class MapState {} 4 | 5 | class MapInitial extends MapState {} 6 | 7 | class MapReady extends MapState {} 8 | 9 | class MapSymbolClicked extends MapState { 10 | final UserState userState; 11 | final bool isYou; 12 | MapSymbolClicked({required this.userState, required this.isYou}); 13 | } 14 | -------------------------------------------------------------------------------- /macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @main 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | 10 | override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { 11 | return true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /lib/cubit/init_router/init_router_state.dart: -------------------------------------------------------------------------------- 1 | abstract class InitRouterState {} 2 | 3 | class InitRouterInitial extends InitRouterState {} 4 | 5 | class InitRouterLoading extends InitRouterState {} 6 | 7 | class InitRouterSuccess extends InitRouterState { 8 | final RouteType route; 9 | InitRouterSuccess({required this.route}); 10 | } 11 | 12 | enum RouteType { home, login, locationPerms } 13 | -------------------------------------------------------------------------------- /assets/background.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void fl_register_plugins(FlPluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void RegisterPlugins(flutter::PluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /lib/cubit/map/map_overlay/map_overlay_state.dart: -------------------------------------------------------------------------------- 1 | abstract class MapOverlayState {} 2 | 3 | class MapOverlayInitial extends MapOverlayState {} 4 | 5 | class MapOverlayReady extends MapOverlayState {} 6 | 7 | class MapOverlayQRPopupPressed extends MapOverlayState { 8 | String keypair; 9 | MapOverlayQRPopupPressed({required this.keypair}); 10 | } 11 | 12 | class MapOverlayGroupPopupPressed extends MapOverlayState {} 13 | -------------------------------------------------------------------------------- /lib/cubit/widgets/file_viewer/file_viewer_state.dart: -------------------------------------------------------------------------------- 1 | // STATE 2 | class FileViewerState { 3 | final String content; 4 | final bool isLoading; 5 | const FileViewerState({this.content = '', this.isLoading = true}); 6 | FileViewerState copyWith({String? content, bool? isLoading}) => 7 | FileViewerState( 8 | content: content ?? this.content, 9 | isLoading: isLoading ?? this.isLoading); 10 | } 11 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.server 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /integration_test/simple_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:integration_test/integration_test.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | import 'package:s5_messenger/s5_messenger.dart'; 4 | 5 | void main() { 6 | IntegrationTestWidgetsFlutterBinding.ensureInitialized(); 7 | setUpAll(() async => await RustLib.init()); 8 | test('Can call rust function', () async { 9 | expect(greet(name: "Tom"), "Hello, Tom!"); 10 | }); 11 | } 12 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | 4 | @main 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController() 7 | let windowFrame = self.frame 8 | self.contentViewController = flutterViewController 9 | self.setFrame(windowFrame, display: true) 10 | 11 | RegisterGeneratedPlugins(registry: flutterViewController) 12 | 13 | super.awakeFromNib() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /linux/runner/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /lib/cubit/map/group_sheet/group_sheet_state.dart: -------------------------------------------------------------------------------- 1 | abstract class GroupSheetState {} 2 | 3 | class GroupSheetInitial extends GroupSheetState {} 4 | 5 | class GroupSheetInviteDialogPressed extends GroupSheetState {} 6 | 7 | class GroupSheetInviteCreated extends GroupSheetState {} 8 | 9 | class GroupSheetShareLocationUpdated extends GroupSheetState {} 10 | 11 | class GroupSheetShareLocationOneShot extends GroupSheetState {} 12 | 13 | class GroupSheetAllUsersLoaded extends GroupSheetState {} 14 | -------------------------------------------------------------------------------- /windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /lib/cubit/create_profile/create_profile_state.dart: -------------------------------------------------------------------------------- 1 | abstract class CreateProfilePageState {} 2 | 3 | class CreateProfileInitial extends CreateProfilePageState {} 4 | 5 | class CreateProfileTextEdited extends CreateProfilePageState {} 6 | 7 | class CreateProfileColorChanged extends CreateProfilePageState {} 8 | 9 | class CreateProfileSuccess extends CreateProfilePageState {} 10 | 11 | class CreateProfileError extends CreateProfilePageState { 12 | final String message; 13 | CreateProfileError(this.message); 14 | } 15 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | allprojects { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | // [required] background_fetch 6 | maven { url "${project(':background_fetch').projectDir}/libs" } 7 | } 8 | } 9 | 10 | rootProject.buildDir = "../build" 11 | subprojects { 12 | project.buildDir = "${rootProject.buildDir}/${project.name}" 13 | } 14 | subprojects { 15 | project.evaluationDependsOn(":app") 16 | } 17 | 18 | tasks.register("clean", Delete) { 19 | delete rootProject.buildDir 20 | } 21 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /PRIVACY.md: -------------------------------------------------------------------------------- 1 | 2 | No personal data shared with us will be given to any third party, under any circumstances. Your data will also never be used by us for any purpose without specific permission. 3 | 4 | The app engages in no ad targeting, data mining, or other activities that may compromise your privacy, and we do not affiliate ourselves with any third parties that do so. 5 | 6 | The architecture of the app makes it impossible for us to collect any relevant data (geolocation). All location data is E2EE before leaving the client. And all code is open source to verify. 7 | -------------------------------------------------------------------------------- /lib/hive/hive_adapters.g.yaml: -------------------------------------------------------------------------------- 1 | # Generated by Hive CE 2 | # Manual modifications may be necessary for certain migrations 3 | # Check in to version control 4 | nextTypeId: 2 5 | types: 6 | HiveLatLng: 7 | typeId: 0 8 | nextIndex: 2 9 | fields: 10 | lat: 11 | index: 0 12 | long: 13 | index: 1 14 | UserState: 15 | typeId: 1 16 | nextIndex: 4 17 | fields: 18 | coords: 19 | index: 0 20 | ts: 21 | index: 1 22 | name: 23 | index: 2 24 | color: 25 | index: 3 26 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/launcher_icon.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /lib/cubit/home/groups_drawer/groups_drawer_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:luogo/model/group_info.dart'; 2 | 3 | abstract class GroupsDrawerState {} 4 | 5 | class GroupsDrawerInitial extends GroupsDrawerState {} 6 | 7 | class GroupsDrawerLoading extends GroupsDrawerState {} 8 | 9 | class GroupsDrawerLoaded extends GroupsDrawerState { 10 | final GroupInfoList groups; 11 | final GroupInfo? group; 12 | GroupsDrawerLoaded(this.groups, [this.group]); 13 | } 14 | 15 | class GroupsDrawerError extends GroupsDrawerState { 16 | final String message; 17 | GroupsDrawerError(this.message); 18 | } 19 | -------------------------------------------------------------------------------- /lib/hive/hive_registrar.g.dart: -------------------------------------------------------------------------------- 1 | // Generated by Hive CE 2 | // Do not modify 3 | // Check in to version control 4 | 5 | import 'package:hive_ce/hive.dart'; 6 | import 'package:luogo/hive/hive_adapters.dart'; 7 | 8 | extension HiveRegistrar on HiveInterface { 9 | void registerAdapters() { 10 | registerAdapter(HiveLatLngAdapter()); 11 | registerAdapter(UserStateAdapter()); 12 | } 13 | } 14 | 15 | extension IsolatedHiveRegistrar on IsolatedHiveInterface { 16 | void registerAdapters() { 17 | registerAdapter(HiveLatLngAdapter()); 18 | registerAdapter(UserStateAdapter()); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /lib/cubit/map/key_pair_qr/keypair_qr_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:luogo/model/group_info.dart'; 2 | 3 | abstract class KeypairQRState {} 4 | 5 | class KeypairQRInitial extends KeypairQRState {} 6 | 7 | class KeypairQSelection extends KeypairQRState { 8 | final bool isQRSelected; 9 | KeypairQSelection({required this.isQRSelected}); 10 | } 11 | 12 | class KeyPairQrGroupLoaded extends KeypairQRState { 13 | final GroupInfoList groups; 14 | final GroupInfo? group; 15 | KeyPairQrGroupLoaded(this.groups, [this.group]); 16 | } 17 | 18 | class KeyPairQrGroupError extends KeypairQRState { 19 | final String message; 20 | KeyPairQrGroupError(this.message); 21 | } 22 | -------------------------------------------------------------------------------- /windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ios/PrivacyInfo.xcprivacy: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | NSPrivacyAccessedAPITypes 7 | 8 | 9 | 10 | NSPrivacyAccessedAPIType 11 | NSPrivacyAccessedAPICategoryUserDefaults 12 | 13 | NSPrivacyAccessedAPITypeReasons 14 | 15 | CA92.1 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = s5_messenger_example 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterPackageExample 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2025 com.example. All rights reserved. 15 | -------------------------------------------------------------------------------- /lib/cubit/widgets/file_viewer/file_viewer_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | import 'package:luogo/cubit/widgets/file_viewer/file_viewer_state.dart'; 5 | 6 | class FileViewerCubit extends Cubit { 7 | final String filePath; 8 | FileViewerCubit(this.filePath) : super(const FileViewerState()) { 9 | loadFile(); 10 | } 11 | Future loadFile() async { 12 | try { 13 | final file = File(filePath); 14 | final content = await file.readAsString(); 15 | emit(state.copyWith(content: content, isLoading: false)); 16 | } catch (e) { 17 | emit(state.copyWith(content: 'Error: $e', isLoading: false)); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | void RegisterPlugins(flutter::PluginRegistry* registry) { 14 | DynamicColorPluginCApiRegisterWithRegistrar( 15 | registry->GetRegistrarForPlugin("DynamicColorPluginCApi")); 16 | GeolocatorWindowsRegisterWithRegistrar( 17 | registry->GetRegistrarForPlugin("GeolocatorWindows")); 18 | UrlLauncherWindowsRegisterWithRegistrar( 19 | registry->GetRegistrarForPlugin("UrlLauncherWindows")); 20 | } 21 | -------------------------------------------------------------------------------- /windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | -------------------------------------------------------------------------------- /flutter_launcher_icons.yaml: -------------------------------------------------------------------------------- 1 | # flutter pub run flutter_launcher_icons 2 | flutter_launcher_icons: 3 | image_path: "assets/icon/icon.png" 4 | 5 | android: "launcher_icon" 6 | min_sdk_android: 21 # android min sdk min:16, default 21 7 | adaptive_icon_background: "assets/icon/background.png" 8 | adaptive_icon_foreground: "assets/icon/foreground.png" 9 | adaptive_icon_foreground_inset: 16 10 | adaptive_icon_monochrome: "assets/icon/icon-mono.png" 11 | 12 | ios: true 13 | remove_alpha_ios: true 14 | image_path_ios: "assets/icon/logo-sqare.png" 15 | # image_path_ios_dark_transparent: "assets/icon/icon_dark.png" 16 | image_path_ios_tinted_grayscale: "assets/icon/icon-mono.png" 17 | desaturate_tinted_to_grayscale_ios: true 18 | background_color_ios: "#6237AE" 19 | -------------------------------------------------------------------------------- /lib/cubit/home/home_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_bloc/flutter_bloc.dart'; 2 | import 'package:luogo/cubit/home/home_state.dart'; 3 | import 'package:luogo/model/group_info.dart'; 4 | 5 | /// A Cubit class for managing the home state. 6 | /// 7 | /// Example usage: 8 | /// ```dart 9 | /// BlocProvider( 10 | /// create: (context) =>; HomeCubit(), 11 | /// child: YourHomeWidget(), 12 | /// ) 13 | /// ``` 14 | 15 | class HomeCubit extends Cubit { 16 | GroupInfo? group; 17 | HomeCubit() : super(HomeInitial()); 18 | 19 | void groupSelected(GroupInfo? groupSelected) { 20 | if (group != groupSelected) { 21 | group = groupSelected; 22 | emit(HomeGroupSelected(group)); 23 | } 24 | } 25 | 26 | void clearSelection() { 27 | emit(HomeInitial()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | #include 10 | #include 11 | 12 | void fl_register_plugins(FlPluginRegistry* registry) { 13 | g_autoptr(FlPluginRegistrar) dynamic_color_registrar = 14 | fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin"); 15 | dynamic_color_plugin_register_with_registrar(dynamic_color_registrar); 16 | g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = 17 | fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); 18 | url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); 19 | } 20 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | def flutterSdkPath = { 3 | def properties = new Properties() 4 | file("local.properties").withInputStream { properties.load(it) } 5 | def flutterSdkPath = properties.getProperty("flutter.sdk") 6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 7 | return flutterSdkPath 8 | }() 9 | 10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") 11 | 12 | repositories { 13 | google() 14 | mavenCentral() 15 | gradlePluginPortal() 16 | } 17 | } 18 | 19 | plugins { 20 | id "dev.flutter.flutter-plugin-loader" version "1.0.0" 21 | id "com.android.application" version "8.6.0" apply false 22 | id "org.jetbrains.kotlin.android" version "2.1.0" apply false 23 | } 24 | 25 | include ":app" 26 | -------------------------------------------------------------------------------- /assets/logo-foreground.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | L 10 | 11 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 12.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/cubit/ask_for_location/ask_for_location_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_bloc/flutter_bloc.dart'; 2 | import 'package:luogo/services/location_service.dart'; 3 | import 'package:shared_preferences/shared_preferences.dart'; 4 | 5 | part 'ask_for_location_state.dart'; 6 | 7 | class AskForLocationCubit extends Cubit { 8 | final SharedPreferencesWithCache prefs; 9 | final LocationService locationService; 10 | AskForLocationCubit({ 11 | required this.prefs, 12 | required this.locationService, 13 | }) : super(AskForLocationInitial()); 14 | 15 | static AskForLocationCubit get(context) => BlocProvider.of(context); 16 | 17 | Future requestPerms() async { 18 | await locationService.askForLocationPermissions(); 19 | locationService.startPeriodicUpdates(); 20 | prefs.setBool("location-perms-have-been-requested", true); 21 | emit(AskForLocationApproved()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | dynamic_color 7 | url_launcher_linux 8 | ) 9 | 10 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 11 | s5_messenger 12 | ) 13 | 14 | set(PLUGIN_BUNDLED_LIBRARIES) 15 | 16 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 17 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) 18 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 19 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 20 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 21 | endforeach(plugin) 22 | 23 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 24 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) 25 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 26 | endforeach(ffi_plugin) 27 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | dynamic_color 7 | geolocator_windows 8 | url_launcher_windows 9 | ) 10 | 11 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 12 | s5_messenger 13 | ) 14 | 15 | set(PLUGIN_BUNDLED_LIBRARIES) 16 | 17 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 18 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) 19 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 20 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 21 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 22 | endforeach(plugin) 23 | 24 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 25 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) 26 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 27 | endforeach(ffi_plugin) 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .build/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | .swiftpm/ 13 | migrate_working_dir/ 14 | 15 | # IntelliJ related 16 | *.iml 17 | *.ipr 18 | *.iws 19 | .idea/ 20 | 21 | # The .vscode folder contains launch configuration and tasks you configure in 22 | # VS Code which you may wish to be included in version control, so this line 23 | # is commented out by default. 24 | #.vscode/ 25 | 26 | # Flutter/Dart/Pub related 27 | **/doc/api/ 28 | **/ios/Flutter/.last_build_id 29 | .dart_tool/ 30 | .flutter-plugins 31 | .flutter-plugins-dependencies 32 | .pub-cache/ 33 | .pub/ 34 | /build/ 35 | 36 | # Symbolication related 37 | app.*.symbols 38 | 39 | # Obfuscation related 40 | app.*.map.json 41 | 42 | # Android Studio will place build artifacts here 43 | /android/app/debug 44 | /android/app/profile 45 | /android/app/release 46 | /android/app/.cxx 47 | 48 | data 49 | TODO.md 50 | -------------------------------------------------------------------------------- /lib/model/user_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:hive_ce/hive.dart'; 2 | import 'package:luogo/model/hive_latlng.dart'; 3 | 4 | /// Represents a user's local state with coordinates, timestamp, name, and color. 5 | /// 6 | /// Example: 7 | /// ```dart 8 | /// final userState = UserState( 9 | /// coords: HiveLatLng(lat: 37.7749, lng: -122.4194), 10 | /// ts: DateTime.now().millisecondsSinceEpoch, 11 | /// name: "John Doe", 12 | /// color: 0xFF0000, 13 | /// ); 14 | /// ``` 15 | class UserState extends HiveObject { 16 | final HiveLatLng coords; 17 | final int ts; // Timestamp when last updated 18 | final String name; 19 | final int color; 20 | UserState( 21 | {required this.coords, 22 | required this.ts, 23 | required this.name, 24 | required this.color}); 25 | 26 | DateTime get date => DateTime.fromMillisecondsSinceEpoch(ts); 27 | 28 | @override 29 | String toString() => 30 | 'UserState(coords: $coords, ts: $ts, name: $name, color: $color)'; 31 | } 32 | -------------------------------------------------------------------------------- /lib/utils/s5_logger.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | 3 | import 'package:lib5/util.dart'; 4 | 5 | // Quick change of: https://github.com/s5-dev/lib5/blob/main/lib/src/node/logger/simple.dart 6 | // Supresses spammy warns 7 | class SilentLogger extends Logger { 8 | final String prefix; 9 | final bool format; 10 | final bool showVerbose; 11 | 12 | SilentLogger({ 13 | this.prefix = '', 14 | this.format = true, 15 | this.showVerbose = false, 16 | }); 17 | 18 | @override 19 | void info(String s) { 20 | log(prefix + s.replaceAll(RegExp('\u001b\\[\\d+m'), '')); 21 | } 22 | 23 | @override 24 | void error(String s) { 25 | log('$prefix[ERROR] $s'); 26 | } 27 | 28 | @override 29 | void verbose(String s) { 30 | if (!showVerbose) return; 31 | log(prefix + s); 32 | } 33 | 34 | @override 35 | void warn(String s) { 36 | // Silent - no output 37 | } 38 | 39 | @override 40 | void catched(e, st, [context]) { 41 | // Silent - no output 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /assets/logo-sqare.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | L 11 | 12 | -------------------------------------------------------------------------------- /assets/logo-round.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | L 11 | 12 | -------------------------------------------------------------------------------- /assets/logo-mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | L 11 | 12 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "win32_window.h" 10 | 11 | // A window that does nothing but host a Flutter view. 12 | class FlutterWindow : public Win32Window { 13 | public: 14 | // Creates a new FlutterWindow hosting a Flutter view running |project|. 15 | explicit FlutterWindow(const flutter::DartProject& project); 16 | virtual ~FlutterWindow(); 17 | 18 | protected: 19 | // Win32Window: 20 | bool OnCreate() override; 21 | void OnDestroy() override; 22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 23 | LPARAM const lparam) noexcept override; 24 | 25 | private: 26 | // The project to run. 27 | flutter::DartProject project_; 28 | 29 | // The Flutter instance hosted by this window. 30 | std::unique_ptr flutter_controller_; 31 | }; 32 | 33 | #endif // RUNNER_FLUTTER_WINDOW_H_ 34 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /linux/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | project(runner LANGUAGES CXX) 3 | 4 | # Define the application target. To change its name, change BINARY_NAME in the 5 | # top-level CMakeLists.txt, not the value here, or `flutter run` will no longer 6 | # work. 7 | # 8 | # Any new source files that you add to the application should be added here. 9 | add_executable(${BINARY_NAME} 10 | "main.cc" 11 | "my_application.cc" 12 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 13 | ) 14 | 15 | # Apply the standard set of build settings. This can be removed for applications 16 | # that need different build settings. 17 | apply_standard_settings(${BINARY_NAME}) 18 | 19 | # Add preprocessor definitions for the application ID. 20 | add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") 21 | 22 | # Add dependency libraries. Add any application-specific dependencies here. 23 | target_link_libraries(${BINARY_NAME} PRIVATE flutter) 24 | target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) 25 | 26 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 27 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /lib/model/hive_latlng.dart: -------------------------------------------------------------------------------- 1 | import 'package:hive_ce/hive.dart'; 2 | import 'package:maplibre_gl/maplibre_gl.dart'; 3 | 4 | /// A Hive-adapted object for storing geographic coordinates (latitude and longitude). 5 | /// 6 | /// This class provides conversion utilities between [HiveLatLng] and [LatLng] (from maplibre_gl), 7 | /// allowing seamless interoperability between Hive storage and MapLibre GL's coordinate system. 8 | /// 9 | /// Example: 10 | /// ```dart 11 | /// final hiveCoords = HiveLatLng(lat: 37.7749, long: -122.4194); 12 | /// final mapCoords = hiveCoords.toLatLng(); // Convert to MapLibre's LatLng 13 | /// ``` 14 | class HiveLatLng extends HiveObject { 15 | double lat; 16 | double long; 17 | 18 | HiveLatLng({required this.lat, required this.long}); 19 | 20 | factory HiveLatLng.fromLatLng(LatLng latLng) { 21 | return HiveLatLng(lat: latLng.latitude, long: latLng.longitude); 22 | } 23 | 24 | LatLng toLatLng() { 25 | return LatLng(lat, long); 26 | } 27 | 28 | @override 29 | String toString() { 30 | return 'HiveLatLng(latty: $lat, longy: $long)'; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /lib/view/widgets/circle_avatar_styled_named.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class CircleAvatarStyledNamed extends StatelessWidget { 4 | final String name; 5 | final Color color; 6 | const CircleAvatarStyledNamed({ 7 | super.key, 8 | required this.name, 9 | required this.color, 10 | }); 11 | @override 12 | Widget build(BuildContext context) { 13 | return Container( 14 | decoration: BoxDecoration( 15 | shape: BoxShape.circle, 16 | border: Border.all( 17 | color: Colors.black, 18 | width: 2.0, // Adjust border thickness as needed 19 | ), 20 | ), 21 | child: CircleAvatar( 22 | backgroundColor: color, 23 | child: Center( 24 | child: FittedBox( 25 | fit: BoxFit.scaleDown, 26 | child: Text( 27 | name[0].toUpperCase(), 28 | textAlign: TextAlign.center, 29 | style: TextStyle( 30 | fontSize: 40, // large font size; will be scaled to fit 31 | height: 40.0, // remove extra line spacing 32 | ), 33 | textHeightBehavior: TextHeightBehavior( 34 | applyHeightToFirstAscent: false, 35 | applyHeightToLastDescent: false, 36 | ), 37 | ), 38 | ), 39 | ), 40 | ), 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/utils/check_s5_connectivity.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:lib5/registry.dart'; 3 | import 'package:luogo/main.dart'; 4 | import 'package:s5/s5.dart'; 5 | 6 | // Quick function to check connectivity 7 | Future checkS5Online(S5 s5) async { 8 | final Uint8List testKey = Uint8List(32); 9 | for (int i = 0; i < 32; i++) { 10 | testKey[i] = i; 11 | } 12 | try { 13 | final Uint8List testData = Uint8List.fromList([1, 2, 3]); 14 | final KeyPairEd25519 kp = 15 | await s5.api.crypto.newKeyPairEd25519(seed: testKey); 16 | int revision = 1; 17 | 18 | // grab it first to check revison 19 | SignedRegistryEntry? res = await s5.api.registryGet(kp.publicKey); 20 | if (res != null) { 21 | revision = res.revision + 1; 22 | } 23 | 24 | // set it 25 | await s5.api.registrySet( 26 | await signRegistryEntry( 27 | kp: kp, 28 | data: testData, 29 | revision: revision, 30 | crypto: s5.api.crypto, 31 | ), 32 | ); 33 | // then grab and test 34 | res = await s5.api.registryGet(kp.publicKey); 35 | if (res != null && listEquals(res.data, testData)) { 36 | return true; 37 | } else { 38 | logger.d("Data mismatch, expected: $testData, got: ${res?.data}"); 39 | return false; 40 | } 41 | } catch (e) { 42 | logger.d("Failed due to error $e"); 43 | return false; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/cubit/map/invite_user_qr/invite_user_qr_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:luogo/cubit/map/invite_user_qr/invite_user_qr_state.dart'; 4 | import 'package:luogo/model/group_info.dart'; 5 | import 'package:s5_messenger/s5_messenger.dart'; 6 | 7 | /// A Cubit class for managing the invite user QR state. 8 | /// 9 | /// This cubit handles QR code processing for user invitations. 10 | /// 11 | /// Example usage: 12 | /// ```dart 13 | /// BlocProvider( 14 | /// create: (context) => InviteUserQrCubit( 15 | /// s5messenger: yourS5MessengerInstance, 16 | /// groupInfo: yourGroupInfoInstance, 17 | /// ), 18 | /// child: YourInviteUserQrWidget(), 19 | /// ) 20 | /// ``` 21 | class InviteUserQrCubit extends Cubit { 22 | S5Messenger s5messenger; 23 | GroupInfo groupInfo; 24 | InviteUserQrCubit({required this.s5messenger, required this.groupInfo}) 25 | : super(InviteUserQrInital()) { 26 | _populateQrCode(); 27 | } 28 | String luogoInviteToken = ""; 29 | String? welcomeMsg; // This is the invite token that another user scans 30 | final TextEditingController textController = TextEditingController(); 31 | 32 | void _populateQrCode() async { 33 | luogoInviteToken = 34 | "luogo-invite-key: ${(await s5messenger.group(groupInfo.id).generateExternalCommitInvite())}"; 35 | emit(InviteUserQrInviteCreated()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flutter_window.h" 6 | #include "utils.h" 7 | 8 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 9 | _In_ wchar_t *command_line, _In_ int show_command) { 10 | // Attach to console when present (e.g., 'flutter run') or create a 11 | // new console when running with a debugger. 12 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 13 | CreateAndAttachConsole(); 14 | } 15 | 16 | // Initialize COM, so that it is available for use in the library and/or 17 | // plugins. 18 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 19 | 20 | flutter::DartProject project(L"data"); 21 | 22 | std::vector command_line_arguments = 23 | GetCommandLineArguments(); 24 | 25 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); 26 | 27 | FlutterWindow window(project); 28 | Win32Window::Point origin(10, 10); 29 | Win32Window::Size size(1280, 720); 30 | if (!window.Create(L"s5_messenger_example", origin, size)) { 31 | return EXIT_FAILURE; 32 | } 33 | window.SetQuitOnClose(true); 34 | 35 | ::MSG msg; 36 | while (::GetMessage(&msg, nullptr, 0, 0)) { 37 | ::TranslateMessage(&msg); 38 | ::DispatchMessage(&msg); 39 | } 40 | 41 | ::CoUninitialize(); 42 | return EXIT_SUCCESS; 43 | } 44 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at https://dart.dev/lints. 17 | # 18 | # Instead of disabling a lint rule for the entire project in the 19 | # section below, it can also be suppressed for a single line of code 20 | # or a specific dart file by using the `// ignore: name_of_lint` and 21 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 22 | # producing the lint. 23 | rules: 24 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 25 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 26 | 27 | # Additional information about this file can be found at 28 | # https://dart.dev/guides/language/analysis-options 29 | -------------------------------------------------------------------------------- /macos/Podfile: -------------------------------------------------------------------------------- 1 | platform :osx, '10.14' 2 | 3 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 4 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 5 | 6 | project 'Runner', { 7 | 'Debug' => :debug, 8 | 'Profile' => :release, 9 | 'Release' => :release, 10 | } 11 | 12 | def flutter_root 13 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) 14 | unless File.exist?(generated_xcode_build_settings_path) 15 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" 16 | end 17 | 18 | File.foreach(generated_xcode_build_settings_path) do |line| 19 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 20 | return matches[1].strip if matches 21 | end 22 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" 23 | end 24 | 25 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 26 | 27 | flutter_macos_podfile_setup 28 | 29 | target 'Runner' do 30 | use_frameworks! 31 | use_modular_headers! 32 | 33 | flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) 34 | target 'RunnerTests' do 35 | inherit! :search_paths 36 | end 37 | end 38 | 39 | post_install do |installer| 40 | installer.pods_project.targets.each do |target| 41 | flutter_additional_macos_build_settings(target) 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/cubit/main/main_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:luogo/services/location_service.dart'; 2 | import 'package:s5/s5.dart'; 3 | import 'package:s5_messenger/s5_messenger.dart'; 4 | import 'package:shared_preferences/shared_preferences.dart'; 5 | 6 | sealed class MainState {} 7 | 8 | class MainStateInitial extends MainState {} 9 | 10 | class MainStateLoading extends MainState {} 11 | 12 | class MainStateNeedsLocationPermission extends MainState { 13 | final SharedPreferencesWithCache prefs; 14 | final LocationService locationService; 15 | 16 | MainStateNeedsLocationPermission({ 17 | required this.prefs, 18 | required this.locationService, 19 | }); 20 | } 21 | 22 | /// The fast to init dependencies can be emitted quickly here 23 | class MainStateLightInitialized extends MainState { 24 | final SharedPreferencesWithCache prefs; 25 | final LocationService locationService; 26 | 27 | MainStateLightInitialized({ 28 | required this.prefs, 29 | required this.locationService, 30 | }); 31 | } 32 | 33 | /// The slower to init dependencies can be emitted later here 34 | class MainStateHeavyInitialized extends MainState { 35 | final S5 s5; 36 | final S5Messenger s5messenger; 37 | final SharedPreferencesWithCache prefs; 38 | final LocationService locationService; 39 | 40 | MainStateHeavyInitialized({ 41 | required this.s5, 42 | required this.s5messenger, 43 | required this.prefs, 44 | required this.locationService, 45 | }); 46 | } 47 | 48 | class MainStateError extends MainState { 49 | final String message; 50 | MainStateError(this.message); 51 | } 52 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | platform :ios, '13.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | target 'RunnerTests' do 36 | inherit! :search_paths 37 | end 38 | end 39 | 40 | post_install do |installer| 41 | installer.pods_project.targets.each do |target| 42 | flutter_additional_ios_build_settings(target) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/view/widgets/file_viewer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:luogo/cubit/widgets/file_viewer/file_viewer_cubit.dart'; 4 | import 'package:luogo/cubit/widgets/file_viewer/file_viewer_state.dart'; 5 | import 'package:luogo/view/widgets/silly_progress_indicator.dart'; 6 | 7 | // A quick and simple file viewer for viewing logs 8 | class TextFileViewer extends StatelessWidget { 9 | final String filePath; 10 | const TextFileViewer({super.key, required this.filePath}); 11 | 12 | @override 13 | Widget build(BuildContext context) => BlocProvider( 14 | create: (_) => FileViewerCubit(filePath), 15 | child: BlocBuilder( 16 | builder: (_, state) => state.isLoading 17 | ? const Center(child: SillyCircularProgressIndicator()) 18 | : Scrollbar( 19 | thickness: 10.0, 20 | thumbVisibility: true, 21 | trackVisibility: true, 22 | interactive: true, 23 | child: SingleChildScrollView( 24 | scrollDirection: Axis.vertical, 25 | child: SingleChildScrollView( 26 | scrollDirection: Axis.horizontal, 27 | child: SelectableText( 28 | state.content, 29 | style: const TextStyle( 30 | fontFamily: 'monospace', fontSize: 12), 31 | ), 32 | ), 33 | ), 34 | ), 35 | )); 36 | } 37 | -------------------------------------------------------------------------------- /lib/model/group_info.dart: -------------------------------------------------------------------------------- 1 | /// A model class for representing group information. 2 | /// 3 | /// Example usage: 4 | /// ```dart 5 | /// final group = GroupInfo(id: "xxx", name: "xxx"); 6 | /// ``` 7 | class GroupInfo { 8 | final String id; 9 | final String name; 10 | 11 | GroupInfo({required this.id, required this.name}); 12 | 13 | factory GroupInfo.fromJson(dynamic json) { 14 | return GroupInfo( 15 | id: json['id'], 16 | name: json['name'], 17 | ); 18 | } 19 | 20 | static GroupInfoList fromJsonList(List jsonList) { 21 | return GroupInfoList( 22 | groups: jsonList.map((json) => GroupInfo.fromJson(json)).toList()); 23 | } 24 | 25 | @override 26 | String toString() { 27 | return "ID: $id, Name: $name"; 28 | } 29 | } 30 | 31 | /// A utility class for managing a list of GroupInfo objects. 32 | /// 33 | /// 34 | /// Example usage: 35 | /// ```dart 36 | /// final groups = GroupInfoList(groups: [GroupInfo(id: "xxx", name: "xxx")]); 37 | /// final group = groups.findByID("xxx"); // Returns the GroupInfo if found 38 | /// final firstGroup = groups[0]; // Accesses the first group 39 | /// final count = groups.length; // Gets the number of groups 40 | /// ``` 41 | class GroupInfoList { 42 | List groups; 43 | GroupInfoList({required this.groups}); 44 | 45 | GroupInfo? findByID(String id) { 46 | for (GroupInfo group in groups) { 47 | if (group.id == id) { 48 | return group; 49 | } 50 | } 51 | return null; 52 | } 53 | 54 | GroupInfo operator [](int index) => groups[index]; 55 | int get length => groups.length; 56 | } 57 | -------------------------------------------------------------------------------- /macos/Flutter/GeneratedPluginRegistrant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | import FlutterMacOS 6 | import Foundation 7 | 8 | import appkit_ui_element_colors 9 | import cryptography_flutter_plus 10 | import dynamic_color 11 | import geolocator_apple 12 | import macos_ui 13 | import macos_window_utils 14 | import mobile_scanner 15 | import package_info_plus 16 | import path_provider_foundation 17 | import shared_preferences_foundation 18 | import url_launcher_macos 19 | 20 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 21 | AppkitUiElementColorsPlugin.register(with: registry.registrar(forPlugin: "AppkitUiElementColorsPlugin")) 22 | CryptographyFlutterPlugin.register(with: registry.registrar(forPlugin: "CryptographyFlutterPlugin")) 23 | DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin")) 24 | GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin")) 25 | MacOSUiPlugin.register(with: registry.registrar(forPlugin: "MacOSUiPlugin")) 26 | MacOSWindowUtilsPlugin.register(with: registry.registrar(forPlugin: "MacOSWindowUtilsPlugin")) 27 | MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin")) 28 | FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) 29 | PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) 30 | SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) 31 | UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) 32 | } 33 | -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "app_icon_16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "app_icon_32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "app_icon_32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "app_icon_64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "app_icon_128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "app_icon_256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "app_icon_256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "app_icon_512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "app_icon_512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "app_icon_1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: luogo 2 | description: "Secure and simple location sharing app" 3 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 4 | 5 | version: 0.4.1+11 6 | 7 | environment: 8 | sdk: ^3.6.2 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | s5_messenger: 14 | # path: ../s5-things/s5_messenger/ 15 | git: https://github.com/s5-dev/s5_messenger.git 16 | s5: 17 | # path: ../s5-things/s5-dart/ 18 | git: 19 | url: https://github.com/lukehmcc/s5-dart.git 20 | ref: 0.2.0-patched 21 | lib5: 22 | # path: ../s5-things/lib5/ 23 | git: 24 | url: https://github.com/lukehmcc/lib5.git 25 | ref: 0.2.0-patched 26 | cupertino_icons: ^1.0.8 27 | hive_ce: ^2.11.2 28 | logger: ^2.5.0 29 | adaptive_dialog: ^2.4.1 30 | latlong2: ^0.9.1 31 | flutter_map: ^8.1.1 32 | url_launcher: ^6.3.1 33 | path_provider: ^2.1.5 34 | flutter_map_pmtiles: ^1.0.5 35 | maplibre_gl: ^0.22.0 36 | flutter_bloc: ^9.1.1 37 | equatable: ^2.0.7 38 | path: ^1.9.1 39 | shared_preferences: ^2.5.3 40 | geolocator: ^14.0.1 41 | flutter_colorpicker: ^1.1.0 42 | image: ^4.3.0 43 | mobile_scanner: ^7.0.1 44 | qr_flutter: ^4.1.0 45 | msgpack_dart: ^1.0.1 46 | uuid: ^4.5.1 47 | intl: ^0.20.2 48 | background_fetch: ^1.4.0 49 | get_it: ^8.1.0 50 | flutter_svg: ^2.0.13 51 | package_info_plus: ^8.3.1 52 | 53 | dev_dependencies: 54 | flutter_test: 55 | sdk: flutter 56 | flutter_lints: ^5.0.0 57 | integration_test: 58 | sdk: flutter 59 | build_runner: ^2.5.1 60 | hive_ce_generator: 1.9.2 61 | flutter_launcher_icons: "^0.14.4" 62 | change_app_package_name: ^1.5.0 63 | 64 | flutter: 65 | uses-material-design: true 66 | assets: 67 | - assets/pmtiles_style.json 68 | - assets/pin.png 69 | - assets/fake-map.png 70 | -------------------------------------------------------------------------------- /lib/model/message_embed.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:flutter/painting.dart'; 4 | import 'package:maplibre_gl/maplibre_gl.dart'; 5 | import 'package:msgpack_dart/msgpack_dart.dart'; 6 | import 'package:shared_preferences/shared_preferences.dart'; 7 | 8 | /// A class representing an embeddable message with geographic coordinates and styling. 9 | class MessageEmbed { 10 | final LatLng coordinates; 11 | final String name; 12 | final Color color; 13 | final String? newGroupName; 14 | 15 | MessageEmbed( 16 | {required this.coordinates, 17 | required this.name, 18 | required this.color, 19 | this.newGroupName}); 20 | 21 | // Serialize to MessagePack (Uint8List) 22 | Uint8List toMsgpack() { 23 | return serialize({ 24 | 'coordinates': [coordinates.latitude, coordinates.longitude], 25 | 'name': name, 26 | 'color': color.toARGB32(), 27 | 'newGroupName': newGroupName, 28 | }); 29 | } 30 | 31 | // Deserialize from MessagePack (Uint8List) 32 | factory MessageEmbed.fromMsgpack(Uint8List msgpackBytes) { 33 | final Map data = deserialize(msgpackBytes); 34 | final List coords = data['coordinates']; 35 | 36 | return MessageEmbed( 37 | coordinates: LatLng(coords[0] as double, coords[1] as double), 38 | name: data['name'], 39 | color: Color(data['color'] ?? 0), 40 | newGroupName: data['newGroupName'], 41 | ); 42 | } 43 | 44 | // Helper constructor from preferences 45 | factory MessageEmbed.fromPrefs(LatLng coordinates, 46 | SharedPreferencesWithCache prefs, String? newGroupName) { 47 | return MessageEmbed( 48 | coordinates: coordinates, 49 | name: prefs.getString('name') ?? "", 50 | color: Color(prefs.getInt('color') ?? 0), 51 | newGroupName: newGroupName, 52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/cubit/home/settings/settings_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:luogo/cubit/home/settings/settings_state.dart'; 4 | import 'package:luogo/main.dart'; 5 | import 'package:package_info_plus/package_info_plus.dart'; 6 | import 'package:shared_preferences/shared_preferences.dart'; 7 | 8 | /// A Cubit class for managing the settings state. 9 | /// 10 | /// Example usage: 11 | /// ```dart 12 | /// BlocProvider( 13 | /// create: (context) =>; SettinsgCubit(), 14 | /// child: YourSettingsWidget(), 15 | /// ) 16 | /// ``` 17 | 18 | class SettingsCubit extends Cubit { 19 | SharedPreferencesWithCache prefs; 20 | SettingsCubit({ 21 | required this.prefs, 22 | }) : super(SettingsInitial()) { 23 | String? nodeString = prefs.getString('s5-node'); 24 | if (nodeString != null) { 25 | controller.text = nodeString; 26 | } 27 | PackageInfo.fromPlatform().then((PackageInfo packageInfo) { 28 | String v = packageInfo.version; 29 | String b = packageInfo.buildNumber; 30 | version = "version $v+$b"; 31 | emit(state); 32 | }); 33 | } 34 | TextEditingController controller = TextEditingController(); 35 | String version = ""; 36 | 37 | void setS5Node() { 38 | if (_validateUrl(controller.text)) { 39 | logger.d("valid Url time"); 40 | prefs.setString('s5-node', controller.text); 41 | emit(SettingsNewNodeSucsess()); 42 | } else { 43 | logger.d("invald url"); 44 | emit(SettingsNewNodeError()); 45 | } 46 | } 47 | 48 | bool _validateUrl(String url) { 49 | if (url.isEmpty) return true; // Allow empty (if needed) 50 | final Uri? uri = Uri.tryParse(url); 51 | return uri != null && 52 | (uri.scheme == 'http' || uri.scheme == 'https' || uri.scheme == 'wss'); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/cubit/init_router/init_router_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_bloc/flutter_bloc.dart'; 2 | import 'package:luogo/cubit/init_router/init_router_state.dart'; 3 | import 'package:luogo/services/location_service.dart'; 4 | import 'package:shared_preferences/shared_preferences.dart'; 5 | 6 | /// A Cubit class for managing the initialization router state, checking if the user has been initialized via shared preferences. 7 | /// 8 | /// Example usage: 9 | /// ```dart 10 | /// BlocProvider( 11 | /// create: (context) => InitRouterCubit(prefs: yourSharedPreferencesInstance), 12 | /// child: YourInitRouterWidget(), 13 | /// ) 14 | /// ``` 15 | class InitRouterCubit extends Cubit { 16 | final SharedPreferencesWithCache prefs; 17 | final LocationService locationService; 18 | 19 | InitRouterCubit({ 20 | required this.prefs, 21 | required this.locationService, 22 | }) : super(InitRouterInitial()); 23 | 24 | Future getRoute() async { 25 | // Emit that it is loading 26 | emit(InitRouterLoading()); 27 | // This is here because this funciton is too fast, so the changes don't register 28 | // unless a frame is skipped 29 | await Future.delayed(Duration.zero); 30 | // Now check if prefs have been set 31 | final bool hasColor = prefs.containsKey('color'); 32 | final bool hasName = prefs.containsKey('name'); 33 | // Check if we have location perms 34 | bool locationPerms = await locationService.checkLocationPermissions(); 35 | bool locationPermsDeniedPersisted = 36 | prefs.getBool("location-perms-have-been-requested") ?? false; 37 | if (hasColor && hasName) { 38 | emit(InitRouterSuccess(route: RouteType.home)); 39 | } else if (!locationPerms && !locationPermsDeniedPersisted) { 40 | emit(InitRouterSuccess(route: RouteType.locationPerms)); 41 | } else { 42 | emit(InitRouterSuccess(route: RouteType.login)); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /windows/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(runner LANGUAGES CXX) 3 | 4 | # Define the application target. To change its name, change BINARY_NAME in the 5 | # top-level CMakeLists.txt, not the value here, or `flutter run` will no longer 6 | # work. 7 | # 8 | # Any new source files that you add to the application should be added here. 9 | add_executable(${BINARY_NAME} WIN32 10 | "flutter_window.cpp" 11 | "main.cpp" 12 | "utils.cpp" 13 | "win32_window.cpp" 14 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 15 | "Runner.rc" 16 | "runner.exe.manifest" 17 | ) 18 | 19 | # Apply the standard set of build settings. This can be removed for applications 20 | # that need different build settings. 21 | apply_standard_settings(${BINARY_NAME}) 22 | 23 | # Add preprocessor definitions for the build version. 24 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") 25 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") 26 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") 27 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") 28 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") 29 | 30 | # Disable Windows macros that collide with C++ standard library functions. 31 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") 32 | 33 | # Add dependency libraries and include directories. Add any application-specific 34 | # dependencies here. 35 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) 36 | target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") 37 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 38 | 39 | # Run the Flutter tool portions of the build. This must not be removed. 40 | add_dependencies(${BINARY_NAME} flutter_assemble) 41 | -------------------------------------------------------------------------------- /windows/runner/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void CreateAndAttachConsole() { 11 | if (::AllocConsole()) { 12 | FILE *unused; 13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) { 14 | _dup2(_fileno(stdout), 1); 15 | } 16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) { 17 | _dup2(_fileno(stdout), 2); 18 | } 19 | std::ios::sync_with_stdio(); 20 | FlutterDesktopResyncOutputStreams(); 21 | } 22 | } 23 | 24 | std::vector GetCommandLineArguments() { 25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. 26 | int argc; 27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); 28 | if (argv == nullptr) { 29 | return std::vector(); 30 | } 31 | 32 | std::vector command_line_arguments; 33 | 34 | // Skip the first argument as it's the binary name. 35 | for (int i = 1; i < argc; i++) { 36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i])); 37 | } 38 | 39 | ::LocalFree(argv); 40 | 41 | return command_line_arguments; 42 | } 43 | 44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) { 45 | if (utf16_string == nullptr) { 46 | return std::string(); 47 | } 48 | unsigned int target_length = ::WideCharToMultiByte( 49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 50 | -1, nullptr, 0, nullptr, nullptr) 51 | -1; // remove the trailing null character 52 | int input_length = (int)wcslen(utf16_string); 53 | std::string utf8_string; 54 | if (target_length == 0 || target_length > utf8_string.max_size()) { 55 | return utf8_string; 56 | } 57 | utf8_string.resize(target_length); 58 | int converted_length = ::WideCharToMultiByte( 59 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 60 | input_length, utf8_string.data(), target_length, nullptr, nullptr); 61 | if (converted_length == 0) { 62 | return std::string(); 63 | } 64 | return utf8_string; 65 | } 66 | -------------------------------------------------------------------------------- /lib/view/page/map/bottom_sheet_info_modal.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:luogo/model/user_state.dart'; 3 | import 'package:intl/intl.dart'; 4 | import 'package:luogo/view/widgets/circle_avatar_styled_named.dart'; 5 | 6 | /// A modal that displays user information when a symbol is clicked. 7 | /// 8 | /// This widget shows a circular avatar with the user's initial and their name. 9 | /// The avatar's color is derived from [userState.color], and the name is displayed 10 | /// adjacent to the avatar. 11 | /// 12 | /// **Example**: 13 | /// ```dart 14 | /// BottomSheetInfoModal( 15 | /// userState: UserState(...), 16 | /// isYou: false, 17 | /// ) 18 | /// ``` 19 | /// 20 | /// See also: 21 | /// - [UserState], the model providing user data. 22 | /// - [CircleAvatar], the widget used for the circular display. 23 | class BottomSheetInfoModal extends StatelessWidget { 24 | final UserState userState; 25 | final bool isYou; 26 | const BottomSheetInfoModal({ 27 | super.key, 28 | required this.userState, 29 | required this.isYou, 30 | }); 31 | @override 32 | Widget build(BuildContext context) { 33 | return SizedBox( 34 | height: 200, 35 | child: Column( 36 | mainAxisAlignment: MainAxisAlignment.center, 37 | children: [ 38 | Row( 39 | mainAxisAlignment: MainAxisAlignment.center, 40 | children: [ 41 | CircleAvatarStyledNamed( 42 | name: userState.name, 43 | color: Color(userState.color), 44 | ), 45 | SizedBox( 46 | width: 10, 47 | ), 48 | Text( 49 | "${userState.name} ${(isYou) ? "(you)" : ""}", 50 | style: TextStyle(fontSize: 20), 51 | ), 52 | ], 53 | ), 54 | SizedBox( 55 | height: 10, 56 | ), 57 | Text( 58 | "Last seen: ${DateFormat('EEE, HH:mm').format(DateTime.fromMillisecondsSinceEpoch(userState.ts))}"), 59 | ], 60 | ), 61 | ); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /.github/workflows/build_on_tag.yaml: -------------------------------------------------------------------------------- 1 | name: Release and build on tag 2 | 3 | on: 4 | push: 5 | tags: 6 | - v* 7 | 8 | jobs: 9 | build-ubuntu: 10 | name: "Build android" 11 | runs-on: ubuntu-latest 12 | permissions: 13 | contents: write 14 | steps: 15 | # Setup Repo 16 | - name: Checkout 17 | uses: actions/checkout@v4 18 | 19 | # Create tag based changelog 20 | - name: Create changelog text 21 | id: changelog 22 | uses: loopwerk/tag-changelog@v1 23 | with: 24 | token: ${{ secrets.GITHUB_TOKEN }} 25 | exclude_types: other,ci 26 | 27 | # Get the dependencies going 28 | - name: Set up Flutter 29 | uses: subosito/flutter-action@v2 30 | with: 31 | channel: stable 32 | - name: Set up Java 33 | uses: actions/setup-java@v4 34 | with: 35 | java-version: 17 36 | distribution: "temurin" 37 | 38 | # And begin the flutter 39 | - name: Install dependencies 40 | run: ./flutterw pub get 41 | 42 | # Bulid android SIGNED 43 | - name: 🔧 Build APK 44 | env: 45 | KEY_JKS: ${{ secrets.KEY_JKS }} 46 | KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} 47 | ALIAS_PASSWORD: ${{ secrets.KEY_PASSWORD }} 48 | run: | 49 | base64 -d <<< $KEY_JKS > ./android/app/release-key.jks 50 | ./flutterw build apk --flavor prod --release -v --split-per-abi 51 | mkdir -p ./outputs 52 | cp build/app/outputs/apk/prod/release/*.apk ./outputs/ 53 | 54 | # Create release 55 | - name: Create Release 56 | uses: actions/create-release@v1 57 | env: 58 | GITHUB_TOKEN: ${{ secrets.github_token }} 59 | with: 60 | tag_name: ${{ github.ref }} 61 | release_name: Release ${{ github.ref }} 62 | body: ${{ steps.changelog.outputs.changes }} 63 | 64 | - name: Debug output 65 | run: | 66 | ls 67 | ls outputs 68 | 69 | # Now upload those assets 70 | - name: Upload release assets 71 | uses: alexellis/upload-assets@0.4.1 72 | env: 73 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 74 | with: 75 | asset_paths: '["./outputs/*"]' 76 | -------------------------------------------------------------------------------- /lib/model/group_settings.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:luogo/main.dart'; 4 | import 'package:shared_preferences/shared_preferences.dart'; 5 | 6 | /// A class for managing group-specific settings that are persisted in SharedPreferences. 7 | /// 8 | /// This class handles the serialization and deserialization of group settings, 9 | /// including location sharing preferences. 10 | /// 11 | /// Example usage: 12 | /// ```dart 13 | /// // Load settings for a group 14 | /// final settings = GroupSettings.load(group, prefs); 15 | /// 16 | /// // Update and save settings 17 | /// settings.shareLocation = false; 18 | /// await GroupSettings.save(prefs, settings); 19 | /// 20 | /// // Create new settings 21 | /// final newSettings = GroupSettings( 22 | /// groupId: groupId;, 23 | /// shareLocation: true, 24 | /// ); 25 | /// ``` 26 | class GroupSettings { 27 | final String groupId; 28 | bool shareLocation; 29 | 30 | GroupSettings({ 31 | required this.groupId, 32 | required this.shareLocation, 33 | }); 34 | 35 | // Convert to JSON for storage 36 | Map toJson() => { 37 | 'shareLocation': shareLocation, 38 | }; 39 | 40 | // Create from JSON 41 | factory GroupSettings.fromJson(String groupId, Map json) => 42 | GroupSettings( 43 | groupId: groupId, 44 | shareLocation: json['shareLocation'] ?? false, 45 | ); 46 | 47 | // read 48 | // If not set, it defaults to true as this is a location sharing app 49 | static GroupSettings load(String groupId, SharedPreferencesWithCache prefs) { 50 | final String? jsonString = prefs.getString(groupId); 51 | if (jsonString == null) { 52 | return GroupSettings( 53 | groupId: groupId, shareLocation: true); // No saved settings 54 | } 55 | 56 | try { 57 | return GroupSettings.fromJson( 58 | groupId, 59 | jsonDecode(jsonString), // Convert JSON string to Map 60 | ); 61 | } catch (e) { 62 | logger.d('Error parsing settings: $e'); 63 | return GroupSettings(groupId: groupId, shareLocation: true); 64 | } 65 | } 66 | 67 | // write 68 | static Future save( 69 | SharedPreferencesWithCache prefs, 70 | GroupSettings settings, 71 | ) async { 72 | await prefs.setString( 73 | settings.groupId, 74 | jsonEncode(settings.toJson()), 75 | ); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.cpp: -------------------------------------------------------------------------------- 1 | #include "flutter_window.h" 2 | 3 | #include 4 | 5 | #include "flutter/generated_plugin_registrant.h" 6 | 7 | FlutterWindow::FlutterWindow(const flutter::DartProject& project) 8 | : project_(project) {} 9 | 10 | FlutterWindow::~FlutterWindow() {} 11 | 12 | bool FlutterWindow::OnCreate() { 13 | if (!Win32Window::OnCreate()) { 14 | return false; 15 | } 16 | 17 | RECT frame = GetClientArea(); 18 | 19 | // The size here must match the window dimensions to avoid unnecessary surface 20 | // creation / destruction in the startup path. 21 | flutter_controller_ = std::make_unique( 22 | frame.right - frame.left, frame.bottom - frame.top, project_); 23 | // Ensure that basic setup of the controller was successful. 24 | if (!flutter_controller_->engine() || !flutter_controller_->view()) { 25 | return false; 26 | } 27 | RegisterPlugins(flutter_controller_->engine()); 28 | SetChildContent(flutter_controller_->view()->GetNativeWindow()); 29 | 30 | flutter_controller_->engine()->SetNextFrameCallback([&]() { 31 | this->Show(); 32 | }); 33 | 34 | // Flutter can complete the first frame before the "show window" callback is 35 | // registered. The following call ensures a frame is pending to ensure the 36 | // window is shown. It is a no-op if the first frame hasn't completed yet. 37 | flutter_controller_->ForceRedraw(); 38 | 39 | return true; 40 | } 41 | 42 | void FlutterWindow::OnDestroy() { 43 | if (flutter_controller_) { 44 | flutter_controller_ = nullptr; 45 | } 46 | 47 | Win32Window::OnDestroy(); 48 | } 49 | 50 | LRESULT 51 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message, 52 | WPARAM const wparam, 53 | LPARAM const lparam) noexcept { 54 | // Give Flutter, including plugins, an opportunity to handle window messages. 55 | if (flutter_controller_) { 56 | std::optional result = 57 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, 58 | lparam); 59 | if (result) { 60 | return *result; 61 | } 62 | } 63 | 64 | switch (message) { 65 | case WM_FONTCHANGE: 66 | flutter_controller_->engine()->ReloadSystemFonts(); 67 | break; 68 | } 69 | 70 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam); 71 | } 72 | -------------------------------------------------------------------------------- /lib/cubit/create_profile/create_profile_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_bloc/flutter_bloc.dart'; 5 | import 'package:luogo/cubit/create_profile/create_profile_state.dart'; 6 | import 'package:shared_preferences/shared_preferences.dart'; 7 | 8 | // Generates a random color so if the user doesn't pick it isn't always the same 9 | Color getRandomLightColor() { 10 | final random = Random(); 11 | final channels = [ 12 | 100 + random.nextInt(56), 13 | 100 + random.nextInt(56), 14 | 100 + random.nextInt(56) 15 | ]; 16 | 17 | // Select one channel to boost 18 | final biasedChannel = random.nextInt(3); 19 | channels[biasedChannel] = 20 | 200 + random.nextInt(56); // 200-255 for the biased channel 21 | 22 | return Color.fromRGBO(channels[0], channels[1], channels[2], 1.0); 23 | } 24 | 25 | /// A Cubit class for managing the state of a profile creation page. 26 | /// 27 | /// Example usage: 28 | /// ```dart 29 | /// BlocProvider( 30 | /// create: (context) => CreateProfilePageCubit(prefs: prefs), 31 | /// child: CreateProfilePage(), 32 | /// ) 33 | /// ``` 34 | class CreateProfilePageCubit extends Cubit { 35 | final SharedPreferencesWithCache prefs; 36 | 37 | CreateProfilePageCubit({required this.prefs}) 38 | : super(CreateProfileInitial()) { 39 | selectedColor = getRandomLightColor(); 40 | pickerColor = selectedColor; 41 | nameController.addListener(() { 42 | emit(CreateProfileTextEdited()); 43 | }); 44 | } 45 | 46 | final TextEditingController nameController = TextEditingController(); 47 | Color? selectedColor; 48 | Color? pickerColor; 49 | 50 | // Callback for when color is updated from the picker 51 | void onColorPicked(Color color) { 52 | selectedColor = color; 53 | emit(CreateProfileColorChanged()); 54 | } 55 | 56 | // If everything is okay, save the settings to prefs so you can move on 57 | Future savePreferences() async { 58 | if (nameController.text.isEmpty || selectedColor == null) { 59 | emit(CreateProfileError('Please enter both name and color')); 60 | return; 61 | } 62 | 63 | try { 64 | await prefs.setString('name', nameController.text); 65 | await prefs.setInt('color', selectedColor?.toARGB32() ?? 0); 66 | emit(CreateProfileSuccess()); 67 | } catch (e) { 68 | emit(CreateProfileError(e.toString())); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /lib/hive/hive_adapters.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'hive_adapters.dart'; 4 | 5 | // ************************************************************************** 6 | // AdaptersGenerator 7 | // ************************************************************************** 8 | 9 | class HiveLatLngAdapter extends TypeAdapter { 10 | @override 11 | final typeId = 0; 12 | 13 | @override 14 | HiveLatLng read(BinaryReader reader) { 15 | final numOfFields = reader.readByte(); 16 | final fields = { 17 | for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), 18 | }; 19 | return HiveLatLng( 20 | lat: (fields[0] as num).toDouble(), 21 | long: (fields[1] as num).toDouble(), 22 | ); 23 | } 24 | 25 | @override 26 | void write(BinaryWriter writer, HiveLatLng obj) { 27 | writer 28 | ..writeByte(2) 29 | ..writeByte(0) 30 | ..write(obj.lat) 31 | ..writeByte(1) 32 | ..write(obj.long); 33 | } 34 | 35 | @override 36 | int get hashCode => typeId.hashCode; 37 | 38 | @override 39 | bool operator ==(Object other) => 40 | identical(this, other) || 41 | other is HiveLatLngAdapter && 42 | runtimeType == other.runtimeType && 43 | typeId == other.typeId; 44 | } 45 | 46 | class UserStateAdapter extends TypeAdapter { 47 | @override 48 | final typeId = 1; 49 | 50 | @override 51 | UserState read(BinaryReader reader) { 52 | final numOfFields = reader.readByte(); 53 | final fields = { 54 | for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), 55 | }; 56 | return UserState( 57 | coords: fields[0] as HiveLatLng, 58 | ts: (fields[1] as num).toInt(), 59 | name: fields[2] as String, 60 | color: (fields[3] as num).toInt(), 61 | ); 62 | } 63 | 64 | @override 65 | void write(BinaryWriter writer, UserState obj) { 66 | writer 67 | ..writeByte(4) 68 | ..writeByte(0) 69 | ..write(obj.coords) 70 | ..writeByte(1) 71 | ..write(obj.ts) 72 | ..writeByte(2) 73 | ..write(obj.name) 74 | ..writeByte(3) 75 | ..write(obj.color); 76 | } 77 | 78 | @override 79 | int get hashCode => typeId.hashCode; 80 | 81 | @override 82 | bool operator ==(Object other) => 83 | identical(this, other) || 84 | other is UserStateAdapter && 85 | runtimeType == other.runtimeType && 86 | typeId == other.typeId; 87 | } 88 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | import java.util.Properties 2 | import java.io.FileInputStream 3 | 4 | plugins { 5 | id "com.android.application" 6 | id "kotlin-android" 7 | // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. 8 | id "dev.flutter.flutter-gradle-plugin" 9 | } 10 | 11 | def keystoreProperties = new Properties() 12 | def keystorePropertiesFile = rootProject.file('key.properties') 13 | if (keystorePropertiesFile.exists()) { 14 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 15 | } else { 16 | // If the file does not exist, use environment variables (probably in a gh runner) 17 | keystoreProperties['storeFile'] = 'release-key.jks' ?: "" 18 | keystoreProperties['storePassword'] = System.getenv('KEY_PASSWORD') ?: "" 19 | keystoreProperties['keyAlias'] = 'uploadod' ?: "" 20 | keystoreProperties['keyPassword'] = System.getenv('KEY_PASSWORD') ?: "" 21 | } 22 | 23 | 24 | android { 25 | namespace = "app.luogo.app" 26 | compileSdk = 36 27 | ndkVersion = "28.1.13356709" // For maplibre_gl 28 | 29 | compileOptions { 30 | sourceCompatibility = JavaVersion.VERSION_1_8 31 | targetCompatibility = JavaVersion.VERSION_1_8 32 | } 33 | 34 | kotlinOptions { 35 | jvmTarget = JavaVersion.VERSION_1_8 36 | } 37 | 38 | defaultConfig { 39 | applicationId = "app.luogo.app" 40 | // You can update the following values to match your application needs. 41 | // For more information, see: https://flutter.dev/to/review-gradle-config. 42 | minSdk = flutter.minSdkVersion 43 | targetSdk = flutter.targetSdkVersion 44 | versionCode = flutter.versionCode 45 | versionName = flutter.versionName 46 | } 47 | 48 | signingConfigs { 49 | release { 50 | keyAlias keystoreProperties['keyAlias'] 51 | keyPassword keystoreProperties['keyPassword'] 52 | storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null 53 | storePassword keystoreProperties['storePassword'] 54 | } 55 | } 56 | 57 | buildTypes { 58 | release { 59 | signingConfig = signingConfigs.getByName("release") 60 | } 61 | } 62 | flavorDimensions "default" 63 | productFlavors { 64 | dev { 65 | dimension "default" 66 | resValue "string", "app_name", "Luogo (dev)" 67 | applicationIdSuffix ".dev" 68 | } 69 | prod { 70 | dimension "default" 71 | resValue "string", "app_name", "Luogo" 72 | applicationIdSuffix "" 73 | } 74 | } 75 | } 76 | 77 | flutter { 78 | source = "../.." 79 | } 80 | -------------------------------------------------------------------------------- /lib/view/page/map/invite_user_qr_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | import 'package:luogo/cubit/map/invite_user_qr/invite_user_qr_cubit.dart'; 5 | import 'package:luogo/cubit/map/invite_user_qr/invite_user_qr_state.dart'; 6 | import 'package:qr_flutter/qr_flutter.dart'; 7 | 8 | class InviteUserQrDialog extends StatelessWidget { 9 | // Explicityly defined here because popups have different context 10 | const InviteUserQrDialog({super.key}); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return BlocBuilder( 15 | builder: (BuildContext content, InviteUserQrState state) { 16 | final InviteUserQrCubit inviteUserQrCubit = 17 | BlocProvider.of(context); 18 | return AlertDialog( 19 | title: Center( 20 | child: Row( 21 | mainAxisAlignment: MainAxisAlignment.center, 22 | children: [ 23 | const Text("Invite Code"), 24 | Tooltip( 25 | message: "Scan this code on another device to pair.", 26 | triggerMode: TooltipTriggerMode.tap, 27 | child: IconButton( 28 | icon: Icon(Icons.help_outline), 29 | onPressed: () {}, 30 | ), 31 | ), 32 | ], 33 | ), 34 | ), 35 | content: SingleChildScrollView( 36 | child: Column( 37 | mainAxisSize: MainAxisSize.min, 38 | children: [ 39 | Column( 40 | children: [ 41 | Container( 42 | color: Colors.purple.shade50, 43 | width: MediaQuery.of(context).size.width * .7, 44 | height: MediaQuery.of(context).size.width * .7, 45 | child: QrImageView( 46 | data: inviteUserQrCubit.luogoInviteToken, 47 | ), 48 | ), 49 | ElevatedButton( 50 | onPressed: () => Clipboard.setData( 51 | ClipboardData( 52 | text: inviteUserQrCubit.luogoInviteToken, 53 | ), 54 | ), 55 | child: Text("Copy Invite")), 56 | ], 57 | ), 58 | ], 59 | ), 60 | ), 61 | actions: [ 62 | Center( 63 | child: TextButton( 64 | onPressed: () => Navigator.pop(context), 65 | child: Text("Close"), 66 | ), 67 | ) 68 | ], 69 | ); 70 | }); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 16 | 20 | 24 | 25 | 26 | 27 | 28 | 29 | 31 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /linux/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file controls Flutter-level build steps. It should not be edited. 2 | cmake_minimum_required(VERSION 3.10) 3 | 4 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 5 | 6 | # Configuration provided via flutter tool. 7 | include(${EPHEMERAL_DIR}/generated_config.cmake) 8 | 9 | # TODO: Move the rest of this into files in ephemeral. See 10 | # https://github.com/flutter/flutter/issues/57146. 11 | 12 | # Serves the same purpose as list(TRANSFORM ... PREPEND ...), 13 | # which isn't available in 3.10. 14 | function(list_prepend LIST_NAME PREFIX) 15 | set(NEW_LIST "") 16 | foreach(element ${${LIST_NAME}}) 17 | list(APPEND NEW_LIST "${PREFIX}${element}") 18 | endforeach(element) 19 | set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) 20 | endfunction() 21 | 22 | # === Flutter Library === 23 | # System-level dependencies. 24 | find_package(PkgConfig REQUIRED) 25 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 26 | pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) 27 | pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) 28 | 29 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") 30 | 31 | # Published to parent scope for install step. 32 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 33 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 34 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 35 | set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) 36 | 37 | list(APPEND FLUTTER_LIBRARY_HEADERS 38 | "fl_basic_message_channel.h" 39 | "fl_binary_codec.h" 40 | "fl_binary_messenger.h" 41 | "fl_dart_project.h" 42 | "fl_engine.h" 43 | "fl_json_message_codec.h" 44 | "fl_json_method_codec.h" 45 | "fl_message_codec.h" 46 | "fl_method_call.h" 47 | "fl_method_channel.h" 48 | "fl_method_codec.h" 49 | "fl_method_response.h" 50 | "fl_plugin_registrar.h" 51 | "fl_plugin_registry.h" 52 | "fl_standard_message_codec.h" 53 | "fl_standard_method_codec.h" 54 | "fl_string_codec.h" 55 | "fl_value.h" 56 | "fl_view.h" 57 | "flutter_linux.h" 58 | ) 59 | list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") 60 | add_library(flutter INTERFACE) 61 | target_include_directories(flutter INTERFACE 62 | "${EPHEMERAL_DIR}" 63 | ) 64 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") 65 | target_link_libraries(flutter INTERFACE 66 | PkgConfig::GTK 67 | PkgConfig::GLIB 68 | PkgConfig::GIO 69 | ) 70 | add_dependencies(flutter flutter_assemble) 71 | 72 | # === Flutter tool backend === 73 | # _phony_ is a non-existent file to force this command to run every time, 74 | # since currently there's no way to get a full input/output list from the 75 | # flutter tool. 76 | add_custom_command( 77 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 78 | ${CMAKE_CURRENT_BINARY_DIR}/_phony_ 79 | COMMAND ${CMAKE_COMMAND} -E env 80 | ${FLUTTER_TOOL_ENVIRONMENT} 81 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" 82 | ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} 83 | VERBATIM 84 | ) 85 | add_custom_target(flutter_assemble DEPENDS 86 | "${FLUTTER_LIBRARY}" 87 | ${FLUTTER_LIBRARY_HEADERS} 88 | ) 89 | -------------------------------------------------------------------------------- /lib/cubit/map/key_pair_qr/keypair_qr_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | import 'dart:typed_data'; 3 | 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_bloc/flutter_bloc.dart'; 6 | import 'package:lib5/util.dart'; 7 | import 'package:luogo/cubit/map/key_pair_qr/keypair_qr_state.dart'; 8 | import 'package:luogo/main.dart'; 9 | import 'package:luogo/model/group_info.dart'; 10 | import 'package:luogo/model/message_embed.dart'; 11 | import 'package:luogo/services/location_service.dart'; 12 | import 'package:maplibre_gl/maplibre_gl.dart'; 13 | import 'package:s5_messenger/s5_messenger.dart'; 14 | import 'package:shared_preferences/shared_preferences.dart'; 15 | import 'package:uuid/uuid.dart'; 16 | 17 | /// A Cubit class for managing the keypair QR state. 18 | /// 19 | /// Example usage: 20 | /// ```dart 21 | /// BlocProvider( 22 | /// create: (context) => KeypairQRCubit( 23 | /// s5messenger: yourS5MessengerInstance, 24 | /// locationService: yourLocationServiceInstance, 25 | /// ), 26 | /// child: YourKeypairQRWidget(), 27 | /// ) 28 | /// ``` 29 | class KeypairQRCubit extends Cubit { 30 | final S5Messenger s5messenger; 31 | final LocationService locationService; 32 | final SharedPreferencesWithCache prefs; 33 | KeypairQRCubit({ 34 | required this.s5messenger, 35 | required this.locationService, 36 | required this.prefs, 37 | }) : super(KeypairQRInitial()); 38 | 39 | TextEditingController textController = TextEditingController(); 40 | 41 | // Once the welcome message has been generated on the other client you 42 | // can then scan it to join the group. 43 | Future handleQRWelcomeMessage(String welcomeMessage) async { 44 | log(welcomeMessage); 45 | 46 | if (!welcomeMessage.startsWith('luogo-invite-key: ')) { 47 | throw 'Incorrect group invite'; 48 | } 49 | 50 | String myID = (s5messenger.dataBox.get('identity_default') 51 | as Map)['publicKey']; 52 | 53 | // Grab the locaiton and encode it to bytes 54 | final LatLng? loc = 55 | locationService.locationBox.get('local_position')?.toLatLng(); 56 | final Uint8List? messageEmbedBytes = (loc != null) 57 | ? MessageEmbed.fromPrefs(loc, prefs, null).toMsgpack() 58 | : null; 59 | 60 | // Accept the invite and send along the location so the other person knows 61 | // where you are immediately 62 | final groupId = await s5messenger.acceptInviteAndJoinGroupExternalCommit( 63 | base64UrlNoPaddingDecode( 64 | welcomeMessage.substring(18), 65 | ), 66 | myID, 67 | Uuid().v4(), 68 | messageEmbedBytes, 69 | ); 70 | 71 | // Now make sure to set the group and update UI 72 | try { 73 | s5messenger.messengerState.groupId = groupId; 74 | s5messenger.messengerState.update(); 75 | final GroupInfoList groups = 76 | GroupInfo.fromJsonList(s5messenger.groupsBox.values.toList()); 77 | final GroupInfo? group = groups.findByID(groupId); 78 | emit(KeyPairQrGroupLoaded(groups, group)); // Use same state 79 | } catch (e) { 80 | logger.e(e); 81 | emit(KeyPairQrGroupError(e.toString())); 82 | } 83 | 84 | // refresh the pins now that a new member is added 85 | locationService.setupListenToPeer(s5messenger.group(groupId)); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /lib/utils/mapping.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart'; 2 | import 'package:maplibre_gl/maplibre_gl.dart'; 3 | import 'package:image/image.dart' as img; 4 | 5 | /// Adds an image from an asset to the current map style with a colored circle and letter overlay. 6 | /// 7 | /// This function: 8 | /// - Loads an image from the specified asset 9 | /// - Draws a colored circle at a fixed position on the image 10 | /// - Adds a letter overlay in the center 11 | /// - Adds the final composited image to the map style 12 | /// 13 | /// Parameters: 14 | /// - `controller`: The [MapLibreMapController] instance used to add the image to the map 15 | /// - `name`: The unique identifier for this image in the map style 16 | /// - `assetName`: The path to the asset image (e.g., 'assets/images/marker.png') 17 | /// - `color`: The [Color] of the circle to be drawn 18 | /// - `letter`: The single character to be drawn on top of the circle 19 | /// 20 | /// Returns: 21 | /// A [Future] that completes when the image has been added to the map style 22 | Future addImageFromAsset(MapLibreMapController controller, String name, 23 | String assetName, Color color, String letter) async { 24 | final Uint8List bytes = 25 | await addColoredCircleAndLetterToImage(assetName, color, letter); 26 | final Uint8List list = bytes.buffer.asUint8List(); 27 | return controller.addImage(name, list); 28 | } 29 | 30 | /// Creates a composite image by adding a colored circle and letter to an asset image. 31 | /// 32 | /// This helper function: 33 | /// 1. Loads and decodes the original image from assets 34 | /// 2. Draws a colored circle at fixed coordinates 35 | /// 3. Creates a separate image with the provided letter 36 | /// 4. Composites both images together 37 | /// 5. Encodes the result as PNG 38 | /// 39 | /// Parameters: 40 | /// - `assetName`: The path to the asset image (e.g., 'assets/images/marker.png') 41 | /// - `color`: The [Color] of the circle to be drawn 42 | /// - `letter`: The single character to be drawn on top of the circle 43 | /// 44 | /// Returns: 45 | /// A [Future] containing the PNG-encoded bytes of the composited image 46 | Future addColoredCircleAndLetterToImage( 47 | String assetName, Color color, String letter) async { 48 | // 1. Load and decode the original image 49 | final bytes = await rootBundle.load(assetName); 50 | final image = img.decodeImage(bytes.buffer.asUint8List())!; 51 | 52 | // 2. Draw a colored circle (x, y, radius, color) 53 | final circleColor = img.ColorRgb8( 54 | (color.r * 255).toInt(), 55 | (color.g * 255).toInt(), 56 | (color.b * 255).toInt()); // Blue (R=0, G=0, B=255) 57 | img.fillCircle( 58 | image, 59 | x: 100, // Center X 60 | y: 84, // Center Y 61 | radius: 67, 62 | color: circleColor, 63 | ); 64 | final letterImage = img.Image(width: 100, height: 100, numChannels: 4); 65 | img.fill(letterImage, color: img.ColorRgba8(0, 0, 0, 0)); 66 | 67 | img.drawString( 68 | letterImage, 69 | letter[0].toUpperCase(), 70 | font: img.arial48, 71 | y: 22, 72 | color: img.ColorRgb8(0, 0, 0), 73 | ); 74 | 75 | // Now scale it up 76 | final img.Image scaledLetter = 77 | img.copyResize(letterImage, width: 200, height: 200); 78 | 79 | // Paste onto your main image 80 | img.compositeImage(image, scaledLetter); 81 | // 3. Encode back to PNG (or JPEG/WebP) 82 | return img.encodePng(image); 83 | } 84 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Luogo 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | s5_messenger_example 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | 32 | 33 | NSCameraUsageDescription 34 | Camera permission is required for scanning QR codes to pair phones. Paring is required to join groups and share location. 35 | NSPhotoLibraryUsageDescription 36 | This app needs photos access to get QR code from photo library. If a user gets texted a QR code for example, being able to pull it from the library is helpful. 37 | UIBackgroundModes 38 | 39 | processing 40 | fetch 41 | location 42 | 43 | NSLocationAlwaysUsageDescription 44 | Since this is a location sharing app, needed to access location always when the app is open. This allows the app to automatically share location with users it has decided to share it's location with. 45 | NSLocationWhenInUseUsageDescription 46 | Since this is a location sharing app, needs to access location when the app is open. This allows the app to automatically keep others informed of location. 47 | NSLocationAlwaysAndWhenInUseUsageDescription 48 | Since this is a location sharing app, needs to access location when the app is open and run in background. This allows the app to automatically keep others informed of location. 49 | BGTaskSchedulerPermittedIdentifiers 50 | 51 | com.transistorsoft.fetch 52 | 53 | ITSAppUsesNonExemptEncryption 54 | 55 | 56 | 57 | UISupportedInterfaceOrientations 58 | 59 | UIInterfaceOrientationPortrait 60 | UIInterfaceOrientationLandscapeLeft 61 | UIInterfaceOrientationLandscapeRight 62 | 63 | UISupportedInterfaceOrientations~ipad 64 | 65 | UIInterfaceOrientationPortrait 66 | UIInterfaceOrientationPortraitUpsideDown 67 | UIInterfaceOrientationLandscapeLeft 68 | UIInterfaceOrientationLandscapeRight 69 | 70 | CADisableMinimumFrameDurationOnPhone 71 | 72 | UIApplicationSupportsIndirectInputEvents 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - background_fetch (1.4.0): 3 | - Flutter 4 | - cryptography_flutter_plus (0.2.0): 5 | - Flutter 6 | - Flutter (1.0.0) 7 | - geolocator_apple (1.2.0): 8 | - Flutter 9 | - FlutterMacOS 10 | - integration_test (0.0.1): 11 | - Flutter 12 | - MapLibre (6.14.0) 13 | - maplibre_gl (0.0.1): 14 | - Flutter 15 | - MapLibre (= 6.14.0) 16 | - mobile_scanner (7.0.0): 17 | - Flutter 18 | - FlutterMacOS 19 | - package_info_plus (0.4.5): 20 | - Flutter 21 | - path_provider_foundation (0.0.1): 22 | - Flutter 23 | - FlutterMacOS 24 | - s5_messenger (0.1.0): 25 | - Flutter 26 | - shared_preferences_foundation (0.0.1): 27 | - Flutter 28 | - FlutterMacOS 29 | - url_launcher_ios (0.0.1): 30 | - Flutter 31 | 32 | DEPENDENCIES: 33 | - background_fetch (from `.symlinks/plugins/background_fetch/ios`) 34 | - cryptography_flutter_plus (from `.symlinks/plugins/cryptography_flutter_plus/ios`) 35 | - Flutter (from `Flutter`) 36 | - geolocator_apple (from `.symlinks/plugins/geolocator_apple/darwin`) 37 | - integration_test (from `.symlinks/plugins/integration_test/ios`) 38 | - maplibre_gl (from `.symlinks/plugins/maplibre_gl/ios`) 39 | - mobile_scanner (from `.symlinks/plugins/mobile_scanner/darwin`) 40 | - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) 41 | - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) 42 | - s5_messenger (from `.symlinks/plugins/s5_messenger/ios`) 43 | - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) 44 | - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) 45 | 46 | SPEC REPOS: 47 | trunk: 48 | - MapLibre 49 | 50 | EXTERNAL SOURCES: 51 | background_fetch: 52 | :path: ".symlinks/plugins/background_fetch/ios" 53 | cryptography_flutter_plus: 54 | :path: ".symlinks/plugins/cryptography_flutter_plus/ios" 55 | Flutter: 56 | :path: Flutter 57 | geolocator_apple: 58 | :path: ".symlinks/plugins/geolocator_apple/darwin" 59 | integration_test: 60 | :path: ".symlinks/plugins/integration_test/ios" 61 | maplibre_gl: 62 | :path: ".symlinks/plugins/maplibre_gl/ios" 63 | mobile_scanner: 64 | :path: ".symlinks/plugins/mobile_scanner/darwin" 65 | package_info_plus: 66 | :path: ".symlinks/plugins/package_info_plus/ios" 67 | path_provider_foundation: 68 | :path: ".symlinks/plugins/path_provider_foundation/darwin" 69 | s5_messenger: 70 | :path: ".symlinks/plugins/s5_messenger/ios" 71 | shared_preferences_foundation: 72 | :path: ".symlinks/plugins/shared_preferences_foundation/darwin" 73 | url_launcher_ios: 74 | :path: ".symlinks/plugins/url_launcher_ios/ios" 75 | 76 | SPEC CHECKSUMS: 77 | background_fetch: 81d0f20490f0126a1c29709e458f47a69e80f44e 78 | cryptography_flutter_plus: 44f4e9e4079395fcbb3e7809c0ac2c6ae2d9576f 79 | Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 80 | geolocator_apple: ab36aa0e8b7d7a2d7639b3b4e48308394e8cef5e 81 | integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e 82 | MapLibre: 69e572367f4ef6287e18246cfafc39c80cdcabcd 83 | maplibre_gl: 3c924e44725147b03dda33430ad216005b40555f 84 | mobile_scanner: 9157936403f5a0644ca3779a38ff8404c5434a93 85 | package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499 86 | path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 87 | s5_messenger: acaaaf4d76e8f2f97499576bf08e3e367c71fca4 88 | shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7 89 | url_launcher_ios: 694010445543906933d732453a59da0a173ae33d 90 | 91 | PODFILE CHECKSUM: a57f30d18f102dd3ce366b1d62a55ecbef2158e5 92 | 93 | COCOAPODS: 1.16.2 94 | -------------------------------------------------------------------------------- /windows/runner/Runner.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #pragma code_page(65001) 4 | #include "resource.h" 5 | 6 | #define APSTUDIO_READONLY_SYMBOLS 7 | ///////////////////////////////////////////////////////////////////////////// 8 | // 9 | // Generated from the TEXTINCLUDE 2 resource. 10 | // 11 | #include "winres.h" 12 | 13 | ///////////////////////////////////////////////////////////////////////////// 14 | #undef APSTUDIO_READONLY_SYMBOLS 15 | 16 | ///////////////////////////////////////////////////////////////////////////// 17 | // English (United States) resources 18 | 19 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 20 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Icon 51 | // 52 | 53 | // Icon with lowest ID value placed first to ensure application icon 54 | // remains consistent on all systems. 55 | IDI_APP_ICON ICON "resources\\app_icon.ico" 56 | 57 | 58 | ///////////////////////////////////////////////////////////////////////////// 59 | // 60 | // Version 61 | // 62 | 63 | #if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) 64 | #define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD 65 | #else 66 | #define VERSION_AS_NUMBER 1,0,0,0 67 | #endif 68 | 69 | #if defined(FLUTTER_VERSION) 70 | #define VERSION_AS_STRING FLUTTER_VERSION 71 | #else 72 | #define VERSION_AS_STRING "1.0.0" 73 | #endif 74 | 75 | VS_VERSION_INFO VERSIONINFO 76 | FILEVERSION VERSION_AS_NUMBER 77 | PRODUCTVERSION VERSION_AS_NUMBER 78 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 79 | #ifdef _DEBUG 80 | FILEFLAGS VS_FF_DEBUG 81 | #else 82 | FILEFLAGS 0x0L 83 | #endif 84 | FILEOS VOS__WINDOWS32 85 | FILETYPE VFT_APP 86 | FILESUBTYPE 0x0L 87 | BEGIN 88 | BLOCK "StringFileInfo" 89 | BEGIN 90 | BLOCK "040904e4" 91 | BEGIN 92 | VALUE "CompanyName", "com.example" "\0" 93 | VALUE "FileDescription", "s5_messenger_example" "\0" 94 | VALUE "FileVersion", VERSION_AS_STRING "\0" 95 | VALUE "InternalName", "s5_messenger_example" "\0" 96 | VALUE "LegalCopyright", "Copyright (C) 2025 com.example. All rights reserved." "\0" 97 | VALUE "OriginalFilename", "s5_messenger_example.exe" "\0" 98 | VALUE "ProductName", "s5_messenger_example" "\0" 99 | VALUE "ProductVersion", VERSION_AS_STRING "\0" 100 | END 101 | END 102 | BLOCK "VarFileInfo" 103 | BEGIN 104 | VALUE "Translation", 0x409, 1252 105 | END 106 | END 107 | 108 | #endif // English (United States) resources 109 | ///////////////////////////////////////////////////////////////////////////// 110 | 111 | 112 | 113 | #ifndef APSTUDIO_INVOKED 114 | ///////////////////////////////////////////////////////////////////////////// 115 | // 116 | // Generated from the TEXTINCLUDE 3 resource. 117 | // 118 | 119 | 120 | ///////////////////////////////////////////////////////////////////////////// 121 | #endif // not APSTUDIO_INVOKED 122 | -------------------------------------------------------------------------------- /lib/cubit/main/main_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | import 'package:get_it/get_it.dart'; 5 | import 'package:hive_ce/hive.dart'; 6 | import 'package:luogo/cubit/main/main_state.dart'; 7 | import 'package:luogo/hive/hive_registrar.g.dart'; 8 | import 'package:luogo/main.dart'; 9 | import 'package:luogo/services/location_service.dart'; 10 | import 'package:luogo/utils/s5_logger.dart'; 11 | import 'package:path_provider/path_provider.dart'; 12 | import 'package:s5/s5.dart'; 13 | import 'package:s5_messenger/s5_messenger.dart'; 14 | import 'package:shared_preferences/shared_preferences.dart'; 15 | import 'package:path/path.dart' as path; 16 | 17 | /// A Cubit class for managing the main application state. 18 | /// 19 | /// It initializes dependencies like S5, S5Messenger, shared 20 | /// preferences, and location services. 21 | /// 22 | /// Example usage: 23 | /// ```dart 24 | /// BlocProvider( 25 | /// create: (context) => MainCubit(), 26 | /// child: YourMainWidget(), 27 | /// ) 28 | /// ``` 29 | class MainCubit extends Cubit { 30 | MainCubit() : super(MainStateInitial()); 31 | 32 | late final S5 s5; 33 | late final S5Messenger s5messenger; 34 | late final SharedPreferencesWithCache prefs; 35 | late final LocationService locationService; 36 | 37 | Future initializeApp() async { 38 | try { 39 | // Emit loading 40 | emit(MainStateLoading()); 41 | 42 | // Do the quick dependencies 43 | prefs = await SharedPreferencesWithCache.create( 44 | cacheOptions: SharedPreferencesWithCacheOptions()); 45 | 46 | // Begin the rust stuff 47 | await RustLib.init(); 48 | final Directory dir = await getApplicationSupportDirectory(); 49 | Hive 50 | ..init(path.join(dir.path, 'hive')) 51 | ..registerAdapters(); 52 | locationService = LocationService(prefs: prefs); 53 | await locationService.init(); 54 | bool sucsess = 55 | await locationService.startPeriodicUpdates(intervalSeconds: 5); 56 | // This shuld only fail if location permissions haven't been granted, if that happens 57 | // emit so the app can push to the correct page 58 | if (sucsess == false) { 59 | emit(MainStateNeedsLocationPermission( 60 | prefs: prefs, 61 | locationService: locationService, 62 | )); 63 | } 64 | // register it here so I can grab for the background task later 65 | GetIt.I.registerSingleton(locationService); 66 | emit(MainStateLightInitialized( 67 | prefs: prefs, 68 | locationService: locationService, 69 | )); // Let the UI build on the quick deps 70 | 71 | // Do the slower dependeinces 72 | s5 = await S5.create( 73 | initialPeers: [ 74 | prefs.getString('s5-node') ?? '', // put the users s5 node first 75 | 'wss://z2DeVYsXdq3Rgt8252LRwNnreAtsGr3BN6FPc6Hvg6dTtRk@s5.jptr.tech/s5/p2p', 76 | 'wss://z2Das8aEF7oNoxkcrfvzerZ1iBPWfm6D7gy3hVE4ALGSpVB@node.sfive.net/s5/p2p', 77 | 'wss://z2DdbxV4xyoqWck5pXXJdVzRnwQC6Gbv6o7xDvyZvzKUfuj@s5.vup.dev/s5/p2p', 78 | 'wss://z2DWuWNZcdSyZLpXFK2uCU3haaWMXrDAgxzv17sDEMHstZb@s5.garden/s5/p2p', 79 | ], 80 | logger: SilentLogger(), 81 | persistFilePath: path.join( 82 | (await getApplicationDocumentsDirectory()).path, 'persist.json'), 83 | ); 84 | s5messenger = S5Messenger(); 85 | await s5messenger.init(s5); 86 | emit(MainStateHeavyInitialized( 87 | s5: s5, 88 | s5messenger: s5messenger, 89 | prefs: prefs, 90 | locationService: locationService, 91 | )); 92 | } catch (e) { 93 | logger.e('Initialization error: $e'); 94 | emit(MainStateError(e.toString())); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:background_fetch/background_fetch.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_bloc/flutter_bloc.dart'; 6 | import 'package:get_it/get_it.dart'; 7 | import 'package:logger/logger.dart'; 8 | import 'package:luogo/cubit/main/main_cubit.dart'; 9 | import 'package:luogo/services/location_service.dart'; 10 | import 'package:luogo/view/page/init_router.dart'; 11 | import 'package:path_provider/path_provider.dart'; 12 | import 'package:path/path.dart' as p; 13 | 14 | // This is my only global var, as no init process 15 | late Logger logger; 16 | 17 | /// Main fucntion that starts everything. Utilizes a Cubit to handle state 18 | void main() async { 19 | WidgetsFlutterBinding.ensureInitialized(); 20 | // Init logger to path if in prod 21 | String logPath = p.join((await getApplicationSupportDirectory()).path, "log"); 22 | logger = Logger( 23 | filter: ProductionFilter(), 24 | output: MultiOutput([ 25 | AdvancedFileOutput( 26 | path: logPath, // Path to log folder 27 | ), 28 | ConsoleOutput(), 29 | ])); 30 | logger.d("Logging at: $logPath"); 31 | final dir = Directory(logPath); 32 | logger.d(await dir.list().map((e) => e.path).join('\n')); 33 | // Initialize background fetch 34 | await BackgroundFetch.configure( 35 | BackgroundFetchConfig( 36 | minimumFetchInterval: 15, // iOS minimum 37 | stopOnTerminate: false, // Allow Android to survive termination 38 | enableHeadless: true, // Enable Android headless mode 39 | ), (taskId) async { 40 | logger.i("[BackgroundFetch] Event received"); 41 | try { 42 | // attempt to grab the old location service 43 | try { 44 | LocationService locationService = GetIt.I(); 45 | locationService.initializeGroupListeners(); 46 | await locationService.sendLocationUpdateOneShot(); 47 | logger.d("Sucsessfully background oneshot"); 48 | } catch (e) { 49 | // if it fails create a new one 50 | logger.e("Failed to get location service: $e"); 51 | logger.d("attempting re-init"); 52 | LocationService locationService = 53 | await LocationService.initializeForBackground(); 54 | locationService.initializeGroupListeners(); 55 | await locationService.sendLocationUpdateOneShot(); 56 | // Now that it has been re-inited, start listener and update peers 57 | } 58 | 59 | // await locationService.sendLocationUpdateOneShot(); 60 | logger.i("Background fetch ran"); 61 | } catch (e) { 62 | logger.e("Background fetch failed: $e"); 63 | } 64 | // give the listeners some breathing room to finsih and then gracefully exit 65 | await Future.delayed(const Duration(seconds: 10)); 66 | BackgroundFetch.finish(taskId); 67 | }, (taskId) => BackgroundFetch.finish(taskId) // Timeout handler 68 | ); 69 | runApp( 70 | BlocProvider( 71 | create: (context) => MainCubit()..initializeApp(), 72 | child: const Luogo(), 73 | ), 74 | ); 75 | } 76 | 77 | /// Top level Luogo function that defines the material app 78 | class Luogo extends StatelessWidget { 79 | const Luogo({super.key}); 80 | 81 | @override 82 | Widget build(BuildContext context) { 83 | return MaterialApp( 84 | title: 'Luogo', 85 | theme: ThemeData( 86 | colorScheme: ColorScheme.fromSeed( 87 | seedColor: Colors.deepPurple, 88 | brightness: Brightness.light, // Light theme 89 | ), 90 | ), 91 | darkTheme: ThemeData( 92 | colorScheme: ColorScheme.fromSeed( 93 | seedColor: Colors.deepPurple, 94 | brightness: Brightness.dark, // Dark theme 95 | ), 96 | ), 97 | themeMode: ThemeMode.system, 98 | home: const InitRouterPage(), 99 | ); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /windows/runner/win32_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_WIN32_WINDOW_H_ 2 | #define RUNNER_WIN32_WINDOW_H_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // A class abstraction for a high DPI-aware Win32 Window. Intended to be 11 | // inherited from by classes that wish to specialize with custom 12 | // rendering and input handling 13 | class Win32Window { 14 | public: 15 | struct Point { 16 | unsigned int x; 17 | unsigned int y; 18 | Point(unsigned int x, unsigned int y) : x(x), y(y) {} 19 | }; 20 | 21 | struct Size { 22 | unsigned int width; 23 | unsigned int height; 24 | Size(unsigned int width, unsigned int height) 25 | : width(width), height(height) {} 26 | }; 27 | 28 | Win32Window(); 29 | virtual ~Win32Window(); 30 | 31 | // Creates a win32 window with |title| that is positioned and sized using 32 | // |origin| and |size|. New windows are created on the default monitor. Window 33 | // sizes are specified to the OS in physical pixels, hence to ensure a 34 | // consistent size this function will scale the inputted width and height as 35 | // as appropriate for the default monitor. The window is invisible until 36 | // |Show| is called. Returns true if the window was created successfully. 37 | bool Create(const std::wstring& title, const Point& origin, const Size& size); 38 | 39 | // Show the current window. Returns true if the window was successfully shown. 40 | bool Show(); 41 | 42 | // Release OS resources associated with window. 43 | void Destroy(); 44 | 45 | // Inserts |content| into the window tree. 46 | void SetChildContent(HWND content); 47 | 48 | // Returns the backing Window handle to enable clients to set icon and other 49 | // window properties. Returns nullptr if the window has been destroyed. 50 | HWND GetHandle(); 51 | 52 | // If true, closing this window will quit the application. 53 | void SetQuitOnClose(bool quit_on_close); 54 | 55 | // Return a RECT representing the bounds of the current client area. 56 | RECT GetClientArea(); 57 | 58 | protected: 59 | // Processes and route salient window messages for mouse handling, 60 | // size change and DPI. Delegates handling of these to member overloads that 61 | // inheriting classes can handle. 62 | virtual LRESULT MessageHandler(HWND window, 63 | UINT const message, 64 | WPARAM const wparam, 65 | LPARAM const lparam) noexcept; 66 | 67 | // Called when CreateAndShow is called, allowing subclass window-related 68 | // setup. Subclasses should return false if setup fails. 69 | virtual bool OnCreate(); 70 | 71 | // Called when Destroy is called. 72 | virtual void OnDestroy(); 73 | 74 | private: 75 | friend class WindowClassRegistrar; 76 | 77 | // OS callback called by message pump. Handles the WM_NCCREATE message which 78 | // is passed when the non-client area is being created and enables automatic 79 | // non-client DPI scaling so that the non-client area automatically 80 | // responds to changes in DPI. All other messages are handled by 81 | // MessageHandler. 82 | static LRESULT CALLBACK WndProc(HWND const window, 83 | UINT const message, 84 | WPARAM const wparam, 85 | LPARAM const lparam) noexcept; 86 | 87 | // Retrieves a class instance pointer for |window| 88 | static Win32Window* GetThisFromHandle(HWND const window) noexcept; 89 | 90 | // Update the window frame's theme to match the system theme. 91 | static void UpdateTheme(HWND const window); 92 | 93 | bool quit_on_close_ = false; 94 | 95 | // window handle for top level window. 96 | HWND window_handle_ = nullptr; 97 | 98 | // window handle for hosted content. 99 | HWND child_content_ = nullptr; 100 | }; 101 | 102 | #endif // RUNNER_WIN32_WINDOW_H_ 103 | -------------------------------------------------------------------------------- /lib/view/page/ask_for_location.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:luogo/cubit/ask_for_location/ask_for_location_cubit.dart'; 4 | import 'package:luogo/view/page/init_router.dart'; 5 | 6 | class AskForLocationPermissionPage extends StatelessWidget { 7 | const AskForLocationPermissionPage({super.key}); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | final ThemeData theme = Theme.of(context); 12 | 13 | return BlocListener( 14 | listener: (BuildContext context, AskForLocationState state) { 15 | if (state is AskForLocationApproved) { 16 | Navigator.push( 17 | context, 18 | MaterialPageRoute( 19 | builder: (BuildContext context) => const InitRouterPage(), 20 | ), 21 | ); 22 | } else if (state is AskForLocationDenied) { 23 | Navigator.push( 24 | context, 25 | MaterialPageRoute( 26 | builder: (BuildContext context) => const InitRouterPage(), 27 | ), 28 | ); 29 | } 30 | }, 31 | child: Scaffold( 32 | body: Center( 33 | child: Padding( 34 | padding: const EdgeInsets.symmetric(horizontal: 24.0), 35 | child: Column( 36 | mainAxisSize: MainAxisSize.min, 37 | children: [ 38 | // Illustration 39 | SizedBox( 40 | height: 300, 41 | child: Image.asset( 42 | "assets/fake-map.png", 43 | fit: BoxFit.contain, 44 | ), 45 | ), 46 | const SizedBox(height: 32), 47 | 48 | // Title 49 | Text( 50 | "Luogo Needs Your Location", 51 | textAlign: TextAlign.center, 52 | style: theme.textTheme.headlineSmall?.copyWith( 53 | fontWeight: FontWeight.bold, 54 | ), 55 | ), 56 | const SizedBox(height: 16), 57 | 58 | // Subtitle / explanation 59 | Text( 60 | "Luogo is a location sharing app, so without your location " 61 | "it’s rather hard to do that. The app will still work, " 62 | "but not ideally — be warned!", 63 | textAlign: TextAlign.center, 64 | style: theme.textTheme.bodyMedium?.copyWith( 65 | height: 1.4, 66 | ), 67 | ), 68 | const SizedBox(height: 32), 69 | 70 | // Buttons 71 | Row( 72 | mainAxisAlignment: MainAxisAlignment.center, 73 | children: [ 74 | ElevatedButton( 75 | onPressed: () => 76 | BlocProvider.of(context) 77 | .requestPerms(), 78 | style: ElevatedButton.styleFrom( 79 | backgroundColor: theme 80 | .colorScheme.primary, // filled with primary color 81 | foregroundColor: theme 82 | .colorScheme.onPrimary, // makes text/icons readable 83 | padding: const EdgeInsets.symmetric( 84 | horizontal: 24, vertical: 12), 85 | shape: RoundedRectangleBorder( 86 | borderRadius: BorderRadius.circular(12), 87 | ), 88 | ), 89 | child: const Text("Continue"), 90 | ), 91 | ], 92 | ), 93 | ], 94 | ), 95 | ), 96 | ), 97 | ), 98 | ); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 65 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /lib/view/page/init_router.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:luogo/cubit/ask_for_location/ask_for_location_cubit.dart'; 4 | import 'package:luogo/cubit/init_router/init_router_cubit.dart'; 5 | import 'package:luogo/cubit/init_router/init_router_state.dart'; 6 | import 'package:luogo/cubit/main/main_cubit.dart'; 7 | import 'package:luogo/cubit/main/main_state.dart'; 8 | import 'package:luogo/cubit/map/map_cubit.dart'; 9 | import 'package:luogo/services/location_service.dart'; 10 | import 'package:luogo/view/page/ask_for_location.dart'; 11 | import 'package:luogo/view/page/create_profile.dart'; 12 | import 'package:luogo/view/page/home.dart'; 13 | import 'package:luogo/view/widgets/silly_progress_indicator.dart'; 14 | import 'package:shared_preferences/shared_preferences.dart'; 15 | 16 | class InitRouterPage extends StatelessWidget { 17 | const InitRouterPage({super.key}); 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return BlocBuilder(builder: (context, mainState) { 22 | return switch (mainState) { 23 | MainStateInitial() => const SillyCircularProgressIndicator(), 24 | MainStateLoading() => const SillyCircularProgressIndicator(), 25 | MainStateError(:final message) => Center(child: Text(message)), 26 | MainStateNeedsLocationPermission( 27 | :final prefs, 28 | :final locationService, 29 | ) => 30 | _buildInitRouterPage(context, prefs, locationService), 31 | MainStateLightInitialized( 32 | :final prefs, 33 | :final locationService, 34 | ) => 35 | _buildInitRouterPage(context, prefs, locationService), 36 | MainStateHeavyInitialized( 37 | :final prefs, 38 | :final locationService, 39 | ) => 40 | _buildInitRouterPage(context, prefs, locationService), 41 | }; 42 | }); 43 | } 44 | 45 | Widget _buildInitRouterPage( 46 | BuildContext context, 47 | SharedPreferencesWithCache prefs, 48 | LocationService locationService, 49 | ) { 50 | return BlocProvider( 51 | create: (context) => 52 | InitRouterCubit(prefs: prefs, locationService: locationService) 53 | ..getRoute(), 54 | child: BlocListener( 55 | listener: (context, state) { 56 | if (state is InitRouterSuccess) { 57 | _navigateBasedOnRoute(context, state.route, prefs, locationService); 58 | } 59 | }, 60 | child: SillyCircularProgressIndicator(), 61 | ), 62 | ); 63 | } 64 | 65 | void _navigateBasedOnRoute( 66 | BuildContext context, 67 | RouteType route, 68 | SharedPreferencesWithCache prefs, 69 | LocationService locationService, 70 | ) { 71 | late StatelessWidget page; 72 | switch (route) { 73 | case RouteType.home: 74 | { 75 | page = BlocProvider( 76 | create: (context) => MapCubit( 77 | locationService: locationService, 78 | prefs: prefs, 79 | ), 80 | child: HomePage( 81 | prefs: prefs, 82 | locationService: locationService, 83 | )); 84 | } 85 | case RouteType.login: 86 | { 87 | page = CreateProfilePage( 88 | prefs: prefs, 89 | locationService: locationService, 90 | ); 91 | } 92 | case RouteType.locationPerms: 93 | { 94 | page = BlocProvider( 95 | create: (BuildContext context) => AskForLocationCubit( 96 | locationService: locationService, 97 | prefs: prefs, 98 | ), 99 | child: AskForLocationPermissionPage(), 100 | ); 101 | } 102 | } 103 | 104 | Navigator.pushAndRemoveUntil(context, 105 | MaterialPageRoute(builder: (context) => page), (route) => false); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /lib/view/page/home/settings.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_bloc/flutter_bloc.dart'; 5 | import 'package:luogo/cubit/home/settings/settings_cubit.dart'; 6 | import 'package:luogo/cubit/home/settings/settings_state.dart'; 7 | import 'package:luogo/main.dart'; 8 | import 'package:luogo/utils/check_s5_connectivity.dart'; 9 | import 'package:luogo/view/widgets/file_viewer.dart'; 10 | import 'package:path_provider/path_provider.dart'; 11 | import 'package:path/path.dart' as p; 12 | 13 | /// Class that defines settings page 14 | class SettingsPage extends StatelessWidget { 15 | const SettingsPage({super.key}); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | final SettingsCubit cubit = context.read(); 20 | return BlocListener( 21 | listener: (BuildContext context, SettingsState state) { 22 | if (state is SettingsNewNodeError) { 23 | ScaffoldMessenger.of(context) 24 | .showSnackBar(SnackBar(content: Text('Invalid URL'))); 25 | } 26 | }, 27 | child: BlocBuilder( 28 | builder: (context, state) { 29 | return Scaffold( 30 | body: SafeArea( 31 | child: Column( 32 | children: [ 33 | const ListTile( 34 | title: Text( 35 | 'Settings', 36 | style: 37 | TextStyle(fontWeight: FontWeight.bold, fontSize: 20), 38 | ), 39 | ), 40 | Expanded( 41 | child: Column( 42 | children: [ 43 | Padding( 44 | padding: const EdgeInsets.all(20.0), 45 | child: Row( 46 | children: [ 47 | Expanded( 48 | child: TextField( 49 | controller: cubit.controller, 50 | decoration: InputDecoration( 51 | labelText: 'S5 Node', 52 | border: OutlineInputBorder(), 53 | hintText: cubit.controller.text, 54 | ), 55 | ), 56 | ), 57 | IconButton( 58 | icon: Icon(Icons.save), 59 | onPressed: () { 60 | cubit.setS5Node(); 61 | }, 62 | ), 63 | ], 64 | )), 65 | ], 66 | ), 67 | ), 68 | ElevatedButton( 69 | onPressed: () async { 70 | final Directory dir = 71 | await getApplicationSupportDirectory(); 72 | final String logPath = 73 | p.join(dir.path, 'log', 'latest.log'); 74 | logger.d("reading log from: $logPath"); 75 | if (context.mounted) { 76 | Navigator.of(context).push( 77 | MaterialPageRoute( 78 | builder: (_) => Scaffold( 79 | appBar: AppBar(title: const Text('Log Viewer')), 80 | body: SafeArea( 81 | child: TextFileViewer(filePath: logPath), 82 | ), 83 | ), 84 | ), 85 | ); 86 | } 87 | }, 88 | child: Text("Logs")), 89 | Text(BlocProvider.of(context).version), 90 | ], 91 | ), 92 | ), 93 | ); 94 | }, 95 | ), 96 | ); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /lib/view/page/map.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:luogo/services/location_service.dart'; 4 | import 'package:luogo/view/page/map/bottom_sheet_info_modal.dart'; 5 | import 'package:maplibre_gl/maplibre_gl.dart'; 6 | import 'package:luogo/cubit/map/map_cubit.dart'; 7 | import 'package:luogo/cubit/map/map_state.dart'; 8 | import 'package:shared_preferences/shared_preferences.dart'; 9 | 10 | /// The primary map interface widget using MapLibre GL for map rendering. 11 | /// 12 | /// This widget provides: 13 | /// - Interactive map display with style configuration 14 | /// - User location tracking functionality 15 | /// - Bottom sheet modals for symbol/point interactions 16 | /// - Floating action buttons for map navigation 17 | /// 18 | /// Dependencies: 19 | /// - [locationService]: Handles location updates and peer communication 20 | /// - [prefs]: Shared preferences for persistent settings 21 | /// 22 | /// Behavior: 23 | /// - Listens to [MapCubit] state changes for UI updates 24 | /// - Shows [BottomSheetInfoModal] when map symbols are clicked 25 | /// - Provides "recenter" and "send location" FAB controls 26 | /// - Initializes with default camera position (LatLng(1,1) at zoom 10) 27 | /// 28 | /// UI Structure: 29 | /// 1. Base MapLibre map (full screen) 30 | /// 2. Floating action buttons: 31 | /// - Right: Recenter map to user location 32 | /// 3. Dynamic bottom sheet modal for point information 33 | /// 34 | /// Note: Uses 'assets/pmtiles_style.json' for map styling configuration 35 | class MapView extends StatelessWidget { 36 | final LocationService locationService; 37 | final SharedPreferencesWithCache prefs; 38 | const MapView({ 39 | super.key, 40 | required this.locationService, 41 | required this.prefs, 42 | }); 43 | 44 | @override 45 | Widget build(BuildContext context) { 46 | // Use BlocProvider to handle cubit state 47 | return Scaffold( 48 | floatingActionButtonLocation: 49 | FloatingActionButtonLocation.miniCenterFloat, 50 | 51 | // This section reacts to state to draw modals and such 52 | body: BlocListener( 53 | listener: (BuildContext context, MapState state) { 54 | if (state is MapSymbolClicked) { 55 | showModalBottomSheet( 56 | context: context, 57 | builder: (BuildContext context) => BottomSheetInfoModal( 58 | userState: state.userState, 59 | isYou: state.isYou, 60 | )); 61 | } 62 | }, 63 | 64 | // This section reacts to state to draw the main page 65 | child: BlocBuilder( 66 | builder: (context, state) { 67 | return SafeArea( 68 | top: false, 69 | child: Stack( 70 | children: [ 71 | MapLibreMap( 72 | onMapCreated: (controller) => 73 | context.read().mapCreated(controller), 74 | initialCameraPosition: const CameraPosition( 75 | target: LatLng(1, 1), 76 | zoom: 10.0, 77 | ), 78 | trackCameraPosition: true, 79 | styleString: "assets/pmtiles_style.json", 80 | ), 81 | if (state is! MapInitial) 82 | Positioned( 83 | bottom: 20, 84 | right: 20, 85 | child: Center( 86 | child: FloatingActionButton( 87 | heroTag: "map-hero-floater", 88 | onPressed: () => 89 | context.read().moveToUser(), 90 | mini: true, 91 | child: const Icon(Icons.navigation), 92 | ), 93 | ), 94 | ), 95 | ], 96 | ), 97 | ); 98 | }, 99 | ), 100 | ), 101 | ); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /windows/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file controls Flutter-level build steps. It should not be edited. 2 | cmake_minimum_required(VERSION 3.14) 3 | 4 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 5 | 6 | # Configuration provided via flutter tool. 7 | include(${EPHEMERAL_DIR}/generated_config.cmake) 8 | 9 | # TODO: Move the rest of this into files in ephemeral. See 10 | # https://github.com/flutter/flutter/issues/57146. 11 | set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") 12 | 13 | # Set fallback configurations for older versions of the flutter tool. 14 | if (NOT DEFINED FLUTTER_TARGET_PLATFORM) 15 | set(FLUTTER_TARGET_PLATFORM "windows-x64") 16 | endif() 17 | 18 | # === Flutter Library === 19 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") 20 | 21 | # Published to parent scope for install step. 22 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 23 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 24 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 25 | set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) 26 | 27 | list(APPEND FLUTTER_LIBRARY_HEADERS 28 | "flutter_export.h" 29 | "flutter_windows.h" 30 | "flutter_messenger.h" 31 | "flutter_plugin_registrar.h" 32 | "flutter_texture_registrar.h" 33 | ) 34 | list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") 35 | add_library(flutter INTERFACE) 36 | target_include_directories(flutter INTERFACE 37 | "${EPHEMERAL_DIR}" 38 | ) 39 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") 40 | add_dependencies(flutter flutter_assemble) 41 | 42 | # === Wrapper === 43 | list(APPEND CPP_WRAPPER_SOURCES_CORE 44 | "core_implementations.cc" 45 | "standard_codec.cc" 46 | ) 47 | list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") 48 | list(APPEND CPP_WRAPPER_SOURCES_PLUGIN 49 | "plugin_registrar.cc" 50 | ) 51 | list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") 52 | list(APPEND CPP_WRAPPER_SOURCES_APP 53 | "flutter_engine.cc" 54 | "flutter_view_controller.cc" 55 | ) 56 | list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") 57 | 58 | # Wrapper sources needed for a plugin. 59 | add_library(flutter_wrapper_plugin STATIC 60 | ${CPP_WRAPPER_SOURCES_CORE} 61 | ${CPP_WRAPPER_SOURCES_PLUGIN} 62 | ) 63 | apply_standard_settings(flutter_wrapper_plugin) 64 | set_target_properties(flutter_wrapper_plugin PROPERTIES 65 | POSITION_INDEPENDENT_CODE ON) 66 | set_target_properties(flutter_wrapper_plugin PROPERTIES 67 | CXX_VISIBILITY_PRESET hidden) 68 | target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) 69 | target_include_directories(flutter_wrapper_plugin PUBLIC 70 | "${WRAPPER_ROOT}/include" 71 | ) 72 | add_dependencies(flutter_wrapper_plugin flutter_assemble) 73 | 74 | # Wrapper sources needed for the runner. 75 | add_library(flutter_wrapper_app STATIC 76 | ${CPP_WRAPPER_SOURCES_CORE} 77 | ${CPP_WRAPPER_SOURCES_APP} 78 | ) 79 | apply_standard_settings(flutter_wrapper_app) 80 | target_link_libraries(flutter_wrapper_app PUBLIC flutter) 81 | target_include_directories(flutter_wrapper_app PUBLIC 82 | "${WRAPPER_ROOT}/include" 83 | ) 84 | add_dependencies(flutter_wrapper_app flutter_assemble) 85 | 86 | # === Flutter tool backend === 87 | # _phony_ is a non-existent file to force this command to run every time, 88 | # since currently there's no way to get a full input/output list from the 89 | # flutter tool. 90 | set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") 91 | set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) 92 | add_custom_command( 93 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 94 | ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} 95 | ${CPP_WRAPPER_SOURCES_APP} 96 | ${PHONY_OUTPUT} 97 | COMMAND ${CMAKE_COMMAND} -E env 98 | ${FLUTTER_TOOL_ENVIRONMENT} 99 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" 100 | ${FLUTTER_TARGET_PLATFORM} $ 101 | VERBATIM 102 | ) 103 | add_custom_target(flutter_assemble DEPENDS 104 | "${FLUTTER_LIBRARY}" 105 | ${FLUTTER_LIBRARY_HEADERS} 106 | ${CPP_WRAPPER_SOURCES_CORE} 107 | ${CPP_WRAPPER_SOURCES_PLUGIN} 108 | ${CPP_WRAPPER_SOURCES_APP} 109 | ) 110 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 38 | 39 | 40 | 41 | 44 | 50 | 51 | 52 | 53 | 54 | 66 | 68 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /lib/cubit/home/groups_drawer/groups_drawer_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_bloc/flutter_bloc.dart'; 2 | import 'package:lib5/util.dart'; 3 | import 'package:luogo/cubit/home/groups_drawer/groups_drawer_state.dart'; 4 | import 'package:luogo/main.dart'; 5 | import 'package:luogo/model/group_info.dart'; 6 | import 'package:luogo/model/user_state.dart'; 7 | import 'package:luogo/services/location_service.dart'; 8 | import 'package:s5_messenger/s5_messenger.dart'; 9 | 10 | /// A Cubit class for managing the state of the groups drawer. 11 | /// 12 | /// Example usage: 13 | /// ```dart 14 | /// BlocProvider( 15 | /// create: (context) => GroupsDrawerCubit(), 16 | /// child: YourGroupsDrawerWidget(), 17 | /// ) 18 | /// ``` 19 | class GroupsDrawerCubit extends Cubit { 20 | S5Messenger? s5messenger; // mutable so can load async 21 | LocationService locationService; 22 | 23 | GroupsDrawerCubit({ 24 | required this.locationService, 25 | }) : super(GroupsDrawerInitial()); 26 | 27 | String? currentGroupID; 28 | 29 | String? getMemebersFromGroup(String groupID) { 30 | String toReturn = "you"; 31 | if (s5messenger != null) { 32 | GroupState groupState = s5messenger!.group(groupID); 33 | for (final GroupMember member in groupState.members) { 34 | final String memberID = base64UrlNoPaddingEncode(member.signatureKey); 35 | if (memberID == locationService.myID) { 36 | continue; // skip if self, no need to waste a loop 37 | } 38 | 39 | // First gotta add the initial symbols 40 | final UserState? userState = locationService.userStateBox.get(memberID); 41 | if (userState != null) { 42 | toReturn += ", ${userState.name}"; 43 | } 44 | } 45 | } 46 | return toReturn; 47 | } 48 | 49 | Future setS5Messenger(S5Messenger s5messengerIn) async { 50 | s5messenger = s5messengerIn; 51 | emit(GroupsDrawerLoading()); 52 | loadGroups(); 53 | } 54 | 55 | Future loadGroups() async { 56 | if (s5messenger == null) return; 57 | emit(GroupsDrawerLoading()); 58 | try { 59 | final GroupInfoList groups = 60 | GroupInfo.fromJsonList(s5messenger!.groupsBox.values.toList()); 61 | final GroupInfo? currentGroup = 62 | groups.findByID(s5messenger!.messengerState.groupId ?? ""); 63 | emit(GroupsDrawerLoaded(groups, currentGroup)); 64 | } catch (e) { 65 | logger.e(e); 66 | emit(GroupsDrawerError(e.toString())); 67 | } 68 | } 69 | 70 | Future createGroup(String? groupName) async { 71 | if (s5messenger == null) { 72 | logger.e("s5_messenger is not yet loaded, cannot create group."); 73 | return null; 74 | } 75 | try { 76 | GroupState newGroup = await s5messenger!.createNewGroup(groupName); 77 | loadGroups(); // Refresh the list 78 | // Then add listener 79 | locationService.setupListenToPeer(newGroup); 80 | return newGroup; 81 | } catch (e) { 82 | emit(GroupsDrawerError(e.toString())); 83 | return null; 84 | } 85 | } 86 | 87 | Future selectGroup(String? groupId) async { 88 | if (s5messenger == null || currentGroupID == groupId) return; 89 | try { 90 | s5messenger!.messengerState.groupId = groupId; 91 | s5messenger!.messengerState.update(); 92 | final GroupInfoList groups = 93 | GroupInfo.fromJsonList(s5messenger!.groupsBox.values.toList()); 94 | final GroupInfo? group = 95 | (groupId == null) ? null : groups.findByID(groupId); 96 | emit(GroupsDrawerLoaded(groups, group)); // Use same state 97 | } catch (e) { 98 | emit(GroupsDrawerError(e.toString())); 99 | } 100 | } 101 | 102 | Future renameGroup(String groupId, String newName) async { 103 | if (s5messenger == null) return; 104 | try { 105 | s5messenger!.group(groupId).rename(newName); 106 | locationService.sendRenameUpdate(groupId, newName); 107 | loadGroups(); // Refresh the list 108 | } catch (e) { 109 | emit(GroupsDrawerError(e.toString())); 110 | } 111 | } 112 | 113 | Future leaveGroup(String groupID) async { 114 | if (s5messenger == null) return; 115 | s5messenger!.leaveGroup(s5messenger!.group(groupID)); 116 | loadGroups(); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | {"images":[{"size":"20x20","idiom":"universal","filename":"Icon-App-20x20@2x.png","scale":"2x","platform":"ios"},{"size":"20x20","idiom":"universal","filename":"Icon-App-20x20@3x.png","scale":"3x","platform":"ios"},{"size":"29x29","idiom":"universal","filename":"Icon-App-29x29@2x.png","scale":"2x","platform":"ios"},{"size":"29x29","idiom":"universal","filename":"Icon-App-29x29@3x.png","scale":"3x","platform":"ios"},{"size":"38x38","idiom":"universal","filename":"Icon-App-38x38@2x.png","scale":"2x","platform":"ios"},{"size":"38x38","idiom":"universal","filename":"Icon-App-38x38@3x.png","scale":"3x","platform":"ios"},{"size":"40x40","idiom":"universal","filename":"Icon-App-40x40@2x.png","scale":"2x","platform":"ios"},{"size":"40x40","idiom":"universal","filename":"Icon-App-40x40@3x.png","scale":"3x","platform":"ios"},{"size":"60x60","idiom":"universal","filename":"Icon-App-60x60@2x.png","scale":"2x","platform":"ios"},{"size":"60x60","idiom":"universal","filename":"Icon-App-60x60@3x.png","scale":"3x","platform":"ios"},{"size":"64x64","idiom":"universal","filename":"Icon-App-64x64@2x.png","scale":"2x","platform":"ios"},{"size":"64x64","idiom":"universal","filename":"Icon-App-64x64@3x.png","scale":"3x","platform":"ios"},{"size":"68x68","idiom":"universal","filename":"Icon-App-68x68@2x.png","scale":"2x","platform":"ios"},{"size":"76x76","idiom":"universal","filename":"Icon-App-76x76@2x.png","scale":"2x","platform":"ios"},{"size":"83.5x83.5","idiom":"universal","filename":"Icon-App-83.5x83.5@2x.png","scale":"2x","platform":"ios"},{"size":"1024x1024","idiom":"universal","filename":"Icon-App-1024x1024@1x.png","scale":"1x","platform":"ios"},{"size":"1024x1024","idiom":"ios-marketing","filename":"Icon-App-1024x1024@1x.png","scale":"1x"},{"size":"20x20","idiom":"universal","filename":"Icon-App-Tinted-20x20@2x.png","scale":"2x","platform":"ios","appearances":[{"appearance":"luminosity","value":"tinted"}]},{"size":"20x20","idiom":"universal","filename":"Icon-App-Tinted-20x20@3x.png","scale":"3x","platform":"ios","appearances":[{"appearance":"luminosity","value":"tinted"}]},{"size":"29x29","idiom":"universal","filename":"Icon-App-Tinted-29x29@2x.png","scale":"2x","platform":"ios","appearances":[{"appearance":"luminosity","value":"tinted"}]},{"size":"29x29","idiom":"universal","filename":"Icon-App-Tinted-29x29@3x.png","scale":"3x","platform":"ios","appearances":[{"appearance":"luminosity","value":"tinted"}]},{"size":"38x38","idiom":"universal","filename":"Icon-App-Tinted-38x38@2x.png","scale":"2x","platform":"ios","appearances":[{"appearance":"luminosity","value":"tinted"}]},{"size":"38x38","idiom":"universal","filename":"Icon-App-Tinted-38x38@3x.png","scale":"3x","platform":"ios","appearances":[{"appearance":"luminosity","value":"tinted"}]},{"size":"40x40","idiom":"universal","filename":"Icon-App-Tinted-40x40@2x.png","scale":"2x","platform":"ios","appearances":[{"appearance":"luminosity","value":"tinted"}]},{"size":"40x40","idiom":"universal","filename":"Icon-App-Tinted-40x40@3x.png","scale":"3x","platform":"ios","appearances":[{"appearance":"luminosity","value":"tinted"}]},{"size":"60x60","idiom":"universal","filename":"Icon-App-Tinted-60x60@2x.png","scale":"2x","platform":"ios","appearances":[{"appearance":"luminosity","value":"tinted"}]},{"size":"60x60","idiom":"universal","filename":"Icon-App-Tinted-60x60@3x.png","scale":"3x","platform":"ios","appearances":[{"appearance":"luminosity","value":"tinted"}]},{"size":"64x64","idiom":"universal","filename":"Icon-App-Tinted-64x64@2x.png","scale":"2x","platform":"ios","appearances":[{"appearance":"luminosity","value":"tinted"}]},{"size":"64x64","idiom":"universal","filename":"Icon-App-Tinted-64x64@3x.png","scale":"3x","platform":"ios","appearances":[{"appearance":"luminosity","value":"tinted"}]},{"size":"68x68","idiom":"universal","filename":"Icon-App-Tinted-68x68@2x.png","scale":"2x","platform":"ios","appearances":[{"appearance":"luminosity","value":"tinted"}]},{"size":"76x76","idiom":"universal","filename":"Icon-App-Tinted-76x76@2x.png","scale":"2x","platform":"ios","appearances":[{"appearance":"luminosity","value":"tinted"}]},{"size":"83.5x83.5","idiom":"universal","filename":"Icon-App-Tinted-83.5x83.5@2x.png","scale":"2x","platform":"ios","appearances":[{"appearance":"luminosity","value":"tinted"}]},{"size":"1024x1024","idiom":"universal","filename":"Icon-App-Tinted-1024x1024@1x.png","scale":"1x","platform":"ios","appearances":[{"appearance":"luminosity","value":"tinted"}]}],"info":{"version":1,"author":"xcode"}} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > ⚠️Luogo is beta software! I've done my best to build a functional app, but if you run into any issues please file an [issue](https://github.com/lukehmcc/luogo/issues). 2 | 3 | # Luogo 4 | 5 | *A Simple & Secure Group Location Sharing App* 6 | 7 |

8 | 9 | 10 | 11 |

12 | 13 |

14 | 15 | Google Play badge 18 | 19 | 20 | 21 | App Store badge 24 | 25 |

26 | 27 |

28 | 29 |

30 | 31 | ![3screenshots](assets/screenshots.jpg) 32 | 33 | **Luogo** is a cross-platform mobile app that allows groups of people to easily share their location without it [being sold](https://www.theverge.com/2021/12/9/22820381/tile-life360-location-tracking-data-privacy). Fully open source, writing in dart & rust, and utilizing modern MLS & encryption tech. You no longer have to trust a large corporatoin with your data. 34 | 35 | --- 36 | 37 | ## Features 38 | 39 | - **Cross Compatible:** No more worrying about walled gardens. All of your friends can join regardless of platform. (iOS/Android) 40 | 41 | - **Batteries Included:** No configuration required to get started. (Though you still can configure your [S5 Node](https://s5.pro/) if you wish). 42 | 43 | - **MLS:** Utilizes [modern encryption](https://github.com/openmls/openmls) to make sure no one can read your location data unless you want them to. 44 | 45 | - **Groups:** Granular group sharing so you can control who knows where you are. 46 | 47 | - **One Time Send:** If you just want someone to have your location once, just send it with the one time button! No need to constantly send updates. 48 | 49 | ## Getting Started 50 | 51 | **In Beta Period**: For now the best way to try the app is to go to the [Releases](https://github.com/lukehmcc/luogo/releases) page and download the Android APK file. This will be updated when the app gets pushed to the App/Play Stores. 52 | 53 | ### Development 54 | 55 | To get started with local development do the following: 56 | 57 | ```bash 58 | git clone --recursive https://github.com/lukehmcc/luogo.git # make sure to recuse submodules 59 | # If you forgot to recurse and already cloned you can do this 60 | # git submodule init && git submodule update 61 | cd luogo/ 62 | ./flutterw run --flavor dev # Make sure to run with the flutter wrapper so everyone is on the same flutter version 63 | ``` 64 | 65 | To build a production apk: 66 | 67 | ```bash 68 | ./flutterw build appbundle --flavor prod 69 | ``` 70 | 71 | Feel free to [submit an issue](https://github.com/lukehmcc/luogo/issues) or [PR](https://github.com/lukehmcc/luogo/pulls) if you run into any issues. I'm here to collaborate and make the best app possible! 72 | 73 | ## Architecture 74 | 75 | **Backend:** Utilizes the [s5_messenger](https://github.com/s5-dev/s5_messenger) library to handle messaging between users. The users location is grabed from the [geolocator](https://pub.dev/packages/geolocator), parsed into a [message format](https://github.com/s5-dev/s5_messenger/blob/main/lib/src/mls5/model/message.dart), then s5_messenger uses [openmls](https://crates.io/crates/openmls) to create the message in the respecitve group, and this message is relayed through the s5 network via a [streams message](https://docs.s5.pro/spec/streams.html). This is then done in reverse by the other clients in the room. 76 | 77 | **Front End:** Utilizes [Flutter](https://flutter.dev/) to handle cross-platform compling with a single codebase. [Cubits](https://bloclibrary.dev/bloc-concepts/#creating-a-cubit) are used to handle state and seperate out buisness logic from the UI. The state code can be found in `lib/cubit` and the UI code is in in `lib/view`. 78 | 79 | ## Etimology 80 | 81 | Luogo is an Italian word for Place. Just thought it sounded nice (and no one had an app called that yet :p) 82 | 83 | ## Acknowledgement 84 | 85 | Th work is supported by a [Sia Foundation](https://sia.tech/) grant. 86 | -------------------------------------------------------------------------------- /windows/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Project-level configuration. 2 | cmake_minimum_required(VERSION 3.14) 3 | project(s5_messenger_example LANGUAGES CXX) 4 | 5 | # The name of the executable created for the application. Change this to change 6 | # the on-disk name of your application. 7 | set(BINARY_NAME "s5_messenger_example") 8 | 9 | # Explicitly opt in to modern CMake behaviors to avoid warnings with recent 10 | # versions of CMake. 11 | cmake_policy(VERSION 3.14...3.25) 12 | 13 | # Define build configuration option. 14 | get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) 15 | if(IS_MULTICONFIG) 16 | set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" 17 | CACHE STRING "" FORCE) 18 | else() 19 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 20 | set(CMAKE_BUILD_TYPE "Debug" CACHE 21 | STRING "Flutter build mode" FORCE) 22 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 23 | "Debug" "Profile" "Release") 24 | endif() 25 | endif() 26 | # Define settings for the Profile build mode. 27 | set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") 28 | set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") 29 | set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") 30 | set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") 31 | 32 | # Use Unicode for all projects. 33 | add_definitions(-DUNICODE -D_UNICODE) 34 | 35 | # Compilation settings that should be applied to most targets. 36 | # 37 | # Be cautious about adding new options here, as plugins use this function by 38 | # default. In most cases, you should add new options to specific targets instead 39 | # of modifying this function. 40 | function(APPLY_STANDARD_SETTINGS TARGET) 41 | target_compile_features(${TARGET} PUBLIC cxx_std_17) 42 | target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") 43 | target_compile_options(${TARGET} PRIVATE /EHsc) 44 | target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") 45 | target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") 46 | endfunction() 47 | 48 | # Flutter library and tool build rules. 49 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 50 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 51 | 52 | # Application build; see runner/CMakeLists.txt. 53 | add_subdirectory("runner") 54 | 55 | 56 | # Generated plugin build rules, which manage building the plugins and adding 57 | # them to the application. 58 | include(flutter/generated_plugins.cmake) 59 | 60 | 61 | # === Installation === 62 | # Support files are copied into place next to the executable, so that it can 63 | # run in place. This is done instead of making a separate bundle (as on Linux) 64 | # so that building and running from within Visual Studio will work. 65 | set(BUILD_BUNDLE_DIR "$") 66 | # Make the "install" step default, as it's required to run. 67 | set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) 68 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 69 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 70 | endif() 71 | 72 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 73 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") 74 | 75 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 76 | COMPONENT Runtime) 77 | 78 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 79 | COMPONENT Runtime) 80 | 81 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 82 | COMPONENT Runtime) 83 | 84 | if(PLUGIN_BUNDLED_LIBRARIES) 85 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" 86 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 87 | COMPONENT Runtime) 88 | endif() 89 | 90 | # Copy the native assets provided by the build.dart from all packages. 91 | set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") 92 | install(DIRECTORY "${NATIVE_ASSETS_DIR}" 93 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 94 | COMPONENT Runtime) 95 | 96 | # Fully re-copy the assets directory on each build to avoid having stale files 97 | # from a previous install. 98 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 99 | install(CODE " 100 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 101 | " COMPONENT Runtime) 102 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 103 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 104 | 105 | # Install the AOT library on non-Debug builds only. 106 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 107 | CONFIGURATIONS Profile;Release 108 | COMPONENT Runtime) 109 | --------------------------------------------------------------------------------