├── .github ├── ISSUE_TEMPLATE │ ├── ---bug-report.md │ ├── ---feature-request.md │ ├── ---questions---help.md │ └── config.yml ├── pull_request_template.md └── workflows │ ├── deploy-pages.yaml │ └── flutter-analyze.yaml ├── .gitignore ├── .metadata ├── CHANGELOG.md ├── CNAME ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── doc ├── .gitignore ├── README.md ├── babel.config.js ├── docs │ ├── guides │ │ ├── supabase-security-rls.md │ │ ├── supabase-usage.md │ │ ├── supabse-indexes.md │ │ ├── supabse-trigges.md │ │ └── supabse-views.md │ └── introduction │ │ ├── supabase-overview.md │ │ ├── supabase-package-installation.md │ │ └── supabase-project-configuration.md ├── docusaurus.config.ts ├── package-lock.json ├── package.json ├── sidebars.ts ├── src │ └── css │ │ └── custom.css ├── static │ ├── .nojekyll │ └── img │ │ ├── favicon.ico │ │ ├── flayer_chat_ui_screenshot.png │ │ ├── logo.svg │ │ ├── social-card.png │ │ ├── supabase-disable-confirm-email.png │ │ ├── supabase-project-credential.png │ │ └── supabase-schema-exposure-setting.png └── tsconfig.json ├── example ├── .gitignore ├── .metadata ├── README.md ├── analysis_options.yaml ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── untitled1 │ │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ │ ├── drawable-v21 │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── values-night │ │ │ │ └── styles.xml │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle ├── assets │ ├── flyer_logo.png │ └── flyer_logo.svg ├── devtools_options.yaml ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ ├── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ ├── Contents.json │ │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ │ ├── Icon-App-20x20@1x.png │ │ │ │ ├── Icon-App-20x20@2x.png │ │ │ │ ├── Icon-App-20x20@3x.png │ │ │ │ ├── Icon-App-29x29@1x.png │ │ │ │ ├── Icon-App-29x29@2x.png │ │ │ │ ├── Icon-App-29x29@3x.png │ │ │ │ ├── Icon-App-40x40@1x.png │ │ │ │ ├── Icon-App-40x40@2x.png │ │ │ │ ├── Icon-App-40x40@3x.png │ │ │ │ ├── Icon-App-60x60@2x.png │ │ │ │ ├── Icon-App-60x60@3x.png │ │ │ │ ├── Icon-App-76x76@1x.png │ │ │ │ ├── Icon-App-76x76@2x.png │ │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ │ └── LaunchImage.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── LaunchImage.png │ │ │ │ ├── LaunchImage@2x.png │ │ │ │ ├── LaunchImage@3x.png │ │ │ │ └── README.md │ │ ├── Base.lproj │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── Runner-Bridging-Header.h │ └── RunnerTests │ │ └── RunnerTests.swift ├── lib │ ├── main.dart │ ├── src │ │ ├── class │ │ │ ├── message_status_ex.dart │ │ │ └── user_ex.dart │ │ ├── pages │ │ │ ├── auth.dart │ │ │ ├── home.dart │ │ │ ├── room.dart │ │ │ ├── rooms.dart │ │ │ └── users.dart │ │ ├── theme │ │ │ └── color_schemes.dart │ │ ├── util.dart │ │ └── widgets │ │ │ ├── room_tile.dart │ │ │ └── user_tile.dart │ └── supabase_options.dart ├── linux │ ├── .gitignore │ ├── CMakeLists.txt │ ├── flutter │ │ ├── CMakeLists.txt │ │ ├── generated_plugin_registrant.cc │ │ ├── generated_plugin_registrant.h │ │ └── generated_plugins.cmake │ ├── main.cc │ ├── my_application.cc │ └── my_application.h ├── macos │ ├── .gitignore │ ├── Flutter │ │ ├── Flutter-Debug.xcconfig │ │ └── Flutter-Release.xcconfig │ ├── Podfile │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ └── xcshareddata │ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ │ └── AppIcon.appiconset │ │ │ │ ├── Contents.json │ │ │ │ ├── app_icon_1024.png │ │ │ │ ├── app_icon_128.png │ │ │ │ ├── app_icon_16.png │ │ │ │ ├── app_icon_256.png │ │ │ │ ├── app_icon_32.png │ │ │ │ ├── app_icon_512.png │ │ │ │ └── app_icon_64.png │ │ ├── Base.lproj │ │ │ └── MainMenu.xib │ │ ├── Configs │ │ │ ├── AppInfo.xcconfig │ │ │ ├── Debug.xcconfig │ │ │ ├── Release.xcconfig │ │ │ └── Warnings.xcconfig │ │ ├── DebugProfile.entitlements │ │ ├── Info.plist │ │ ├── MainFlutterWindow.swift │ │ └── Release.entitlements │ └── RunnerTests │ │ └── RunnerTests.swift ├── pubspec.yaml ├── utils │ ├── prepare.ps1 │ ├── prepare.sh │ └── sql │ │ ├── 01_database_schema.sql │ │ ├── 02_database_trigger.sql │ │ ├── 03_database_policy.sql │ │ ├── 04_storage.sql │ │ ├── 05_database_view.sql │ │ └── 99_database_schema_permission.sql ├── web │ ├── favicon.png │ ├── icons │ │ ├── Icon-192.png │ │ ├── Icon-512.png │ │ ├── Icon-maskable-192.png │ │ └── Icon-maskable-512.png │ ├── index.html │ └── manifest.json └── windows │ ├── .gitignore │ ├── CMakeLists.txt │ ├── flutter │ ├── CMakeLists.txt │ ├── generated_plugin_registrant.cc │ ├── generated_plugin_registrant.h │ └── generated_plugins.cmake │ └── runner │ ├── CMakeLists.txt │ ├── Runner.rc │ ├── flutter_window.cpp │ ├── flutter_window.h │ ├── main.cpp │ ├── resource.h │ ├── resources │ └── app_icon.ico │ ├── runner.exe.manifest │ ├── utils.cpp │ ├── utils.h │ ├── win32_window.cpp │ └── win32_window.h ├── lib ├── flutter_supabase_chat_core.dart └── src │ ├── class │ ├── supabase_chat_controller.dart │ ├── supabase_chat_core.dart │ ├── supabase_chat_core_config.dart │ ├── upload_asset_result.dart │ └── user_online_status.dart │ ├── util.dart │ └── widgets │ ├── user_online_status.dart │ ├── user_status_detector │ ├── other_platform.dart │ ├── platforms_widgets_binding.dart │ ├── web_platform.dart │ └── widgets_binding.dart │ └── user_status_observer.dart └── pubspec.yaml /.github/ISSUE_TEMPLATE/---bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41B Bug report" 3 | about: Something isn't working? Create a report to help us improve Flyer Chat. 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | 22 | 23 | # General 24 | 25 | ## What bug do you experience? 🐞 26 | Provide a clear and concise description of what the bug is. 27 | 28 | ## How can it be reproduced? 🤔 29 | A few steps to define where does the bug occur. 30 | Step 1. ... 31 | Step 2. ... etc. 32 | 33 | ## What behavior is expected? 💡 34 | A clear and concise description of what you expected to happen. 35 | 36 |
37 | 38 | # Extras 39 | 40 | ## Screenshots or videos 📸 41 | If applicable, add screenshots or videos to help explain your problem. 42 | 43 | ## Code snippets 📝 44 | If applicable, add code samples to help explain your problem. 45 | 46 | ```dart 47 | // Your snippet here... 48 | ``` 49 | 50 |
51 | 52 | # Environment info 53 | 54 | Please specify the flutter, flutter_supabase_chat_core versions. 55 | 56 | flutter: ... 57 | flutter_supabase_chat_core: ... 58 | 59 | `flutter doctor -v` output 👇 60 | 61 | ```sh 62 | ``` 63 | 64 |
65 | 66 | # Platform 67 | 68 | Device (e.g. Android emulator, iOS simulator, Samsung Galaxy S21): ... 69 | 70 | OS version (e.g. iOS 14.5, Android 11): ... 71 | 72 |
73 | 74 | # Additional context 75 | 76 | Add any other context about the problem here. 77 | 78 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/---feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "⚡️ Feature request" 3 | about: Suggest an idea to help make Flyer Chat even better! 4 | title: '' 5 | labels: feature 6 | assignees: '' 7 | 8 | --- 9 | 10 | 15 | 16 | ## Is your feature request related to a problem? 17 | A clear and concise description of what the problem is or why we should process it. 18 | 19 | ## What solution would you suggest? 20 | A clear and concise description of what you want to happen. How would it solve the problem of yours? 21 | 22 | ## Is there any additional solution to that? 23 | A clear and concise description of any alternative solutions or features you've considered. 24 | 25 | ## Extras 26 | **Screenshots or videos 📸** 27 | If applicable, add screenshots or videos to help explain your feature. 28 | 29 | **Code snippets 📝** 30 | If applicable, add code samples to help explain your feature. 31 | 32 | ```dart 33 | // Your snippet here... 34 | ``` 35 | 36 | ## Related issues/PRs 37 | Let us know if this is related to any issue/pull request. 38 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/---questions---help.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F914 Questions & help" 3 | about: " Use this if there is something not clear about the code or documentation." 4 | title: '' 5 | labels: question 6 | assignees: '' 7 | 8 | --- 9 | 10 | 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Link preview 4 | url: https://github.com/flyerhq/flutter_link_previewer/issues/new 5 | about: Use this to report link preview issues. 6 | - name: Chat UI 7 | url: https://github.com/flyerhq/flutter_chat_ui/issues/new/choose 8 | about: Use this to report chat UI issues. 9 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 12 | 13 | ### What does it do? 14 | 15 | Describe the technical changes you did. 16 | 17 | ### Why is it needed? 18 | 19 | Describe the issue you are solving. 20 | 21 | ### How to test it? 22 | 23 | Provide information about the environment and the path to verify the behavior. 24 | 25 | ### Related issues/PRs 26 | 27 | Let us know if this is related to any issue/pull request. -------------------------------------------------------------------------------- /.github/workflows/deploy-pages.yaml: -------------------------------------------------------------------------------- 1 | name: Deploy GitHub pages 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | 7 | jobs: 8 | deploy: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout code 12 | uses: actions/checkout@v2 13 | 14 | - name: Setup Node.js 15 | uses: actions/setup-node@v4 16 | with: 17 | node-version: 20 18 | 19 | - name: Install dependencies 20 | run: npm install 21 | working-directory: doc 22 | 23 | - name: Build site 24 | run: npm run build 25 | working-directory: doc 26 | 27 | - name: Add CNAME file 28 | run: cp ./CNAME ./doc/build/CNAME 29 | 30 | - name: Deploy to GitHub Pages 31 | uses: peaceiris/actions-gh-pages@v3 32 | with: 33 | github_token: ${{ secrets.GITHUB_TOKEN }} 34 | publish_dir: ./doc/build -------------------------------------------------------------------------------- /.github/workflows/flutter-analyze.yaml: -------------------------------------------------------------------------------- 1 | name: Flutter analyze 2 | 3 | on: 4 | pull_request: 5 | branches: [main, dev] 6 | 7 | jobs: 8 | flutter-analyze: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | 13 | - name: Setup Flutter environment 14 | uses: subosito/flutter-action@v2 15 | with: 16 | channel: 'stable' 17 | 18 | - name: Install dependencies 19 | run: flutter pub get 20 | 21 | - name: Analyze formatting 22 | run: dart format . --set-exit-if-changed 23 | 24 | - name: Analyze project source 25 | run: flutter analyze --no-pub 26 | 27 | - name: Install pana 28 | run: dart pub global activate pana 29 | 30 | - name: Analyze project source with pana 31 | run: pana 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.lock 4 | *.log 5 | *.pyc 6 | *.swp 7 | .DS_Store 8 | .atom/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # Visual Studio Code related 20 | .classpath 21 | .project 22 | .settings/ 23 | .vscode/ 24 | 25 | # Flutter repo-specific 26 | /bin/cache/ 27 | /bin/internal/bootstrap.bat 28 | /bin/internal/bootstrap.sh 29 | /bin/mingit/ 30 | /dev/benchmarks/mega_gallery/ 31 | /dev/bots/.recipe_deps 32 | /dev/bots/android_tools/ 33 | /dev/devicelab/ABresults*.json 34 | /dev/docs/doc/ 35 | /dev/docs/flutter.docs.zip 36 | /dev/docs/lib/ 37 | /dev/docs/pubspec.yaml 38 | /dev/integration_tests/**/xcuserdata 39 | /dev/integration_tests/**/Pods 40 | /packages/flutter/coverage/ 41 | version 42 | analysis_benchmark.json 43 | 44 | # packages file containing multi-root paths 45 | .packages.generated 46 | 47 | # Flutter/Dart/Pub related 48 | **/doc/api/ 49 | .dart_tool/ 50 | .flutter-plugins 51 | .flutter-plugins-dependencies 52 | **/generated_plugin_registrant.dart 53 | .packages 54 | .pub-cache/ 55 | .pub/ 56 | build/ 57 | flutter_*.png 58 | linked_*.ds 59 | unlinked.ds 60 | unlinked_spec.ds 61 | 62 | # Android related 63 | **/android/**/gradle-wrapper.jar 64 | **/android/.gradle 65 | **/android/captures/ 66 | **/android/gradlew 67 | **/android/gradlew.bat 68 | **/android/local.properties 69 | **/android/**/GeneratedPluginRegistrant.java 70 | **/android/key.properties 71 | *.jks 72 | 73 | # iOS/XCode related 74 | **/ios/**/*.mode1v3 75 | **/ios/**/*.mode2v3 76 | **/ios/**/*.moved-aside 77 | **/ios/**/*.pbxuser 78 | **/ios/**/*.perspectivev3 79 | **/ios/**/*sync/ 80 | **/ios/**/.sconsign.dblite 81 | **/ios/**/.tags* 82 | **/ios/**/.vagrant/ 83 | **/ios/**/DerivedData/ 84 | **/ios/**/Icon? 85 | **/ios/**/Pods/ 86 | **/ios/**/.symlinks/ 87 | **/ios/**/profile 88 | **/ios/**/xcuserdata 89 | **/ios/.generated/ 90 | **/ios/Flutter/.last_build_id 91 | **/ios/Flutter/App.framework 92 | **/ios/Flutter/Flutter.framework 93 | **/ios/Flutter/Flutter.podspec 94 | **/ios/Flutter/Generated.xcconfig 95 | **/ios/Flutter/ephemeral 96 | **/ios/Flutter/app.flx 97 | **/ios/Flutter/app.zip 98 | **/ios/Flutter/flutter_assets/ 99 | **/ios/Flutter/flutter_export_environment.sh 100 | **/ios/ServiceDefinitions.json 101 | **/ios/Runner/GeneratedPluginRegistrant.* 102 | 103 | # macOS 104 | **/macos/Flutter/GeneratedPluginRegistrant.swift 105 | 106 | # Coverage 107 | coverage/ 108 | 109 | # Symbols 110 | app.*.symbols 111 | 112 | # Exceptions to above rules. 113 | !**/ios/**/default.mode1v3 114 | !**/ios/**/default.mode2v3 115 | !**/ios/**/default.pbxuser 116 | !**/ios/**/default.perspectivev3 117 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 118 | !/dev/ci/**/Gemfile.lock 119 | !**/ios/**/Podfile.lock 120 | 121 | # Test scripts files 122 | test-scripts -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 84a1e904f44f9b0e9c4510138010edcc653163f8 8 | channel: stable 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [1.5.0] - 2025-02-06 2 | #### [@rickypid](https://github.com/rickypid) 3 | 4 | ### Dependencies 5 | 6 | * Upgrade supabase_flutter: ^2.8.3 7 | 8 | ## [1.4.3] - 2024-12-26 9 | #### [@rickypid](https://github.com/rickypid) 10 | #### [@lazy-geek](https://github.com/lazy-geek) 11 | 12 | ℹ️ ℹ️ **Recommended scheme migration** ℹ️ ℹ️ 13 | 14 | Improvement documentations. 15 | 16 | ### Fixed 17 | 18 | * Fixed #25 Update chats.update_last_messages trigger to make sure it is only called for actual last message in case of update 19 | * Fixed #27 Error in room or message deletion query, a `limit(1)` was incorrectly applied 20 | 21 | ## [1.4.2] - 2024-11-30 22 | #### [@rickypid](https://github.com/rickypid) 23 | 24 | ### Dependencies 25 | 26 | * Upgrade mine: '>=1.0.2 <3.0.0' 27 | 28 | ## [1.4.1] - 2024-11-21 29 | #### [@rickypid](https://github.com/rickypid) 30 | #### [@Parfyonator](https://github.com/Parfyonator) 31 | 32 | ### Fixed 33 | 34 | * Fixed #20 Chat creator role is null instead of admin 35 | 36 | ## [1.4.0] - 2024-11-21 37 | #### [@rickypid](https://github.com/rickypid) 38 | 39 | ⚠️⚠️ **Need schema migration** ⚠️⚠️ 40 | 41 | ### Improvements 42 | 43 | * Now when we get the rooms the `rooms_l` view is used so that we can get all the information without having to do multiple queries 44 | 45 | ### Fixed 46 | 47 | * Fixed #20 Chat creator role is null instead of admin 48 | * Fixed online user status realtime subscription 49 | 50 | ## [1.3.2] - 2024-11-13 51 | #### [@rickypid](https://github.com/rickypid) 52 | 53 | Improve documentations 54 | 55 | ### Fixed 56 | 57 | * Fixed schema `chats` permission after view creation 58 | 59 | ## [1.3.1] - 2024-11-12 60 | #### [@rickypid](https://github.com/rickypid) 61 | 62 | ### Fixed 63 | 64 | * Fixed `updateRoomList` sorting 65 | 66 | ## [1.3.0] - 2024-11-04 67 | #### [@rickypid](https://github.com/rickypid) 68 | 69 | ⚠️⚠️ **Some Breaking Changes** ⚠️⚠️ 70 | 71 | ### New features 72 | 73 | * Added Rooms list pagination and searchable 74 | 75 | ### Fixed 76 | 77 | * Security fix on RLS helper functions 78 | 79 | ## [1.2.0] - 2024-10-31 80 | #### [@rickypid](https://github.com/rickypid) 81 | 82 | ⚠️⚠️ **Some Breaking Changes** ⚠️⚠️ 83 | 84 | ### New features 85 | 86 | * Added Users list pagination and searchable 87 | * Added Users typing status 88 | 89 | ### Fixed 90 | 91 | * Fix database index e security fix on function 92 | 93 | ## [1.1.0] - 2024-08-30 94 | #### [@danbeech](https://github.com/danbeech) 95 | #### [@rickypid](https://github.com/rickypid) 96 | 97 | ### Fixed 98 | 99 | * Fixed update room call 100 | * Fixed Dart SDK version for Flutter 3.22.x (#9) 101 | * Updated dependencies 102 | 103 | ## [1.0.0] - 2024-06-19 104 | #### [@rickypid](https://github.com/rickypid) 105 | 106 | ### Fixed 107 | 108 | * Fixed schema migration (#3) 109 | * Fixed example theme variables, now support Flutter > 3.22.x 110 | 111 | ## [0.10.0] - 2024-04-04 112 | #### [@rickypid](https://github.com/rickypid) 113 | 114 | ### Features 115 | 116 | * Added user online status support 117 | * Added room messages pagination, now it's possible load messages on chat scrolling 118 | * Added `SupabaseChatController` 119 | 120 | ### Widgets 121 | 122 | * Added `UserOnlineStateObserver` widget 123 | * Added `UserOnlineStatusWidget` widget 124 | 125 | ### Dependencies 126 | 127 | * Removed `dio` dependency 128 | * Upgraded `supabase_flutter` to `^2.4.0` 129 | 130 | ## [0.9.0] - 2024-03-11 131 | #### [@rickypid](https://github.com/rickypid) 132 | 133 | * Improved documentation 134 | * Fixed RSL on chats schema 135 | * Add files download support 136 | * Test on Android device 137 | * Renamed `SupabseChatCore` to `SupabaseChatCore` 😅 (typo) 138 | * Added logic to change status in read messages 139 | * Dependencies updated 140 | 141 | ## [0.0.2] - 2024-02-12 142 | #### [@rickypid](https://github.com/rickypid) 143 | 144 | * Improved documentation and fixed GitHub Pages CI 145 | 146 | ## [0.0.1] - 2024-02-09 147 | #### [@rickypid](https://github.com/rickypid) 148 | 149 | * Initial release 150 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | flutter-supabase-chat-core.insideapp.it -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Flyer Chat 2 | 3 | Thank you for your interest in contributing to Flyer Chat! Our vision is to create an easy-to-use chat experience for any application and while we can't have everything right from the start, we can build it together! If you plan to implement a feature and open a pull request, make sure you don't spend a lot of time on it, otherwise open an issue so we can discuss bigger stuff. 4 | 5 | The [Open Source Guides](https://opensource.guide) website has a collection of resources for individuals, communities, and companies who want to learn how to run and contribute to an open-source project. Contributors and people new to open source alike will find the following guides especially useful: 6 | 7 | * [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/) 8 | * [Building Welcoming Communities](https://opensource.guide/building-community/) 9 | 10 | ## Helping with Documentation 11 | 12 | We use [Docusaurus](https://docusaurus.io) to build our documentation which is located in the [doc](https://github.com/insideapp-srl/flutter_supabase_chat_core/tree/main/doc) folder. If you are adding new functionality or introducing a behavior change, we will ask you to update the documentation to reflect your changes. 13 | 14 | ## Contributing Code 15 | 16 | Code-level contributions to Flyer Chat generally come in the form of pull requests. These are done by forking the repo and making changes locally. Directly in the repo, there is the [example](https://github.com/insideapp-srl/flutter_supabase_chat_core/tree/main/example) project that you can install on your device (or simulators) and use to test the changes you're making to Flyer Chat sources. 17 | 18 | [Here](https://opensource.guide/how-to-contribute/#opening-a-pull-request) you can read how to open a pull request. 19 | 20 | ## Community Contributions 21 | 22 | Contributions to Flyer Chat are not limited to GitHub. You can help others by sharing your experience using Flyer Chat, whether that is through blog posts, presenting talks at conferences, or simply sharing your thoughts on social media. 23 | 24 | ## License 25 | 26 | By contributing to Flyer Chat, you agree that your contributions will be licensed under its [Apache License, Version 2.0](LICENSE). -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | 3 | linter: 4 | rules: 5 | - always_declare_return_types 6 | - avoid_unused_constructor_parameters 7 | - directives_ordering 8 | - omit_local_variable_types 9 | - prefer_expression_function_bodies 10 | - prefer_final_fields 11 | - prefer_final_locals 12 | - prefer_relative_imports 13 | - prefer_single_quotes 14 | - sized_box_for_whitespace 15 | - sort_child_properties_last 16 | - sort_pub_dependencies 17 | - type_annotate_public_apis 18 | - unawaited_futures 19 | - use_named_constants 20 | - use_super_parameters 21 | - require_trailing_commas 22 | 23 | -------------------------------------------------------------------------------- /doc/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /doc/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ yarn 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ yarn start 15 | ``` 16 | 17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ### Build 20 | 21 | ``` 22 | $ yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ### Deployment 28 | 29 | Using SSH: 30 | 31 | ``` 32 | $ USE_SSH=true yarn deploy 33 | ``` 34 | 35 | Not using SSH: 36 | 37 | ``` 38 | $ GIT_USER= yarn deploy 39 | ``` 40 | 41 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 42 | -------------------------------------------------------------------------------- /doc/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /doc/docs/guides/supabse-indexes.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: supabase-indexes 3 | title: Database Indexes 4 | --- 5 | 6 | ## `chats.messages` indexes 7 | 8 | These indexes are added to improve the performance of foreign keys in database tables: 9 | 10 | ```sql 11 | CREATE INDEX ON "chats"."messages" USING btree ("authorId"); 12 | CREATE INDEX ON "chats"."messages" USING btree ("roomId"); 13 | ``` 14 | -------------------------------------------------------------------------------- /doc/docs/guides/supabse-trigges.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: supabase-triggers 3 | title: Database Triggers 4 | --- 5 | 6 | ## Update room last message 7 | 8 | This is a trigger that sets room's `lastMessages` to the most recent message sent once recieved in Supabase. 9 | 10 | ```sql 11 | CREATE OR REPLACE FUNCTION chats.update_last_messages() 12 | RETURNS TRIGGER 13 | SET search_path = '' 14 | AS $$ 15 | DECLARE 16 | ts_in_milliseconds bigint; 17 | BEGIN 18 | SELECT EXTRACT(epoch FROM NOW()) * 1000 INTO ts_in_milliseconds; 19 | UPDATE chats.rooms 20 | SET "updatedAt" = ts_in_milliseconds, 21 | "lastMessages" = jsonb_build_array(NEW) 22 | WHERE id = NEW."roomId"; 23 | RETURN NEW; 24 | END; 25 | $$ LANGUAGE plpgsql; 26 | 27 | drop trigger if exists update_last_messages_trigger on chats.messages; 28 | CREATE TRIGGER update_last_messages_trigger 29 | AFTER INSERT OR UPDATE ON chats.messages 30 | FOR EACH ROW 31 | EXECUTE FUNCTION chats.update_last_messages(); 32 | ``` 33 | 34 | ## Set message status to sent 35 | 36 | "This trigger, on the other hand, is responsible for setting the message status to `sent` when it is added to the `messages` table: 37 | 38 | ```sql 39 | CREATE OR REPLACE FUNCTION chats.set_message_status_to_sent() 40 | RETURNS TRIGGER 41 | SET search_path = '' 42 | AS $$ 43 | BEGIN 44 | NEW.status := 'sent'; 45 | RETURN NEW; 46 | END; 47 | $$ LANGUAGE plpgsql; 48 | 49 | drop trigger if exists update_status_before_insert on chats.messages; 50 | CREATE TRIGGER update_status_before_insert 51 | BEFORE INSERT ON chats.messages 52 | FOR EACH ROW EXECUTE FUNCTION chats.set_message_status_to_sent(); 53 | ``` 54 | 55 | ## Handle new user from `auth.users` 56 | 57 | "This trigger, is responsible for replicate `auth.users` table rows in `chats.users` table, this is to avoid exposing user data : 58 | 59 | ```sql 60 | 61 | CREATE OR REPLACE FUNCTION chats.handle_new_user() 62 | RETURNS trigger 63 | LANGUAGE 'plpgsql' 64 | COST 100 65 | VOLATILE NOT LEAKPROOF SECURITY DEFINER 66 | SET search_path=public 67 | SET search_path = '' 68 | AS $BODY$ 69 | DECLARE 70 | ts_in_milliseconds bigint; 71 | BEGIN 72 | SELECT EXTRACT(epoch FROM NOW()) * 1000 INTO ts_in_milliseconds; 73 | insert into chats.users (id, "createdAt", "updatedAt", "lastSeen") 74 | values (new.id, ts_in_milliseconds, ts_in_milliseconds, ts_in_milliseconds); 75 | return new; 76 | end; 77 | $BODY$; 78 | 79 | drop trigger if exists on_auth_user_created on auth.users; 80 | create trigger on_auth_user_created 81 | after insert on auth.users 82 | for each row execute procedure chats.handle_new_user(); 83 | 84 | ``` -------------------------------------------------------------------------------- /doc/docs/guides/supabse-views.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: supabase-views 3 | title: Database Views 4 | --- 5 | 6 | ## Rooms view 7 | 8 | This is a view of `rooms` table, this view allows you to obtain the name of the sender of the message dynamically in direct rooms, based on the logged-in user the name of the correspondent is displayed, it is also included the list of uses member objects. 9 | 10 | ```sql 11 | create or replace view chats.rooms_l 12 | with (security_invoker='on') as 13 | select r.id, 14 | r."imageUrl", 15 | r.metadata, 16 | case 17 | when r.type = 'direct' and auth.uid() is not null then 18 | (select coalesce(u."firstName", '') || ' ' || coalesce(u."lastName", '') 19 | from chats.users u 20 | where u.id = any (r."userIds") 21 | and u.id <> auth.uid() 22 | limit 1) 23 | else 24 | r.name 25 | end as name, 26 | r.type, 27 | r."userIds", 28 | r."lastMessages", 29 | r."userRoles", 30 | r."createdAt", 31 | r."updatedAt", 32 | (select jsonb_agg(to_jsonb(u)) 33 | from chats.users u 34 | where u.id = any (r."userIds")) as users 35 | from chats.rooms r; 36 | ``` 37 | 38 | 39 | ## Messages view 40 | 41 | This is a view of `messages` table, this view allows you to obtain the author user object and room object. 42 | 43 | ```sql 44 | create or replace view chats.messages_l 45 | with (security_invoker='on') as 46 | select m.id, 47 | m."createdAt", 48 | m.metadata, 49 | m.duration, 50 | m."mimeType", 51 | m.name, 52 | m."remoteId", 53 | m."repliedMessage", 54 | m."roomId", 55 | m."showStatus", 56 | m.size, 57 | m.status, 58 | m.type, 59 | m."updatedAt", 60 | m.uri, 61 | m."waveForm", 62 | m."isLoading", 63 | m.height, 64 | m.width, 65 | m."previewData", 66 | m."authorId", 67 | m.text, 68 | to_jsonb(u) as author, 69 | to_jsonb(r) as room 70 | from chats.messages m 71 | left join chats.users u on u.id = m."authorId" 72 | left join chats.rooms_l r on r.id = m."roomId"; 73 | ``` -------------------------------------------------------------------------------- /doc/docs/introduction/supabase-overview.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: supabase-overview 3 | title: Overview 4 | slug: / 5 | --- 6 | 7 | Have you ever wanted to implement a chat in your application? Do you think it's difficult and complex? 8 | 9 | Try this, thanks to the magnificent [Supabase](https://supabase.com/) platform and the [Flutter Chat UI](https://pub.dev/packages/flutter_chat_ui) package, you can achieve it in just a few minutes and effortlessly. 10 | 11 | --- 12 | 13 | Flyer Chat is a platform for creating in-app chat experiences using Flutter or React Native. This is the documentation for Supabase BaaS implementation for Flutter. 14 | 15 | ## How it works? 16 | 17 | We use [Supabase](https://supabase.com/docs) as the backend. There we have three tables, `rooms`, `messages` and `users`. Let's break them down: 18 | 19 | * `rooms` table contains all conversations in your app. [Postgres Row Security Level](https://supabase.com/docs/guides/database/postgres/row-level-security) are responsible for showing only those rooms where the user's in. The room contains some metadata, a participants list and other room information. 20 | * `messages` table contains all messages in your app. [Postgres Row Security Level](https://supabase.com/docs/guides/database/postgres/row-level-security) are responsible for showing only those messages of the rooms where the user's in. The massage contains all message data. 21 | * `users` table contains users data, such as avatars and names. You can use this collection to render a list of users available for chat. This table is populate automatically by trigger. 22 | 23 | Both array of participant IDs in the `room` and rows in `users` tables use `User UID` from [Supabase Authentication](https://supabase.com/docs/guides/auth) as an ID, for easier navigation through the data. That means every user of the chat should be registered using Supabase's authentication module, but if your app doesn't use Supabase as an authentication provider, you still can register in Supabase by providing your `JWT` token, so you can have your backend working together with a chat on Supabase (in this case it will be necessary to remove the trigger that inserts users). 24 | 25 | [Postgres Triggers](https://supabase.com/docs/guides/database/postgres/triggers) can be used for setting message's statuses and for triggering push notifications. In this documentation, you will see an [example](../guides/supabse-trigges.md) of setting `delivered` message status. 26 | 27 | [Supabase Storage](https://supabase.com/docs/guides/storage) can be used as a storage provider for images and files. 28 | 29 | ## Motivation 30 | 31 | Ever estimated a simple chat for weeks of work? Didn't want to start because it is always the same boring work for an extended period of time? Was it moved to post MVP because of lack of time and resources? Were you left with a frustrated client, who couldn't understand why the thing that exists in almost every app takes that much time to implement? 32 | 33 | We are trying to solve all this by working on a Flyer Chat. 34 | 35 | You will say that there are libraries out there that will help create chats, but we are working on a more complete solution - very similar on completely different platforms like React Native and Flutter (we don't always work in just one) with an optional Supabase BaaS, since chat is not only UI. We are making this free and open-source so together we can create a product that works for everyone. 36 | -------------------------------------------------------------------------------- /doc/docs/introduction/supabase-package-installation.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: supabase-package-installation 3 | title: Installation supabase_flutter 4 | --- 5 | 6 | This library depends on [supabase_flutter](https://pub.dev/packages/supabase_flutter). Follow the instructions there to configure the Supabase project and install [supabase_flutter](https://supabase.com/docs/reference/dart/introduction) plugin. 7 | 8 | Add `flutter_supabase_chat_core` to your package's `pubspec.yaml` file. Check current version on [pub.dev](https://pub.dev/packages/flutter_supabase_chat_core/install). -------------------------------------------------------------------------------- /doc/docs/introduction/supabase-project-configuration.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: supabase-project-configuration 3 | title: Supabase project preparation 4 | --- 5 | 6 | ### Create a Supabase project 7 | 8 | 1. Install Supabase CLI: [Official documentation](https://supabase.com/docs/guides/cli/getting-started) 9 | 2. Open your bash 10 | 3. Login with Supabase: 11 | 12 | ```bash 13 | supabase login 14 | ``` 15 | 16 | 4. Create new project (For example `demo-chat`): 17 | 18 | ```bash 19 | supabase projects create demo-chat 20 | ``` 21 | 22 | 5. Select your organization 23 | 6. Select an region 24 | 7. Insert a secure password for new Postgres database (Save this in a secure location) 25 | 8. Obtain your `REFERENCE ID` (After command select your project, for example `demo-chat`): 26 | 27 | ```bash 28 | supabase projects list 29 | ``` 30 | 31 | 9. Obtain your `anon` key (After command select your project, for example `demo-chat`): 32 | 33 | ```bash 34 | supabase projects api-keys 35 | ``` 36 | 10. Edit `example project` file `example/lib/supabase_options.dart`, insert your project `{{your_project_reference_id}}` and `{{supabase_anon_key}}` 37 | 38 | #### Prepare Supabase project 39 | 40 | Inside the example project (`example/utils`) there is a script, running the latter will automatically configure the Supabase project, creating tables, security rules, buckets and everything that is necessary for the example project to function. 41 | 42 | In order to run the script you need to be aware of the following information about your Supabase project: 43 | 44 | - `host` : Project host 45 | - `port` : Database port 46 | - `database` : Database name 47 | - `user` : Database user 48 | - `password` : Database password 49 | 50 | This information, with the exception of the password which is provided only during the creation of the database (if necessary, you can use the password reset function of your database to obtain it), can be found very easily from the Dashboard of your Supabase project: 51 | 52 | ![Supabase dashboard database info](/img/supabase-project-credential.png "Database info") 53 | 54 | #### Running prepare script 55 | 56 | Below are the commands for running the scripts (During execution you will be asked for the password for your database user): 57 | 58 | > Required `psql` installed -> [Official documentation](https://www.postgresql.org/download/) 59 | 60 | #### Linux 61 | 62 | ```bash 63 | cd ./example/utils/ 64 | ./prepare.sh -h "your-postgres-host" -p your-postgres-port -d "your-postgres-database-name" -U "your-postgres-user" 65 | ``` 66 | 67 | #### Windows 68 | 69 | ```powershell 70 | cd .\example\utils\ 71 | .\prepare.ps1 -hostname "your-postgres-host" -port your-postgres-port -database "your-postgres-database-name" -user "your-postgres-user" 72 | ``` 73 | 74 | 75 | after running the database preparation script. you need to change the database schema exposure setting by adding the `chats` schema (from the supabase dashboard): 76 | 77 | ![Supabase dashboard schema exposure setting](/img/supabase-schema-exposure-setting.png "Schema exposure setting") 78 | 79 | Optional (**Only for test**): Disable email verification and save the configuration (To speed up testing and allow user registration in just one click, it is advisable to disable mailbox verification): 80 | 81 | ![Supabase dashboard disable email verification](/img/supabase-disable-confirm-email.png "Disable email verification") 82 | 83 | Read our [documentation](https://flutter-supabase-chat-core.insideapp.it) or see the [example](https://github.com/insideapp-srl/flutter_supabase_chat_core/tree/main/example) project. To run the example project you need to have your own [Supabase](https://supabase.com/dashboard/projects) project and then follow [Add Supabase to your Flutter app](https://supabase.com/docs/reference/dart/initializing), override `example/lib/supabase_options.dart`, don't commit it though 😉 84 | 85 | After all of this is done you will need to register a couple of users and the example app will automatically suggest email and password on the register screen, default password is `Qawsed1-`. To set up [Supabase Security Rules](https://supabase.com/docs/guides/database/postgres/row-level-security) so users can see only the data they should see, continue with our [documentation](https://flutter-supabase-chat-core.insideapp.it/). 86 | -------------------------------------------------------------------------------- /doc/docusaurus.config.ts: -------------------------------------------------------------------------------- 1 | import {themes as prismThemes} from 'prism-react-renderer'; 2 | import type {Config} from '@docusaurus/types'; 3 | import type * as Preset from '@docusaurus/preset-classic'; 4 | 5 | const config: Config = { 6 | title: 'Flutter Supabase Chat Core', 7 | tagline: 'Dinosaurs are cool', 8 | favicon: 'img/favicon.ico', 9 | 10 | // Set the production url of your site here 11 | url: 'https://flutter-supabase-chat-core.insideapp.it', 12 | // Set the // pathname under which your site is served 13 | // For GitHub pages deployment, it is often '//' 14 | baseUrl: '/', 15 | 16 | // GitHub pages deployment config. 17 | // If you aren't using GitHub pages, you don't need these. 18 | organizationName: 'insideapp-srl', // Usually your GitHub org/user name. 19 | projectName: 'flutter_supabase_chat_core', // Usually your repo name. 20 | 21 | onBrokenLinks: 'throw', 22 | onBrokenMarkdownLinks: 'warn', 23 | 24 | // Even if you don't use internationalization, you can use this field to set 25 | // useful metadata like html lang. For example, if your site is Chinese, you 26 | // may want to replace "en" with "zh-Hans". 27 | i18n: { 28 | defaultLocale: 'en', 29 | locales: ['en'], 30 | }, 31 | presets: [ 32 | [ 33 | 'classic', 34 | { 35 | docs: { 36 | sidebarPath: './sidebars.ts', 37 | editUrl: 38 | 'https://github.com/insideapp-srl/flutter_supabase_chat_core', 39 | routeBasePath: '/', 40 | }, 41 | 42 | theme: { 43 | customCss: './src/css/custom.css', 44 | }, 45 | blog: false, 46 | } satisfies Preset.Options, 47 | ], 48 | ], 49 | 50 | themeConfig: { 51 | // Replace with your project's social card 52 | image: 'img/social-card.png', 53 | colorMode: { 54 | defaultMode: 'dark', 55 | disableSwitch: false, 56 | respectPrefersColorScheme: false, 57 | }, 58 | navbar: { 59 | title: 'Flutter Supabase Chat Core', 60 | logo: { 61 | alt: 'Flyer Chat Logo', 62 | src: 'img/logo.svg', 63 | }, 64 | items: [ 65 | { 66 | type: 'docSidebar', 67 | sidebarId: 'tutorialSidebar', 68 | position: 'left', 69 | label: 'Tutorial', 70 | }, 71 | {to: 'https://docs.flyer.chat/flutter/chat-ui/', label: 'Chat UI', position: 'left'}, 72 | { 73 | to: 'https://docs.flyer.chat/flutter/firebase/firebase-overview', 74 | label: 'Firebase Core', 75 | position: 'left' 76 | }, 77 | { 78 | href: 'https://github.com/insideapp-srl/flutter_supabase_chat_core', 79 | label: 'GitHub', 80 | position: 'right', 81 | }, 82 | ], 83 | }, 84 | footer: { 85 | style: 'dark', 86 | links: [ 87 | { 88 | title: 'Docs', 89 | items: [ 90 | { 91 | label: 'How use', 92 | to: '/', 93 | }, 94 | ], 95 | }, 96 | { 97 | title: 'Community', 98 | items: [ 99 | { 100 | label: 'Site', 101 | href: 'https://www.insideapp.it/', 102 | }, 103 | { 104 | label: 'GitHub', 105 | href: 'https://github.com/insideapp-srl/flutter_supabase_chat_core', 106 | }, 107 | ], 108 | }, 109 | { 110 | title: 'More', 111 | items: [ 112 | { 113 | label: 'Blog', 114 | to: 'https://blog.insideapp.it/', 115 | }, 116 | ], 117 | }, 118 | ], 119 | copyright: `Copyright © ${new Date().getFullYear()} My Project, Inc. Built with Docusaurus.`, 120 | }, 121 | prism: { 122 | theme: prismThemes.github, 123 | darkTheme: prismThemes.dracula, 124 | }, 125 | } satisfies Preset.ThemeConfig, 126 | }; 127 | 128 | export default config; 129 | -------------------------------------------------------------------------------- /doc/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flutter-supabase-chat-core", 3 | "version": "1.4.3", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids", 15 | "typecheck": "tsc" 16 | }, 17 | "dependencies": { 18 | "@docusaurus/core": "3.6.3", 19 | "@docusaurus/preset-classic": "3.6.3", 20 | "@mdx-js/react": "^3.1.0", 21 | "clsx": "^2.1.1", 22 | "prism-react-renderer": "^2.4.1", 23 | "react": "^18.3.1", 24 | "react-dom": "^18.3.1" 25 | }, 26 | "devDependencies": { 27 | "@docusaurus/module-type-aliases": "3.6.3", 28 | "@docusaurus/tsconfig": "3.6.3", 29 | "@docusaurus/types": "3.6.3", 30 | "typescript": "~5.7.2" 31 | }, 32 | "browserslist": { 33 | "production": [ 34 | ">0.5%", 35 | "not dead", 36 | "not op_mini all" 37 | ], 38 | "development": [ 39 | "last 3 chrome version", 40 | "last 3 firefox version", 41 | "last 5 safari version" 42 | ] 43 | }, 44 | "engines": { 45 | "node": ">=18.0" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /doc/sidebars.ts: -------------------------------------------------------------------------------- 1 | import type {SidebarsConfig} from '@docusaurus/plugin-content-docs'; 2 | 3 | /** 4 | * Creating a sidebar enables you to: 5 | - create an ordered group of docs 6 | - render a sidebar for each doc of that group 7 | - provide next/previous navigation 8 | 9 | The sidebars can be generated from the filesystem, or explicitly defined here. 10 | 11 | Create as many sidebars as you want. 12 | */ 13 | const sidebars: SidebarsConfig = { 14 | // By default, Docusaurus generates a sidebar from the docs folder structure 15 | //tutorialSidebar: [{type: 'autogenerated', dirName: '.'}], 16 | 17 | // But you can create a sidebar manually 18 | 19 | tutorialSidebar: [ 20 | { 21 | type: 'category', 22 | label: 'Introduction', 23 | items: [ 24 | 'introduction/supabase-overview', 25 | 'introduction/supabase-project-configuration', 26 | 'introduction/supabase-package-installation', 27 | ], 28 | }, 29 | { 30 | type: 'category', 31 | label: 'Guides', 32 | items: [ 33 | 'guides/supabase-usage', 34 | 'guides/supabase-security', 35 | 'guides/supabase-triggers', 36 | 'guides/supabase-views', 37 | 'guides/supabase-indexes', 38 | ], 39 | }, 40 | ], 41 | 42 | }; 43 | 44 | export default sidebars; 45 | -------------------------------------------------------------------------------- /doc/src/css/custom.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Any CSS included here will be global. The classic template 3 | * bundles Infima by default. Infima is a CSS framework designed to 4 | * work well for content-centric websites. 5 | */ 6 | 7 | /* You can override the default Infima variables here. */ 8 | :root { 9 | --ifm-color-primary: #2e8555; 10 | --ifm-color-primary-dark: #29784c; 11 | --ifm-color-primary-darker: #277148; 12 | --ifm-color-primary-darkest: #205d3b; 13 | --ifm-color-primary-light: #33925d; 14 | --ifm-color-primary-lighter: #359962; 15 | --ifm-color-primary-lightest: #3cad6e; 16 | --ifm-code-font-size: 95%; 17 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); 18 | } 19 | 20 | /* For readability concerns, you should choose a lighter palette in dark mode. */ 21 | [data-theme='dark'] { 22 | --ifm-color-primary: #25c2a0; 23 | --ifm-color-primary-dark: #21af90; 24 | --ifm-color-primary-darker: #1fa588; 25 | --ifm-color-primary-darkest: #1a8870; 26 | --ifm-color-primary-light: #29d5b0; 27 | --ifm-color-primary-lighter: #32d8b4; 28 | --ifm-color-primary-lightest: #4fddbf; 29 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); 30 | } 31 | -------------------------------------------------------------------------------- /doc/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/doc/static/.nojekyll -------------------------------------------------------------------------------- /doc/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/doc/static/img/favicon.ico -------------------------------------------------------------------------------- /doc/static/img/flayer_chat_ui_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/doc/static/img/flayer_chat_ui_screenshot.png -------------------------------------------------------------------------------- /doc/static/img/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /doc/static/img/social-card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/doc/static/img/social-card.png -------------------------------------------------------------------------------- /doc/static/img/supabase-disable-confirm-email.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/doc/static/img/supabase-disable-confirm-email.png -------------------------------------------------------------------------------- /doc/static/img/supabase-project-credential.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/doc/static/img/supabase-project-credential.png -------------------------------------------------------------------------------- /doc/static/img/supabase-schema-exposure-setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/doc/static/img/supabase-schema-exposure-setting.png -------------------------------------------------------------------------------- /doc/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // This file is not used in compilation. It is here just for a nice editor experience. 3 | "extends": "@docusaurus/tsconfig", 4 | "compilerOptions": { 5 | "baseUrl": "." 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | **/doc/api/ 26 | **/ios/Flutter/.last_build_id 27 | .dart_tool/ 28 | .flutter-plugins 29 | .flutter-plugins-dependencies 30 | .packages 31 | .pub-cache/ 32 | .pub/ 33 | /build/ 34 | 35 | # Symbolication related 36 | app.*.symbols 37 | 38 | # Obfuscation related 39 | app.*.map.json 40 | 41 | # Android Studio will place build artifacts here 42 | /android/app/debug 43 | /android/app/profile 44 | /android/app/release 45 | 46 | /lib/supabase_options_for_test.dart 47 | /lib/main_test.dart 48 | -------------------------------------------------------------------------------- /example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled. 5 | 6 | version: 7 | revision: 84a1e904f44f9b0e9c4510138010edcc653163f8 8 | channel: stable 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8 17 | base_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8 18 | - platform: android 19 | create_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8 20 | base_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8 21 | - platform: ios 22 | create_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8 23 | base_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8 24 | - platform: linux 25 | create_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8 26 | base_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8 27 | - platform: macos 28 | create_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8 29 | base_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8 30 | - platform: web 31 | create_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8 32 | base_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8 33 | - platform: windows 34 | create_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8 35 | base_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8 36 | 37 | # User provided section 38 | 39 | # List of Local paths (relative to this file) that should be 40 | # ignored by the migrate tool. 41 | # 42 | # Files that are not part of the templates will be ignored by default. 43 | unmanaged_files: 44 | - 'lib/main.dart' 45 | - 'ios/Runner.xcodeproj/project.pbxproj' 46 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # example 2 | 3 | A new Flutter project. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) 13 | 14 | For help getting started with Flutter development, view the 15 | [online documentation](https://docs.flutter.dev/), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | 3 | linter: 4 | rules: 5 | - always_declare_return_types 6 | - avoid_unused_constructor_parameters 7 | - directives_ordering 8 | - omit_local_variable_types 9 | - prefer_expression_function_bodies 10 | - prefer_final_fields 11 | - prefer_final_locals 12 | - prefer_relative_imports 13 | - prefer_single_quotes 14 | - sized_box_for_whitespace 15 | - sort_child_properties_last 16 | - sort_pub_dependencies 17 | - type_annotate_public_apis 18 | - unawaited_futures 19 | - use_named_constants 20 | - use_super_parameters 21 | - require_trailing_commas 22 | 23 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.application" 3 | id "kotlin-android" 4 | id "dev.flutter.flutter-gradle-plugin" 5 | } 6 | 7 | def localProperties = new Properties() 8 | def localPropertiesFile = rootProject.file('local.properties') 9 | if (localPropertiesFile.exists()) { 10 | localPropertiesFile.withReader('UTF-8') { reader -> 11 | localProperties.load(reader) 12 | } 13 | } 14 | 15 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 16 | if (flutterVersionCode == null) { 17 | flutterVersionCode = '1' 18 | } 19 | 20 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 21 | if (flutterVersionName == null) { 22 | flutterVersionName = '1.0' 23 | } 24 | 25 | android { 26 | namespace "com.example" 27 | compileSdk flutter.compileSdkVersion 28 | ndkVersion flutter.ndkVersion 29 | 30 | compileOptions { 31 | sourceCompatibility JavaVersion.VERSION_1_8 32 | targetCompatibility JavaVersion.VERSION_1_8 33 | } 34 | 35 | kotlinOptions { 36 | jvmTarget = '1.8' 37 | } 38 | 39 | sourceSets { 40 | main.java.srcDirs += 'src/main/kotlin' 41 | } 42 | 43 | defaultConfig { 44 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 45 | applicationId "com.example" 46 | // You can update the following values to match your application needs. 47 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. 48 | minSdkVersion flutter.minSdkVersion 49 | targetSdkVersion flutter.targetSdkVersion 50 | versionCode flutterVersionCode.toInteger() 51 | versionName flutterVersionName 52 | } 53 | 54 | buildTypes { 55 | release { 56 | // TODO: Add your own signing config for the release build. 57 | // Signing with the debug keys for now, so `flutter run --release` works. 58 | signingConfig signingConfigs.debug 59 | } 60 | } 61 | } 62 | 63 | flutter { 64 | source '../..' 65 | } 66 | 67 | dependencies {} 68 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 15 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 33 | 34 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/com/example/untitled1/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() 6 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | allprojects { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | } 7 | 8 | rootProject.buildDir = '../build' 9 | subprojects { 10 | project.buildDir = "${rootProject.buildDir}/${project.name}" 11 | } 12 | subprojects { 13 | project.evaluationDependsOn(':app') 14 | } 15 | 16 | tasks.register("clean", Delete) { 17 | delete rootProject.buildDir 18 | } 19 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx4G 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /example/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-7.6.3-all.zip 6 | -------------------------------------------------------------------------------- /example/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 | settings.ext.flutterSdkPath = flutterSdkPath() 10 | 11 | includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") 12 | 13 | repositories { 14 | google() 15 | mavenCentral() 16 | gradlePluginPortal() 17 | } 18 | } 19 | 20 | plugins { 21 | id "dev.flutter.flutter-plugin-loader" version "1.0.0" 22 | id "com.android.application" version "7.3.0" apply false 23 | id "org.jetbrains.kotlin.android" version "1.9.0" apply false 24 | } 25 | 26 | include ":app" 27 | -------------------------------------------------------------------------------- /example/assets/flyer_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/assets/flyer_logo.png -------------------------------------------------------------------------------- /example/assets/flyer_logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/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 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/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. -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Example 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | 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 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | CADisableMinimumFrameDurationOnPhone 45 | 46 | UIApplicationSupportsIndirectInputEvents 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_supabase_chat_core/flutter_supabase_chat_core.dart'; 3 | import 'package:supabase_flutter/supabase_flutter.dart'; 4 | 5 | import 'src/pages/home.dart'; 6 | import 'src/theme/color_schemes.dart'; 7 | import 'supabase_options.dart'; 8 | 9 | void main() async { 10 | WidgetsFlutterBinding.ensureInitialized(); 11 | await Supabase.initialize( 12 | url: supabaseOptions.url, 13 | anonKey: supabaseOptions.anonKey, 14 | ); 15 | runApp(const MyApp()); 16 | } 17 | 18 | class MyApp extends StatelessWidget { 19 | const MyApp({ 20 | super.key, 21 | }); 22 | 23 | @override 24 | Widget build(BuildContext context) => MaterialApp( 25 | title: 'Supabase Chat', 26 | debugShowCheckedModeBanner: false, 27 | theme: ThemeData( 28 | useMaterial3: true, 29 | colorScheme: lightColorScheme, 30 | ), 31 | darkTheme: ThemeData( 32 | useMaterial3: true, 33 | colorScheme: darkColorScheme, 34 | ), 35 | themeMode: ThemeMode.dark, 36 | home: const UserOnlineStateObserver( 37 | child: HomePage(), 38 | ), 39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /example/lib/src/class/message_status_ex.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_chat_types/flutter_chat_types.dart' as types; 3 | 4 | extension MessageStatusEx on types.Status { 5 | IconData get icon { 6 | switch (this) { 7 | case types.Status.delivered: 8 | return Icons.done_all; 9 | case types.Status.error: 10 | return Icons.error_outline; 11 | case types.Status.seen: 12 | return Icons.done_all; 13 | case types.Status.sending: 14 | return Icons.timelapse; 15 | case types.Status.sent: 16 | return Icons.done; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /example/lib/src/class/user_ex.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_chat_types/flutter_chat_types.dart' as types; 2 | 3 | extension UserEx on types.User { 4 | String getUserName() => firstName != null || lastName != null 5 | ? '${firstName ?? ''} ${lastName ?? ''}'.trim() 6 | : id; 7 | } 8 | -------------------------------------------------------------------------------- /example/lib/src/pages/auth.dart: -------------------------------------------------------------------------------- 1 | import 'package:faker/faker.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_chat_types/flutter_chat_types.dart' as types; 4 | import 'package:flutter_login/flutter_login.dart'; 5 | import 'package:flutter_supabase_chat_core/flutter_supabase_chat_core.dart'; 6 | import 'package:supabase_flutter/supabase_flutter.dart'; 7 | 8 | import 'home.dart'; 9 | 10 | class AuthScreen extends StatefulWidget { 11 | const AuthScreen({ 12 | super.key, 13 | }); 14 | 15 | @override 16 | State createState() => _AuthScreenState(); 17 | } 18 | 19 | class _AuthScreenState extends State { 20 | final faker = Faker(); 21 | 22 | @override 23 | Widget build(BuildContext context) => FlutterLogin( 24 | logo: const AssetImage('assets/flyer_logo.png'), 25 | savedEmail: faker.internet.email(), 26 | savedPassword: 'Qawsed1-', //faker.internet.password(), 27 | navigateBackAfterRecovery: true, 28 | additionalSignupFields: [ 29 | UserFormField( 30 | keyName: 'first_name', 31 | displayName: 'First name', 32 | defaultValue: faker.person.firstName(), 33 | fieldValidator: (value) { 34 | if (value == null || value == '') return 'Required'; 35 | return null; 36 | }, 37 | ), 38 | UserFormField( 39 | keyName: 'last_name', 40 | displayName: 'Last name', 41 | defaultValue: faker.person.lastName(), 42 | fieldValidator: (value) { 43 | if (value == null || value == '') return 'Required'; 44 | return null; 45 | }, 46 | ), 47 | ], 48 | passwordValidator: (value) { 49 | if (value!.isEmpty) { 50 | return 'Password is empty'; 51 | } 52 | return null; 53 | }, 54 | onLogin: (loginData) async { 55 | try { 56 | await Supabase.instance.client.auth.signInWithPassword( 57 | email: loginData.name, 58 | password: loginData.password, 59 | ); 60 | } catch (e) { 61 | return e.toString(); 62 | } 63 | return null; 64 | }, 65 | onSignup: (signupData) async { 66 | try { 67 | final response = await Supabase.instance.client.auth.signUp( 68 | email: signupData.name, 69 | password: signupData.password!, 70 | ); 71 | await SupabaseChatCore.instance.updateUser( 72 | types.User( 73 | firstName: signupData.additionalSignupData!['first_name'], 74 | id: response.user!.id, 75 | lastName: signupData.additionalSignupData!['last_name'], 76 | ), 77 | ); 78 | } catch (e) { 79 | return e.toString(); 80 | } 81 | return null; 82 | }, 83 | onSubmitAnimationCompleted: () { 84 | Navigator.of(context).pushReplacement( 85 | MaterialPageRoute( 86 | builder: (context) => const HomePage(), 87 | ), 88 | ); 89 | }, 90 | onRecoverPassword: (name) async { 91 | try { 92 | await Supabase.instance.client.auth.resetPasswordForEmail( 93 | name, 94 | ); 95 | } catch (e) { 96 | return e.toString(); 97 | } 98 | return null; 99 | }, 100 | initialAuthMode: AuthMode.signup, 101 | ); 102 | } 103 | -------------------------------------------------------------------------------- /example/lib/src/pages/home.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:supabase_flutter/supabase_flutter.dart'; 4 | 5 | import 'auth.dart'; 6 | import 'rooms.dart'; 7 | import 'users.dart'; 8 | 9 | class HomePage extends StatefulWidget { 10 | const HomePage({super.key}); 11 | 12 | @override 13 | State createState() => _HomePageState(); 14 | } 15 | 16 | class _HomePageState extends State { 17 | bool _error = false; 18 | bool _initialized = false; 19 | User? _user; 20 | 21 | @override 22 | void initState() { 23 | initializeSupabase(); 24 | super.initState(); 25 | } 26 | 27 | void initializeSupabase() async { 28 | try { 29 | Supabase.instance.client.auth.onAuthStateChange.listen((data) { 30 | setState(() { 31 | _user = data.session?.user; 32 | }); 33 | }); 34 | setState(() { 35 | _initialized = true; 36 | }); 37 | } catch (e) { 38 | setState(() { 39 | _error = true; 40 | }); 41 | } 42 | } 43 | 44 | void logout() async { 45 | await Supabase.instance.client.auth.signOut(); 46 | } 47 | 48 | @override 49 | Widget build(BuildContext context) { 50 | if (_error) { 51 | return Container(); 52 | } 53 | 54 | if (!_initialized) { 55 | return Container(); 56 | } 57 | 58 | return Scaffold( 59 | appBar: AppBar( 60 | actions: [ 61 | IconButton( 62 | icon: const Icon( 63 | Icons.add, 64 | ), 65 | onPressed: _user == null 66 | ? null 67 | : () { 68 | Navigator.of(context).push( 69 | MaterialPageRoute( 70 | fullscreenDialog: true, 71 | builder: (context) => const UsersPage(), 72 | ), 73 | ); 74 | }, 75 | ), 76 | ], 77 | leading: IconButton( 78 | icon: const Icon(Icons.logout), 79 | onPressed: _user == null ? null : logout, 80 | ), 81 | systemOverlayStyle: SystemUiOverlayStyle.light, 82 | title: const Text('Rooms'), 83 | ), 84 | body: _user == null 85 | ? Container( 86 | alignment: Alignment.center, 87 | margin: const EdgeInsets.only( 88 | bottom: 200, 89 | ), 90 | child: Column( 91 | mainAxisAlignment: MainAxisAlignment.center, 92 | children: [ 93 | const Text('Not authenticated'), 94 | TextButton( 95 | onPressed: () { 96 | Navigator.of(context).push( 97 | MaterialPageRoute( 98 | fullscreenDialog: true, 99 | builder: (context) => const AuthScreen(), 100 | ), 101 | ); 102 | }, 103 | child: const Text('Login'), 104 | ), 105 | ], 106 | ), 107 | ) 108 | : RoomsPage(), 109 | ); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /example/lib/src/pages/rooms.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_chat_types/flutter_chat_types.dart' as types; 3 | import 'package:flutter_supabase_chat_core/flutter_supabase_chat_core.dart'; 4 | import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; 5 | 6 | import '../widgets/room_tile.dart'; 7 | import 'room.dart'; 8 | 9 | class RoomsPage extends StatefulWidget { 10 | const RoomsPage({super.key}); 11 | 12 | @override 13 | State createState() => _RoomsPageState(); 14 | } 15 | 16 | class _RoomsPageState extends State { 17 | static const _pageSize = 20; 18 | String _filter = ''; 19 | 20 | final PagingController _controller = 21 | PagingController(firstPageKey: 0); 22 | 23 | @override 24 | void initState() { 25 | _controller.addPageRequestListener((pageKey) { 26 | _fetchPage(pageKey); 27 | }); 28 | super.initState(); 29 | } 30 | 31 | @override 32 | void dispose() { 33 | _controller.dispose(); 34 | super.dispose(); 35 | } 36 | 37 | void _setFilters(String filter) { 38 | WidgetsBinding.instance.addPostFrameCallback((_) { 39 | _filter = filter; 40 | if (mounted) { 41 | _controller.nextPageKey = 0; 42 | _controller.refresh(); 43 | } 44 | }); 45 | } 46 | 47 | Future _fetchPage(int offset) async { 48 | try { 49 | final newItems = await SupabaseChatCore.instance 50 | .rooms(filter: _filter, offset: offset, limit: _pageSize); 51 | final isLastPage = newItems.length < _pageSize; 52 | if (isLastPage) { 53 | _controller.appendLastPage(newItems); 54 | } else { 55 | final nextPageKey = offset + newItems.length; 56 | _controller.appendPage(newItems, nextPageKey); 57 | } 58 | } catch (error) { 59 | _controller.error = error; 60 | } 61 | } 62 | 63 | @override 64 | Widget build(BuildContext context) => Column( 65 | crossAxisAlignment: CrossAxisAlignment.center, 66 | children: [ 67 | FractionallySizedBox( 68 | widthFactor: .5, 69 | child: TextField( 70 | decoration: InputDecoration( 71 | border: OutlineInputBorder(), 72 | labelText: 'Search', 73 | ), 74 | onChanged: (value) => _setFilters(value), 75 | ), 76 | ), 77 | Expanded( 78 | child: RefreshIndicator( 79 | onRefresh: () async { 80 | WidgetsBinding.instance.addPostFrameCallback((_) { 81 | if (mounted) { 82 | _controller.nextPageKey = 0; 83 | _controller.refresh(); 84 | } 85 | }); 86 | }, 87 | child: StreamBuilder>( 88 | stream: SupabaseChatCore.instance.roomsUpdates(), 89 | builder: (context, snapshot) { 90 | WidgetsBinding.instance.addPostFrameCallback((_) { 91 | if (mounted) { 92 | if (_filter == '' && snapshot.data != null) { 93 | _controller.itemList = SupabaseChatCore.updateRoomList( 94 | _controller.itemList ?? [], 95 | snapshot.data!, 96 | ); 97 | } 98 | } 99 | }); 100 | return PagedListView( 101 | pagingController: _controller, 102 | builderDelegate: PagedChildBuilderDelegate( 103 | itemBuilder: (context, room, index) => RoomTile( 104 | room: room, 105 | onTap: (room) { 106 | Navigator.of(context).push( 107 | MaterialPageRoute( 108 | builder: (context) => RoomPage( 109 | room: room, 110 | ), 111 | ), 112 | ); 113 | }, 114 | ), 115 | ), 116 | ); 117 | }, 118 | ), 119 | ), 120 | ), 121 | ], 122 | ); 123 | } 124 | -------------------------------------------------------------------------------- /example/lib/src/pages/users.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:flutter_chat_types/flutter_chat_types.dart' as types; 4 | import 'package:flutter_supabase_chat_core/flutter_supabase_chat_core.dart'; 5 | import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; 6 | 7 | import '../widgets/user_tile.dart'; 8 | import 'room.dart'; 9 | 10 | class UsersPage extends StatefulWidget { 11 | const UsersPage({super.key}); 12 | 13 | @override 14 | State createState() => _UsersPageState(); 15 | } 16 | 17 | class _UsersPageState extends State { 18 | static const _pageSize = 20; 19 | String _filter = ''; 20 | 21 | final PagingController _controller = 22 | PagingController(firstPageKey: 0); 23 | 24 | @override 25 | void initState() { 26 | _controller.addPageRequestListener((pageKey) { 27 | _fetchPage(pageKey); 28 | }); 29 | super.initState(); 30 | } 31 | 32 | @override 33 | void dispose() { 34 | _controller.dispose(); 35 | super.dispose(); 36 | } 37 | 38 | void _setFilters(String filter) { 39 | WidgetsBinding.instance.addPostFrameCallback((_) { 40 | _filter = filter; 41 | if (mounted) { 42 | _controller.nextPageKey = 0; 43 | _controller.refresh(); 44 | } 45 | }); 46 | } 47 | 48 | Future _fetchPage(int offset) async { 49 | try { 50 | final newItems = await SupabaseChatCore.instance 51 | .users(filter: _filter, offset: offset, limit: _pageSize); 52 | final isLastPage = newItems.length < _pageSize; 53 | if (isLastPage) { 54 | _controller.appendLastPage(newItems); 55 | } else { 56 | final nextPageKey = offset + newItems.length; 57 | _controller.appendPage(newItems, nextPageKey); 58 | } 59 | } catch (error) { 60 | _controller.error = error; 61 | } 62 | } 63 | 64 | void _handlePressed(types.User otherUser, BuildContext context) async { 65 | final navigator = Navigator.of(context); 66 | final room = await SupabaseChatCore.instance.createRoom(otherUser); 67 | 68 | navigator.pop(); 69 | await navigator.push( 70 | MaterialPageRoute( 71 | builder: (context) => RoomPage( 72 | room: room, 73 | ), 74 | ), 75 | ); 76 | } 77 | 78 | @override 79 | Widget build(BuildContext context) => Scaffold( 80 | appBar: AppBar( 81 | systemOverlayStyle: SystemUiOverlayStyle.light, 82 | title: const Text('Users'), 83 | ), 84 | body: Column( 85 | crossAxisAlignment: CrossAxisAlignment.center, 86 | children: [ 87 | FractionallySizedBox( 88 | widthFactor: .5, 89 | child: TextField( 90 | decoration: InputDecoration( 91 | border: OutlineInputBorder(), 92 | labelText: 'Search', 93 | ), 94 | onChanged: (value) => _setFilters(value), 95 | ), 96 | ), 97 | Expanded( 98 | child: PagedListView( 99 | pagingController: _controller, 100 | builderDelegate: PagedChildBuilderDelegate( 101 | itemBuilder: (context, user, index) => UserTile( 102 | user: user, 103 | onTap: (user) { 104 | _handlePressed(user, context); 105 | }, 106 | ), 107 | ), 108 | ), 109 | ), 110 | ], 111 | ), 112 | ); 113 | } 114 | -------------------------------------------------------------------------------- /example/lib/src/theme/color_schemes.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | const lightColorScheme = ColorScheme( 4 | brightness: Brightness.light, 5 | primary: Color(0xFF6750A4), 6 | onPrimary: Color(0xFFFFFFFF), 7 | primaryContainer: Color(0xFFEADDFF), 8 | onPrimaryContainer: Color(0xFF21005D), 9 | secondary: Color(0xFF625B71), 10 | onSecondary: Color(0xFFFFFFFF), 11 | secondaryContainer: Color(0xFFE8DEF8), 12 | onSecondaryContainer: Color(0xFF1D192B), 13 | tertiary: Color(0xFF7D5260), 14 | onTertiary: Color(0xFFFFFFFF), 15 | tertiaryContainer: Color(0xFFFFD8E4), 16 | onTertiaryContainer: Color(0xFF31111D), 17 | error: Color(0xFFB3261E), 18 | onError: Color(0xFFFFFFFF), 19 | errorContainer: Color(0xFFF9DEDC), 20 | onErrorContainer: Color(0xFF410E0B), 21 | outline: Color(0xFF79747E), 22 | surface: Color(0xFFFFFBFE), 23 | onSurface: Color(0xFF1C1B1F), 24 | surfaceContainerHighest: Color(0xFFE7E0EC), 25 | onSurfaceVariant: Color(0xFF49454F), 26 | inverseSurface: Color(0xFF313033), 27 | onInverseSurface: Color(0xFFF4EFF4), 28 | inversePrimary: Color(0xFFD0BCFF), 29 | shadow: Color(0xFF000000), 30 | surfaceTint: Color(0xFF6750A4), 31 | outlineVariant: Color(0xFFCAC4D0), 32 | scrim: Color(0xFF000000), 33 | ); 34 | 35 | const darkColorScheme = ColorScheme( 36 | brightness: Brightness.dark, 37 | primary: Color(0xFFD0BCFF), 38 | onPrimary: Color(0xFF381E72), 39 | primaryContainer: Color(0xFF4F378B), 40 | onPrimaryContainer: Color(0xFFEADDFF), 41 | secondary: Color(0xFFCCC2DC), 42 | onSecondary: Color(0xFF332D41), 43 | secondaryContainer: Color(0xFF4A4458), 44 | onSecondaryContainer: Color(0xFFE8DEF8), 45 | tertiary: Color(0xFFEFB8C8), 46 | onTertiary: Color(0xFF492532), 47 | tertiaryContainer: Color(0xFF633B48), 48 | onTertiaryContainer: Color(0xFFFFD8E4), 49 | error: Color(0xFFF2B8B5), 50 | onError: Color(0xFF601410), 51 | errorContainer: Color(0xFF8C1D18), 52 | onErrorContainer: Color(0xFFF9DEDC), 53 | outline: Color(0xFF938F99), 54 | surface: Color(0xFF1C1B1F), 55 | onSurface: Color(0xFFE6E1E5), 56 | surfaceContainerHighest: Color(0xFF49454F), 57 | onSurfaceVariant: Color(0xFFCAC4D0), 58 | inverseSurface: Color(0xFFE6E1E5), 59 | onInverseSurface: Color(0xFF313033), 60 | inversePrimary: Color(0xFF6750A4), 61 | shadow: Color(0xFF000000), 62 | surfaceTint: Color(0xFFD0BCFF), 63 | outlineVariant: Color(0xFF49454F), 64 | scrim: Color(0xFF000000), 65 | ); 66 | -------------------------------------------------------------------------------- /example/lib/src/util.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | const colors = [ 4 | Color(0xffff6767), 5 | Color(0xff66e0da), 6 | Color(0xfff5a2d9), 7 | Color(0xfff0c722), 8 | Color(0xff6a85e5), 9 | Color(0xfffd9a6f), 10 | Color(0xff92db6e), 11 | Color(0xff73b8e5), 12 | Color(0xfffd7590), 13 | Color(0xffc78ae5), 14 | ]; 15 | 16 | Color getAvatarColor(String name) { 17 | final index = name.hashCode % colors.length; 18 | return colors[index]; 19 | } 20 | -------------------------------------------------------------------------------- /example/lib/src/widgets/room_tile.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_chat_types/flutter_chat_types.dart' as types; 3 | import 'package:flutter_supabase_chat_core/flutter_supabase_chat_core.dart'; 4 | import 'package:timeago/timeago.dart' as timeago; 5 | 6 | import '../class/message_status_ex.dart'; 7 | import '../util.dart'; 8 | 9 | class RoomTile extends StatelessWidget { 10 | final types.Room room; 11 | final ValueChanged onTap; 12 | 13 | const RoomTile({ 14 | super.key, 15 | required this.room, 16 | required this.onTap, 17 | }); 18 | 19 | Widget _buildAvatar(types.Room room) { 20 | final color = getAvatarColor(room.id); 21 | var otherUserIndex = -1; 22 | types.User? otherUser; 23 | 24 | if (room.type == types.RoomType.direct) { 25 | otherUserIndex = room.users.indexWhere( 26 | (u) => u.id != SupabaseChatCore.instance.loggedSupabaseUser!.id, 27 | ); 28 | if (otherUserIndex >= 0) { 29 | otherUser = room.users[otherUserIndex]; 30 | } 31 | } 32 | 33 | final hasImage = room.imageUrl != null; 34 | final name = room.name ?? ''; 35 | final Widget child = CircleAvatar( 36 | backgroundColor: hasImage ? Colors.transparent : color, 37 | backgroundImage: hasImage ? NetworkImage(room.imageUrl!) : null, 38 | radius: 20, 39 | child: !hasImage 40 | ? Text( 41 | name.isEmpty ? '' : name[0].toUpperCase(), 42 | style: const TextStyle(color: Colors.white), 43 | ) 44 | : null, 45 | ); 46 | if (otherUser == null) { 47 | return Padding( 48 | padding: const EdgeInsets.only(right: 16), 49 | child: child, 50 | ); 51 | } 52 | return Padding( 53 | padding: const EdgeInsets.only(right: 16), 54 | child: UserOnlineStatusWidget( 55 | uid: otherUser.id, 56 | builder: (status) => Stack( 57 | alignment: Alignment.bottomRight, 58 | children: [ 59 | child, 60 | if (status == UserOnlineStatus.online) 61 | Container( 62 | width: 10, 63 | height: 10, 64 | margin: const EdgeInsets.only( 65 | right: 3, 66 | bottom: 3, 67 | ), 68 | decoration: BoxDecoration( 69 | color: Colors.green, 70 | shape: BoxShape.circle, 71 | border: Border.all( 72 | color: Colors.white, 73 | width: 2, 74 | ), 75 | ), 76 | ), 77 | ], 78 | ), 79 | ), 80 | ); 81 | } 82 | 83 | @override 84 | Widget build(BuildContext context) => ListTile( 85 | key: ValueKey(room.id), 86 | leading: _buildAvatar(room), 87 | title: Row( 88 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 89 | children: [ 90 | Text(room.name ?? ''), 91 | if (room.lastMessages?.isNotEmpty == true) 92 | Row( 93 | crossAxisAlignment: CrossAxisAlignment.end, 94 | children: [ 95 | Text( 96 | timeago.format( 97 | DateTime.now().subtract( 98 | Duration( 99 | milliseconds: DateTime.now().millisecondsSinceEpoch - 100 | (room.updatedAt ?? 0), 101 | ), 102 | ), 103 | locale: 'en_short', 104 | ), 105 | ), 106 | if (room.lastMessages!.first.status != null) 107 | Padding( 108 | padding: const EdgeInsets.only(left: 8.0), 109 | child: Icon( 110 | size: 20, 111 | room.lastMessages!.first.status!.icon, 112 | color: 113 | room.lastMessages!.first.status == types.Status.seen 114 | ? Colors.lightBlue 115 | : null, 116 | ), 117 | ), 118 | ], 119 | ), 120 | ], 121 | ), 122 | subtitle: room.lastMessages?.isNotEmpty == true && 123 | room.lastMessages!.first is types.TextMessage 124 | ? Text( 125 | (room.lastMessages!.first as types.TextMessage).text, 126 | maxLines: 1, 127 | overflow: TextOverflow.ellipsis, 128 | ) 129 | : null, 130 | onTap: () => onTap(room), 131 | ); 132 | } 133 | -------------------------------------------------------------------------------- /example/lib/src/widgets/user_tile.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_chat_types/flutter_chat_types.dart' as types; 3 | import 'package:flutter_supabase_chat_core/flutter_supabase_chat_core.dart'; 4 | 5 | import '../class/user_ex.dart'; 6 | import '../util.dart'; 7 | 8 | class UserTile extends StatelessWidget { 9 | final types.User user; 10 | final ValueChanged onTap; 11 | 12 | const UserTile({ 13 | super.key, 14 | required this.user, 15 | required this.onTap, 16 | }); 17 | 18 | Widget _buildAvatar(types.User user) { 19 | final color = getAvatarColor(user.id); 20 | final hasImage = user.imageUrl != null; 21 | final name = user.getUserName(); 22 | return Container( 23 | margin: const EdgeInsets.only(right: 16), 24 | child: UserOnlineStatusWidget( 25 | uid: user.id, 26 | builder: (status) => Stack( 27 | alignment: Alignment.bottomRight, 28 | children: [ 29 | CircleAvatar( 30 | backgroundColor: hasImage ? Colors.transparent : color, 31 | backgroundImage: hasImage ? NetworkImage(user.imageUrl!) : null, 32 | radius: 20, 33 | child: !hasImage 34 | ? Text( 35 | name.isEmpty ? '' : name[0].toUpperCase(), 36 | style: const TextStyle(color: Colors.white), 37 | ) 38 | : null, 39 | ), 40 | if (status == UserOnlineStatus.online) 41 | Container( 42 | width: 10, 43 | height: 10, 44 | margin: const EdgeInsets.only(right: 3, bottom: 3), 45 | decoration: BoxDecoration( 46 | color: Colors.green, 47 | shape: BoxShape.circle, 48 | border: Border.all( 49 | color: Colors.white, 50 | width: 2, 51 | ), 52 | ), 53 | ), 54 | ], 55 | ), 56 | ), 57 | ); 58 | } 59 | 60 | @override 61 | Widget build(BuildContext context) => ListTile( 62 | leading: _buildAvatar(user), 63 | title: Text(user.getUserName()), 64 | onTap: () => onTap(user), 65 | ); 66 | } 67 | -------------------------------------------------------------------------------- /example/lib/supabase_options.dart: -------------------------------------------------------------------------------- 1 | class SupabaseOptions { 2 | final String url; 3 | final String anonKey; 4 | 5 | SupabaseOptions({ 6 | required this.url, 7 | required this.anonKey, 8 | }); 9 | } 10 | 11 | final SupabaseOptions supabaseOptions = SupabaseOptions( 12 | url: 'https://{{your_project_reference_id}}.supabase.co', 13 | anonKey: '{{supabase_anon_key}}', 14 | ); 15 | -------------------------------------------------------------------------------- /example/linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/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 | #include 12 | #include 13 | 14 | void fl_register_plugins(FlPluginRegistry* registry) { 15 | g_autoptr(FlPluginRegistrar) file_saver_registrar = 16 | fl_plugin_registry_get_registrar_for_plugin(registry, "FileSaverPlugin"); 17 | file_saver_plugin_register_with_registrar(file_saver_registrar); 18 | g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = 19 | fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); 20 | file_selector_plugin_register_with_registrar(file_selector_linux_registrar); 21 | g_autoptr(FlPluginRegistrar) gtk_registrar = 22 | fl_plugin_registry_get_registrar_for_plugin(registry, "GtkPlugin"); 23 | gtk_plugin_register_with_registrar(gtk_registrar); 24 | g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = 25 | fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); 26 | url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); 27 | } 28 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/linux/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | file_saver 7 | file_selector_linux 8 | gtk 9 | url_launcher_linux 10 | ) 11 | 12 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 13 | ) 14 | 15 | set(PLUGIN_BUNDLED_LIBRARIES) 16 | 17 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 18 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux 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}/linux plugins/${ffi_plugin}) 26 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 27 | endforeach(ffi_plugin) 28 | -------------------------------------------------------------------------------- /example/linux/main.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | int main(int argc, char** argv) { 4 | g_autoptr(MyApplication) app = my_application_new(); 5 | return g_application_run(G_APPLICATION(app), argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /example/linux/my_application.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | #include 4 | #ifdef GDK_WINDOWING_X11 5 | #include 6 | #endif 7 | 8 | #include "flutter/generated_plugin_registrant.h" 9 | 10 | struct _MyApplication { 11 | GtkApplication parent_instance; 12 | char** dart_entrypoint_arguments; 13 | }; 14 | 15 | G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) 16 | 17 | // Implements GApplication::activate. 18 | static void my_application_activate(GApplication* application) { 19 | MyApplication* self = MY_APPLICATION(application); 20 | GtkWindow* window = 21 | GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); 22 | 23 | // Use a header bar when running in GNOME as this is the common style used 24 | // by applications and is the setup most users will be using (e.g. Ubuntu 25 | // desktop). 26 | // If running on X and not using GNOME then just use a traditional title bar 27 | // in case the window manager does more exotic layout, e.g. tiling. 28 | // If running on Wayland assume the header bar will work (may need changing 29 | // if future cases occur). 30 | gboolean use_header_bar = TRUE; 31 | #ifdef GDK_WINDOWING_X11 32 | GdkScreen* screen = gtk_window_get_screen(window); 33 | if (GDK_IS_X11_SCREEN(screen)) { 34 | const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); 35 | if (g_strcmp0(wm_name, "GNOME Shell") != 0) { 36 | use_header_bar = FALSE; 37 | } 38 | } 39 | #endif 40 | if (use_header_bar) { 41 | GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); 42 | gtk_widget_show(GTK_WIDGET(header_bar)); 43 | gtk_header_bar_set_title(header_bar, "example"); 44 | gtk_header_bar_set_show_close_button(header_bar, TRUE); 45 | gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); 46 | } else { 47 | gtk_window_set_title(window, "example"); 48 | } 49 | 50 | gtk_window_set_default_size(window, 1280, 720); 51 | gtk_widget_show(GTK_WIDGET(window)); 52 | 53 | g_autoptr(FlDartProject) project = fl_dart_project_new(); 54 | fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); 55 | 56 | FlView* view = fl_view_new(project); 57 | gtk_widget_show(GTK_WIDGET(view)); 58 | gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); 59 | 60 | fl_register_plugins(FL_PLUGIN_REGISTRY(view)); 61 | 62 | gtk_widget_grab_focus(GTK_WIDGET(view)); 63 | } 64 | 65 | // Implements GApplication::local_command_line. 66 | static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { 67 | MyApplication* self = MY_APPLICATION(application); 68 | // Strip out the first argument as it is the binary name. 69 | self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); 70 | 71 | g_autoptr(GError) error = nullptr; 72 | if (!g_application_register(application, nullptr, &error)) { 73 | g_warning("Failed to register: %s", error->message); 74 | *exit_status = 1; 75 | return TRUE; 76 | } 77 | 78 | g_application_activate(application); 79 | *exit_status = 0; 80 | 81 | return TRUE; 82 | } 83 | 84 | // Implements GObject::dispose. 85 | static void my_application_dispose(GObject* object) { 86 | MyApplication* self = MY_APPLICATION(object); 87 | g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); 88 | G_OBJECT_CLASS(my_application_parent_class)->dispose(object); 89 | } 90 | 91 | static void my_application_class_init(MyApplicationClass* klass) { 92 | G_APPLICATION_CLASS(klass)->activate = my_application_activate; 93 | G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; 94 | G_OBJECT_CLASS(klass)->dispose = my_application_dispose; 95 | } 96 | 97 | static void my_application_init(MyApplication* self) {} 98 | 99 | MyApplication* my_application_new() { 100 | return MY_APPLICATION(g_object_new(my_application_get_type(), 101 | "application-id", APPLICATION_ID, 102 | "flags", G_APPLICATION_NON_UNIQUE, 103 | nullptr)); 104 | } 105 | -------------------------------------------------------------------------------- /example/linux/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /example/macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/dgph 7 | **/xcuserdata/ 8 | -------------------------------------------------------------------------------- /example/macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @NSApplicationMain 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /example/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 = example 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = com.example 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2023 com.. All rights reserved. 15 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/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.client 10 | 11 | com.apple.security.network.server 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import FlutterMacOS 2 | import Cocoa 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 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: example 2 | description: A new Flutter project. 3 | publish_to: 'none' 4 | 5 | version: 1.5.0 6 | 7 | environment: 8 | sdk: '>=3.4.0 <4.0.0' 9 | 10 | dependencies: 11 | cupertino_icons: ^1.0.8 12 | dio: ^5.8.0+1 13 | faker: ^2.2.0 14 | file_picker: ^8.3.1 15 | file_saver: ^0.2.14 16 | flutter: 17 | sdk: flutter 18 | flutter_chat_types: ^3.6.2 19 | flutter_chat_ui: ^1.6.15 20 | flutter_login: ^5.0.0 21 | flutter_supabase_chat_core: 22 | path: ../ 23 | flutter_svg: ^2.0.17 24 | http: ^1.3.0 25 | image_picker: ^1.1.2 26 | infinite_scroll_pagination: ^4.1.0 27 | open_filex: ^4.6.0 28 | path_provider: ^2.1.5 29 | supabase_flutter: ^2.8.3 30 | timeago: ^3.7.0 31 | 32 | 33 | dev_dependencies: 34 | flutter_lints: ^5.0.0 35 | 36 | flutter: 37 | uses-material-design: true 38 | assets: 39 | - assets/ -------------------------------------------------------------------------------- /example/utils/prepare.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | [string]$hostname, 3 | [int]$port, 4 | [string]$database, 5 | [string]$user 6 | ) 7 | 8 | try { 9 | Get-Command psql.exe -ErrorAction Stop 10 | } 11 | catch { 12 | Write-Error "psql was not found. Please ensure PostgreSQL is installed and that psql is in your PATH." 13 | exit 14 | } 15 | 16 | $psqlCommand = "psql -U $user -h $hostname -p $port -d $database" 17 | 18 | Invoke-Expression "$psqlCommand -f .\sql\01_database_schema.sql" 19 | Invoke-Expression "$psqlCommand -f .\sql\02_database_trigger.sql" 20 | Invoke-Expression "$psqlCommand -f .\sql\03_database_policy.sql" 21 | Invoke-Expression "$psqlCommand -f .\sql\04_storage.sql" 22 | Invoke-Expression "$psqlCommand -f .\sql\05_database_view.sql" 23 | Invoke-Expression "$psqlCommand -f .\sql\99_database_schema_permission.sql" 24 | -------------------------------------------------------------------------------- /example/utils/prepare.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | while getopts h:p:d:U: flag 4 | do 5 | case "${flag}" in 6 | h) hostname=${OPTARG};; 7 | p) port=${OPTARG};; 8 | d) database=${OPTARG};; 9 | U) user=${OPTARG};; 10 | esac 11 | done 12 | 13 | psql -U $user -h $hostname -p $port -d $database -f ./sql/01_database_schema.sql 14 | psql -U $user -h $hostname -p $port -d $database -f ./sql/02_database_trigger.sql 15 | psql -U $user -h $hostname -p $port -d $database -f ./sql/03_database_policy.sql 16 | psql -U $user -h $hostname -p $port -d $database -f ./sql/04_storage.sql 17 | psql -U $user -h $hostname -p $port -d $database -f ./sql/05_database_view.sql 18 | psql -U $user -h $hostname -p $port -d $database -f ./sql/99_database_schema_permission.sql -------------------------------------------------------------------------------- /example/utils/sql/01_database_schema.sql: -------------------------------------------------------------------------------- 1 | SET statement_timeout = 0; 2 | SET lock_timeout = 0; 3 | SET idle_in_transaction_session_timeout = 0; 4 | SET client_encoding = 'UTF8'; 5 | SET standard_conforming_strings = on; 6 | SELECT pg_catalog.set_config('search_path', '', false); 7 | SET check_function_bodies = false; 8 | SET xmloption = content; 9 | SET client_min_messages = warning; 10 | SET row_security = off; 11 | 12 | CREATE SCHEMA chats; 13 | 14 | ALTER SCHEMA chats OWNER TO postgres; 15 | 16 | SET default_tablespace = ''; 17 | 18 | SET default_table_access_method = heap; 19 | 20 | CREATE TABLE chats.messages ( 21 | id bigint NOT NULL, 22 | "createdAt" bigint, 23 | metadata jsonb, 24 | duration bigint, 25 | "mimeType" text, 26 | name text, 27 | "remoteId" text, 28 | "repliedMessage" jsonb, 29 | "roomId" bigint NOT NULL, 30 | "showStatus" boolean, 31 | size bigint, 32 | status text, 33 | type text, 34 | "updatedAt" bigint, 35 | uri text, 36 | "waveForm" jsonb, 37 | "isLoading" boolean, 38 | height double precision, 39 | width double precision, 40 | "previewData" jsonb, 41 | "authorId" uuid NOT NULL, 42 | text text 43 | ); 44 | 45 | ALTER TABLE chats.messages OWNER TO postgres; 46 | 47 | ALTER TABLE chats.messages ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( 48 | SEQUENCE NAME chats.messages_id_seq 49 | START WITH 1 50 | INCREMENT BY 1 51 | NO MINVALUE 52 | NO MAXVALUE 53 | CACHE 1 54 | ); 55 | 56 | CREATE TABLE chats.rooms ( 57 | id bigint NOT NULL, 58 | "imageUrl" text, 59 | metadata jsonb, 60 | name text, 61 | type text, 62 | "userIds" uuid[] NOT NULL, 63 | "lastMessages" jsonb, 64 | "userRoles" jsonb, 65 | "createdAt" bigint NOT NULL, 66 | "updatedAt" bigint NOT NULL 67 | ); 68 | 69 | ALTER TABLE chats.rooms OWNER TO postgres; 70 | 71 | ALTER TABLE chats.rooms ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY ( 72 | SEQUENCE NAME chats.rooms_id_seq 73 | START WITH 1 74 | INCREMENT BY 1 75 | NO MINVALUE 76 | NO MAXVALUE 77 | CACHE 1 78 | ); 79 | 80 | CREATE TABLE chats.users ( 81 | "firstName" text, 82 | "imageUrl" text, 83 | "lastName" text, 84 | metadata jsonb, 85 | role text, 86 | id uuid NOT NULL, 87 | "createdAt" bigint NOT NULL, 88 | "updatedAt" bigint NOT NULL, 89 | "lastSeen" bigint NOT NULL 90 | ); 91 | 92 | ALTER TABLE chats.users OWNER TO postgres; 93 | 94 | ALTER TABLE ONLY chats.messages 95 | ADD CONSTRAINT messages_pkey PRIMARY KEY (id); 96 | 97 | ALTER TABLE ONLY chats.rooms 98 | ADD CONSTRAINT rooms_pkey PRIMARY KEY (id); 99 | 100 | ALTER TABLE ONLY chats.users 101 | ADD CONSTRAINT users_pkey PRIMARY KEY (id); 102 | 103 | ALTER TABLE ONLY chats.messages 104 | ADD CONSTRAINT "messages_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES auth.users(id) ON DELETE CASCADE; 105 | 106 | ALTER TABLE ONLY chats.messages 107 | ADD CONSTRAINT "messages_roomId_fkey" FOREIGN KEY ("roomId") REFERENCES chats.rooms(id) ON UPDATE CASCADE ON DELETE CASCADE; 108 | 109 | ALTER TABLE ONLY chats.users 110 | ADD CONSTRAINT users_id_fkey FOREIGN KEY (id) REFERENCES auth.users(id) ON UPDATE CASCADE ON DELETE CASCADE; 111 | 112 | CREATE INDEX ON "chats"."messages" USING btree ("authorId"); 113 | CREATE INDEX ON "chats"."messages" USING btree ("roomId"); 114 | 115 | ALTER TABLE chats.messages ENABLE ROW LEVEL SECURITY; 116 | 117 | ALTER TABLE chats.rooms ENABLE ROW LEVEL SECURITY; 118 | 119 | ALTER TABLE chats.users ENABLE ROW LEVEL SECURITY; 120 | 121 | REVOKE USAGE ON SCHEMA chats FROM PUBLIC; 122 | GRANT USAGE ON SCHEMA chats TO anon; 123 | GRANT USAGE ON SCHEMA chats TO authenticated; 124 | GRANT USAGE ON SCHEMA chats TO service_role; 125 | 126 | GRANT ALL ON TABLE chats.messages TO anon; 127 | GRANT ALL ON TABLE chats.messages TO authenticated; 128 | GRANT ALL ON TABLE chats.messages TO service_role; 129 | 130 | GRANT ALL ON SEQUENCE chats.messages_id_seq TO anon; 131 | GRANT ALL ON SEQUENCE chats.messages_id_seq TO authenticated; 132 | GRANT ALL ON SEQUENCE chats.messages_id_seq TO service_role; 133 | 134 | GRANT ALL ON TABLE chats.rooms TO anon; 135 | GRANT ALL ON TABLE chats.rooms TO authenticated; 136 | GRANT ALL ON TABLE chats.rooms TO service_role; 137 | 138 | GRANT ALL ON SEQUENCE chats.rooms_id_seq TO anon; 139 | GRANT ALL ON SEQUENCE chats.rooms_id_seq TO authenticated; 140 | GRANT ALL ON SEQUENCE chats.rooms_id_seq TO service_role; 141 | 142 | GRANT ALL ON TABLE chats.users TO anon; 143 | GRANT ALL ON TABLE chats.users TO authenticated; 144 | GRANT ALL ON TABLE chats.users TO service_role; 145 | 146 | GRANT USAGE ON SCHEMA chats TO anon, authenticated, service_role; 147 | GRANT ALL ON ALL TABLES IN SCHEMA chats TO anon, authenticated, service_role; 148 | GRANT ALL ON ALL ROUTINES IN SCHEMA chats TO anon, authenticated, service_role; 149 | GRANT ALL ON ALL SEQUENCES IN SCHEMA chats TO anon, authenticated, service_role; 150 | ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA chats GRANT ALL ON TABLES TO anon, authenticated, service_role; 151 | ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA chats GRANT ALL ON ROUTINES TO anon, authenticated, service_role; 152 | ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA chats GRANT ALL ON SEQUENCES TO anon, authenticated, service_role; 153 | 154 | ALTER PUBLICATION supabase_realtime ADD TABLE ONLY chats.messages; 155 | 156 | ALTER PUBLICATION supabase_realtime ADD TABLE ONLY chats.rooms; 157 | 158 | ALTER PUBLICATION supabase_realtime ADD TABLE ONLY chats.users; -------------------------------------------------------------------------------- /example/utils/sql/02_database_trigger.sql: -------------------------------------------------------------------------------- 1 | 2 | CREATE OR REPLACE FUNCTION chats.handle_new_user() 3 | RETURNS trigger 4 | LANGUAGE 'plpgsql' 5 | COST 100 6 | VOLATILE NOT LEAKPROOF SECURITY DEFINER 7 | SET search_path=public 8 | SET search_path = '' 9 | AS $BODY$ 10 | DECLARE 11 | ts_in_milliseconds bigint; 12 | BEGIN 13 | SELECT EXTRACT(epoch FROM NOW()) * 1000 INTO ts_in_milliseconds; 14 | insert into chats.users (id, "createdAt", "updatedAt", "lastSeen") 15 | values (new.id, ts_in_milliseconds, ts_in_milliseconds, ts_in_milliseconds); 16 | return new; 17 | end; 18 | $BODY$; 19 | 20 | drop trigger if exists on_auth_user_created on auth.users; 21 | create trigger on_auth_user_created 22 | after insert on auth.users 23 | for each row execute procedure chats.handle_new_user(); 24 | 25 | 26 | CREATE OR REPLACE FUNCTION chats.update_last_messages() 27 | RETURNS TRIGGER 28 | SET search_path = '' 29 | AS $$ 30 | DECLARE 31 | latest_message jsonb; 32 | ts_in_milliseconds bigint; 33 | BEGIN 34 | SELECT jsonb_build_object( 35 | 'id', id, 36 | 'createdAt', "createdAt", 37 | 'metadata', metadata, 38 | 'duration', duration, 39 | 'mimeType', "mimeType", 40 | 'name', name, 41 | 'remoteId', "remoteId", 42 | 'repliedMessage', "repliedMessage", 43 | 'roomId', "roomId", 44 | 'showStatus', "showStatus", 45 | 'size', size, 46 | 'status', status, 47 | 'type', type, 48 | 'updatedAt', "updatedAt", 49 | 'uri', uri, 50 | 'waveForm', "waveForm", 51 | 'isLoading', "isLoading", 52 | 'height', height, 53 | 'width', width, 54 | 'previewData', "previewData", 55 | 'authorId', "authorId", 56 | 'text', text 57 | ) 58 | INTO latest_message 59 | FROM chats.messages 60 | WHERE "roomId" = NEW."roomId" 61 | ORDER BY "createdAt" DESC 62 | LIMIT 1; 63 | IF latest_message IS DISTINCT FROM (SELECT "lastMessages" FROM chats.rooms WHERE id = NEW."roomId") THEN 64 | SELECT EXTRACT(epoch FROM NOW()) * 1000 INTO ts_in_milliseconds; 65 | UPDATE chats.rooms 66 | SET "updatedAt" = ts_in_milliseconds, 67 | "lastMessages" = jsonb_build_array(latest_message) 68 | WHERE id = NEW."roomId"; 69 | END IF; 70 | RETURN NEW; 71 | END; 72 | $$ LANGUAGE plpgsql; 73 | 74 | drop trigger if exists update_last_messages_trigger on chats.messages; 75 | CREATE TRIGGER update_last_messages_trigger 76 | AFTER INSERT OR UPDATE ON chats.messages 77 | FOR EACH ROW 78 | EXECUTE FUNCTION chats.update_last_messages(); 79 | 80 | CREATE OR REPLACE FUNCTION chats.set_message_status_to_sent() 81 | RETURNS TRIGGER 82 | SET search_path = '' 83 | AS $$ 84 | BEGIN 85 | NEW.status := 'sent'; 86 | RETURN NEW; 87 | END; 88 | $$ LANGUAGE plpgsql; 89 | 90 | drop trigger if exists update_status_before_insert on chats.messages; 91 | CREATE TRIGGER update_status_before_insert 92 | BEFORE INSERT ON chats.messages 93 | FOR EACH ROW EXECUTE FUNCTION chats.set_message_status_to_sent(); 94 | -------------------------------------------------------------------------------- /example/utils/sql/03_database_policy.sql: -------------------------------------------------------------------------------- 1 | DROP POLICY IF EXISTS "chats.users_grant_create" ON chats.users; 2 | DROP POLICY IF EXISTS "chats.users_grant_read" ON chats.users; 3 | DROP POLICY IF EXISTS "chats.users_grant_update" ON chats.users; 4 | DROP POLICY IF EXISTS "chats.users_grant_delete" ON chats.users; 5 | 6 | DROP POLICY IF EXISTS "chats.rooms_grant_create" ON chats.rooms; 7 | DROP POLICY IF EXISTS "chats.rooms_grant_read" ON chats.rooms; 8 | DROP POLICY IF EXISTS "chats.rooms_grant_update" ON chats.rooms; 9 | DROP POLICY IF EXISTS "chats.rooms_grant_delete" ON chats.rooms; 10 | 11 | DROP POLICY IF EXISTS "chats.messages_grant_create" ON chats.messages; 12 | DROP POLICY IF EXISTS "chats.messages_grant_read" ON chats.messages; 13 | DROP POLICY IF EXISTS "chats.messages_grant_update" ON chats.messages; 14 | DROP POLICY IF EXISTS "chats.messages_grant_delete" ON chats.messages; 15 | 16 | CREATE OR REPLACE FUNCTION chats.is_auth() 17 | RETURNS boolean 18 | LANGUAGE 'plpgsql' 19 | COST 100 20 | VOLATILE NOT LEAKPROOF SECURITY INVOKER 21 | SET search_path = '' 22 | AS $BODY$ 23 | BEGIN 24 | return auth.uid() IS NOT NULL; 25 | end; 26 | $BODY$; 27 | 28 | CREATE OR REPLACE FUNCTION chats.is_owner(user_id uuid) 29 | RETURNS boolean 30 | LANGUAGE 'plpgsql' 31 | COST 100 32 | VOLATILE NOT LEAKPROOF SECURITY INVOKER 33 | SET search_path = '' 34 | AS $BODY$ 35 | BEGIN 36 | return auth.uid() = user_id; 37 | end; 38 | $BODY$; 39 | 40 | CREATE OR REPLACE FUNCTION chats.is_member(members uuid[]) 41 | RETURNS boolean 42 | LANGUAGE 'plpgsql' 43 | COST 100 44 | VOLATILE NOT LEAKPROOF SECURITY INVOKER 45 | SET search_path = '' 46 | AS $BODY$ 47 | BEGIN 48 | return auth.uid() = ANY(members); 49 | end; 50 | $BODY$; 51 | 52 | CREATE OR REPLACE FUNCTION chats.is_chat_member(room_id bigint) 53 | RETURNS boolean 54 | LANGUAGE 'plpgsql' 55 | COST 100 56 | VOLATILE NOT LEAKPROOF SECURITY INVOKER 57 | SET search_path = '' 58 | AS $BODY$ 59 | DECLARE 60 | members uuid[]; 61 | BEGIN 62 | SELECT "userIds" INTO members 63 | FROM chats.rooms 64 | WHERE id = room_id; 65 | return chats.is_member(members); 66 | end; 67 | $BODY$; 68 | 69 | CREATE POLICY "chats.users_grant_create" 70 | ON chats.users 71 | AS PERMISSIVE 72 | FOR INSERT 73 | TO public 74 | WITH CHECK (false); -- Created by trigger 75 | 76 | CREATE POLICY "chats.users_grant_read" 77 | ON chats.users 78 | AS PERMISSIVE 79 | FOR SELECT 80 | TO public 81 | USING (chats.is_auth()); 82 | 83 | CREATE POLICY "chats.users_grant_update" 84 | ON chats.users 85 | AS PERMISSIVE 86 | FOR UPDATE 87 | TO public 88 | USING (chats.is_auth()) 89 | WITH CHECK (chats.is_owner(id)); 90 | 91 | CREATE POLICY "chats.users_grant_delete" 92 | ON chats.users 93 | AS PERMISSIVE 94 | FOR DELETE 95 | TO public 96 | USING (false); -- Delete by foreign key 97 | 98 | CREATE POLICY "chats.rooms_grant_create" 99 | ON chats.rooms 100 | AS PERMISSIVE 101 | FOR INSERT 102 | TO public 103 | WITH CHECK (chats.is_auth()); 104 | 105 | CREATE POLICY "chats.rooms_grant_read" 106 | ON chats.rooms 107 | AS PERMISSIVE 108 | FOR SELECT 109 | TO public 110 | USING (chats.is_member("userIds")); 111 | 112 | CREATE POLICY "chats.rooms_grant_update" 113 | ON chats.rooms 114 | AS PERMISSIVE 115 | FOR UPDATE 116 | TO public 117 | USING (chats.is_member("userIds")) 118 | WITH CHECK (chats.is_member("userIds")); 119 | 120 | CREATE POLICY "chats.rooms_grant_delete" 121 | ON chats.rooms 122 | AS PERMISSIVE 123 | FOR DELETE 124 | TO public 125 | USING (chats.is_member("userIds")); 126 | 127 | CREATE POLICY "chats.messages_grant_create" 128 | ON chats.messages 129 | AS PERMISSIVE 130 | FOR INSERT 131 | TO public 132 | WITH CHECK (chats.is_chat_member("roomId")); 133 | 134 | CREATE POLICY "chats.messages_grant_read" 135 | ON chats.messages 136 | AS PERMISSIVE 137 | FOR SELECT 138 | TO public 139 | USING (chats.is_chat_member("roomId")); 140 | 141 | CREATE POLICY "chats.messages_grant_update" 142 | ON chats.messages 143 | AS PERMISSIVE 144 | FOR UPDATE 145 | TO public 146 | USING (chats.is_chat_member("roomId")) 147 | WITH CHECK (chats.is_chat_member("roomId")); 148 | 149 | CREATE POLICY "chats.messages_grant_delete" 150 | ON chats.messages 151 | AS PERMISSIVE 152 | FOR DELETE 153 | TO public 154 | USING (chats.is_chat_member("roomId")); -------------------------------------------------------------------------------- /example/utils/sql/04_storage.sql: -------------------------------------------------------------------------------- 1 | insert into storage.buckets 2 | (id, name) 3 | values 4 | ('chats_assets', 'chats_assets'), 5 | ('chats_user_avatar', 'chats_user_avatar'); 6 | 7 | DROP policy IF EXISTS "storage.object_grant_create_auth_chats_assets" 8 | ON storage.objects; 9 | create policy "storage.object_grant_create_auth_chats_assets" 10 | on storage.objects for insert 11 | with check ( 12 | bucket_id = 'chats_assets' 13 | and 14 | chats.is_chat_member((storage.foldername(name))[1]::bigint)); 15 | 16 | DROP policy IF EXISTS "storage.object_grant_read_auth_chats_assets" 17 | ON storage.objects; 18 | create policy "storage.object_grant_read_auth_chats_assets" 19 | on storage.objects for select 20 | using ( 21 | bucket_id = 'chats_assets' 22 | and 23 | chats.is_chat_member((storage.foldername(name))[1]::bigint)); 24 | 25 | DROP policy IF EXISTS "storage.object_grant_update_auth_chats_assets" 26 | ON storage.objects; 27 | create policy "storage.object_grant_update_auth_chats_assets" 28 | on storage.objects for update 29 | using ( 30 | bucket_id = 'chats_assets' 31 | and 32 | chats.is_chat_member((storage.foldername(name))[1]::bigint)) 33 | with check ( 34 | bucket_id = 'chats_assets' 35 | and 36 | chats.is_chat_member((storage.foldername(name))[1]::bigint)); 37 | 38 | DROP policy IF EXISTS "storage.object_grant_delete_auth_chats_assets" 39 | ON storage.objects; 40 | create policy "storage.object_grant_delete_auth_chats_assets" 41 | on storage.objects for delete 42 | using ( 43 | bucket_id = 'chats_assets' 44 | and 45 | chats.is_chat_member((storage.foldername(name))[1]::bigint)); 46 | 47 | DROP policy IF EXISTS "storage.object_grant_create_auth_chats_user_avatar" 48 | ON storage.objects; 49 | create policy "storage.object_grant_create_auth_chats_user_avatar" 50 | on storage.objects for insert 51 | with check ( 52 | bucket_id = 'chats_user_avatar' 53 | and 54 | chats.is_owner((storage.foldername(name))[1]::uuid)); 55 | 56 | DROP policy IF EXISTS "storage.object_grant_read_auth_chats_user_avatar" 57 | ON storage.objects; 58 | create policy "storage.object_grant_read_auth_chats_user_avatar" 59 | on storage.objects for select 60 | using ( 61 | bucket_id = 'chats_user_avatar' 62 | and 63 | chats.is_auth()); 64 | 65 | DROP policy IF EXISTS "storage.object_grant_update_auth_chats_user_avatar" 66 | ON storage.objects; 67 | create policy "storage.object_grant_update_auth_chats_user_avatar" 68 | on storage.objects for update 69 | using ( 70 | bucket_id = 'chats_user_avatar' 71 | and 72 | chats.is_owner((storage.foldername(name))[1]::uuid)) 73 | with check ( 74 | bucket_id = 'chats_user_avatar' 75 | and 76 | chats.is_owner((storage.foldername(name))[1]::uuid)); 77 | 78 | DROP policy IF EXISTS "storage.object_grant_delete_auth_chats_user_avatar" 79 | ON storage.objects; 80 | create policy "storage.object_grant_delete_auth_chats_user_avatar" 81 | on storage.objects for delete 82 | using ( 83 | bucket_id = 'chats_user_avatar' 84 | and 85 | chats.is_owner((storage.foldername(name))[1]::uuid)); 86 | -------------------------------------------------------------------------------- /example/utils/sql/05_database_view.sql: -------------------------------------------------------------------------------- 1 | DROP VIEW IF EXISTS chats.messages_l; 2 | DROP VIEW IF EXISTS chats.rooms_l; 3 | 4 | create or replace view chats.rooms_l 5 | with (security_invoker='on') as 6 | select r.id, 7 | r."imageUrl", 8 | r.metadata, 9 | case 10 | when r.type = 'direct' and auth.uid() is not null then 11 | (select coalesce(u."firstName", '') || ' ' || coalesce(u."lastName", '') 12 | from chats.users u 13 | where u.id = any (r."userIds") 14 | and u.id <> auth.uid() 15 | limit 1) 16 | else 17 | r.name 18 | end as name, 19 | r.type, 20 | r."userIds", 21 | r."lastMessages", 22 | r."userRoles", 23 | r."createdAt", 24 | r."updatedAt", 25 | (select jsonb_agg(to_jsonb(u)) 26 | from chats.users u 27 | where u.id = any (r."userIds")) as users 28 | from chats.rooms r; 29 | 30 | 31 | create or replace view chats.messages_l 32 | with (security_invoker='on') as 33 | select m.id, 34 | m."createdAt", 35 | m.metadata, 36 | m.duration, 37 | m."mimeType", 38 | m.name, 39 | m."remoteId", 40 | m."repliedMessage", 41 | m."roomId", 42 | m."showStatus", 43 | m.size, 44 | m.status, 45 | m.type, 46 | m."updatedAt", 47 | m.uri, 48 | m."waveForm", 49 | m."isLoading", 50 | m.height, 51 | m.width, 52 | m."previewData", 53 | m."authorId", 54 | m.text, 55 | to_jsonb(u) as author, 56 | to_jsonb(r) as room 57 | from chats.messages m 58 | left join chats.users u on u.id = m."authorId" 59 | left join chats.rooms_l r on r.id = m."roomId"; 60 | -------------------------------------------------------------------------------- /example/utils/sql/99_database_schema_permission.sql: -------------------------------------------------------------------------------- 1 | GRANT USAGE ON SCHEMA chats TO anon, authenticated, service_role; 2 | GRANT ALL ON ALL TABLES IN SCHEMA chats TO anon, authenticated, service_role; 3 | GRANT ALL ON ALL ROUTINES IN SCHEMA chats TO anon, authenticated, service_role; 4 | GRANT ALL ON ALL SEQUENCES IN SCHEMA chats TO anon, authenticated, service_role; 5 | ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA chats GRANT ALL ON TABLES TO anon, authenticated, service_role; 6 | ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA chats GRANT ALL ON ROUTINES TO anon, authenticated, service_role; 7 | ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA chats GRANT ALL ON SEQUENCES TO anon, authenticated, service_role; 8 | -------------------------------------------------------------------------------- /example/web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/web/favicon.png -------------------------------------------------------------------------------- /example/web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/web/icons/Icon-192.png -------------------------------------------------------------------------------- /example/web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/web/icons/Icon-512.png -------------------------------------------------------------------------------- /example/web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /example/web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /example/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | example 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /example/web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "short_name": "example", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/windows/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Project-level configuration. 2 | cmake_minimum_required(VERSION 3.14) 3 | project(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 "example") 8 | 9 | # Explicitly opt in to modern CMake behaviors to avoid warnings with recent 10 | # versions of CMake. 11 | cmake_policy(SET CMP0063 NEW) 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 | # Fully re-copy the assets directory on each build to avoid having stale files 91 | # from a previous install. 92 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 93 | install(CODE " 94 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 95 | " COMPONENT Runtime) 96 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 97 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 98 | 99 | # Install the AOT library on non-Debug builds only. 100 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 101 | CONFIGURATIONS Profile;Release 102 | COMPONENT Runtime) 103 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/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 | #include 13 | 14 | void RegisterPlugins(flutter::PluginRegistry* registry) { 15 | AppLinksPluginCApiRegisterWithRegistrar( 16 | registry->GetRegistrarForPlugin("AppLinksPluginCApi")); 17 | FileSaverPluginRegisterWithRegistrar( 18 | registry->GetRegistrarForPlugin("FileSaverPlugin")); 19 | FileSelectorWindowsRegisterWithRegistrar( 20 | registry->GetRegistrarForPlugin("FileSelectorWindows")); 21 | UrlLauncherWindowsRegisterWithRegistrar( 22 | registry->GetRegistrarForPlugin("UrlLauncherWindows")); 23 | } 24 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/windows/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | app_links 7 | file_saver 8 | file_selector_windows 9 | url_launcher_windows 10 | ) 11 | 12 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 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 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/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." "\0" 93 | VALUE "FileDescription", "example" "\0" 94 | VALUE "FileVersion", VERSION_AS_STRING "\0" 95 | VALUE "InternalName", "example" "\0" 96 | VALUE "LegalCopyright", "Copyright (C) 2023 com.. All rights reserved." "\0" 97 | VALUE "OriginalFilename", "example.exe" "\0" 98 | VALUE "ProductName", "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 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/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"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 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insideapp-srl/flutter_supabase_chat_core/5b767822714b9d5876df8fc2f77de1badeb02669/example/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /example/windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /example/windows/runner/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void CreateAndAttachConsole() { 11 | if (::AllocConsole()) { 12 | FILE *unused; 13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) { 14 | _dup2(_fileno(stdout), 1); 15 | } 16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) { 17 | _dup2(_fileno(stdout), 2); 18 | } 19 | std::ios::sync_with_stdio(); 20 | FlutterDesktopResyncOutputStreams(); 21 | } 22 | } 23 | 24 | std::vector GetCommandLineArguments() { 25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. 26 | int argc; 27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); 28 | if (argv == nullptr) { 29 | return std::vector(); 30 | } 31 | 32 | std::vector command_line_arguments; 33 | 34 | // Skip the first argument as it's the binary name. 35 | for (int i = 1; i < argc; i++) { 36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i])); 37 | } 38 | 39 | ::LocalFree(argv); 40 | 41 | return command_line_arguments; 42 | } 43 | 44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) { 45 | if (utf16_string == nullptr) { 46 | return std::string(); 47 | } 48 | int target_length = ::WideCharToMultiByte( 49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 50 | -1, nullptr, 0, nullptr, nullptr) 51 | -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 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/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/flutter_supabase_chat_core.dart: -------------------------------------------------------------------------------- 1 | library; 2 | 3 | export 'src/class/supabase_chat_controller.dart'; 4 | export 'src/class/supabase_chat_core.dart'; 5 | export 'src/class/supabase_chat_core_config.dart'; 6 | export 'src/class/upload_asset_result.dart'; 7 | export 'src/class/user_online_status.dart'; 8 | export 'src/util.dart'; 9 | export 'src/widgets/user_online_status.dart'; 10 | export 'src/widgets/user_status_observer.dart'; 11 | -------------------------------------------------------------------------------- /lib/src/class/supabase_chat_core_config.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | /// Class that represents the chat config. Can be used for setting custom names 4 | /// for rooms and users collections. Call [SupabaseChatCore.instance.setConfig] 5 | /// before doing anything else with [SupabaseChatCore.instance] if you want to 6 | /// change the default collection names. When using custom names don't forget 7 | /// to update your security rules and indexes. 8 | @immutable 9 | class SupabaseChatCoreConfig { 10 | const SupabaseChatCoreConfig( 11 | this.schema, 12 | this.roomsTableName, 13 | this.roomsViewName, 14 | this.messagesTableName, 15 | this.messagesViewName, 16 | this.usersTableName, 17 | this.realtimeOnlineUserPrefixChannel, 18 | this.realtimeChatTypingUserPrefixChannel, 19 | this.chatAssetsBucket, 20 | ); 21 | 22 | /// Property to set database schema name. 23 | final String schema; 24 | 25 | /// Property to set rooms table name. 26 | final String roomsTableName; 27 | 28 | /// Property to set rooms table view name. 29 | final String roomsViewName; 30 | 31 | /// Property to set messages table name. 32 | final String messagesTableName; 33 | 34 | /// Property to set messages view name. 35 | final String messagesViewName; 36 | 37 | /// Property to set users table name. 38 | final String usersTableName; 39 | 40 | /// Property to set online users realtime channel. 41 | final String realtimeOnlineUserPrefixChannel; 42 | 43 | /// Property to set users typing in room realtime channel. 44 | final String realtimeChatTypingUserPrefixChannel; 45 | 46 | /// Property to set chat assets bucket. 47 | final String chatAssetsBucket; 48 | } 49 | -------------------------------------------------------------------------------- /lib/src/class/upload_asset_result.dart: -------------------------------------------------------------------------------- 1 | class UploadAssetResult { 2 | final String url; 3 | final String? mimeType; 4 | 5 | UploadAssetResult({ 6 | required this.url, 7 | required this.mimeType, 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /lib/src/class/user_online_status.dart: -------------------------------------------------------------------------------- 1 | enum UserOnlineStatus { 2 | online, 3 | offline, 4 | } 5 | -------------------------------------------------------------------------------- /lib/src/util.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_chat_types/flutter_chat_types.dart' as types; 2 | import 'package:supabase_flutter/supabase_flutter.dart'; 3 | 4 | /// Extension with one [toShortString] method. 5 | extension RoleToShortString on types.Role { 6 | /// Converts enum to the string equal to enum's name. 7 | String toShortString() => toString().split('.').last; 8 | } 9 | 10 | /// Extension with one [toShortString] method. 11 | extension RoomTypeToShortString on types.RoomType { 12 | /// Converts enum to the string equal to enum's name. 13 | String toShortString() => toString().split('.').last; 14 | } 15 | 16 | /// Fetches user from Firebase and returns a promise. 17 | Future> fetchUser( 18 | SupabaseClient instance, 19 | String userId, 20 | String usersTableName, 21 | String schema, { 22 | String? role, 23 | }) async { 24 | final data = (await instance 25 | .schema(schema) 26 | .from(usersTableName) 27 | .select() 28 | .eq('id', userId) 29 | .limit(1)) 30 | .first; 31 | data['role'] = role; 32 | return data; 33 | } 34 | 35 | /// Returns a list of [types.Room] created from Firebase query. 36 | /// If room has 2 participants, sets correct room name and image. 37 | Future> processRoomsRows( 38 | User supabaseUser, 39 | SupabaseClient instance, 40 | List rows, 41 | String usersTableName, 42 | String schema, 43 | ) async => 44 | await Future.wait( 45 | rows.map( 46 | (doc) => processRoomRow( 47 | doc, 48 | supabaseUser, 49 | instance, 50 | usersTableName, 51 | schema, 52 | ), 53 | ), 54 | ); 55 | 56 | /// Returns a [types.Room] created from Firebase document. 57 | Future processRoomRow( 58 | Map data, 59 | User supabaseUser, 60 | SupabaseClient instance, 61 | String usersTableName, 62 | String schema, 63 | ) async { 64 | var imageUrl = data['imageUrl'] as String?; 65 | var name = data['name'] as String?; 66 | final type = data['type'] as String; 67 | final userIds = data['userIds'] as List; 68 | final userRoles = data['userRoles'] as Map?; 69 | final users = data['users']?.map( 70 | (e) { 71 | e['role'] = userRoles?[e['id']]; 72 | return e; 73 | }, 74 | ).toList() ?? 75 | await Future.wait( 76 | userIds.map( 77 | (userId) => fetchUser( 78 | instance, 79 | userId as String, 80 | usersTableName, 81 | schema, 82 | role: userRoles?[userId] as String?, 83 | ), 84 | ), 85 | ); 86 | if (type == types.RoomType.direct.toShortString()) { 87 | final index = users.indexWhere( 88 | (u) => u['id'] != supabaseUser.id, 89 | ); 90 | if (index >= 0) { 91 | final otherUser = users[index]; 92 | imageUrl = otherUser['imageUrl'] as String?; 93 | name = '${otherUser['firstName'] ?? ''} ${otherUser['lastName'] ?? ''}' 94 | .trim(); 95 | } 96 | } 97 | data['imageUrl'] = imageUrl; 98 | data['name'] = name; 99 | data['users'] = users; 100 | data['id'] = data['id'].toString(); 101 | if (data['lastMessages'] != null) { 102 | final lastMessages = data['lastMessages'].map((lm) { 103 | final author = users.firstWhere( 104 | (u) => u['id'] == lm['authorId'], 105 | orElse: () => {'id': lm['authorId'] as String}, 106 | ); 107 | lm['author'] = author; 108 | lm['id'] = lm['id'].toString(); 109 | lm['roomId'] = lm['roomId'].toString(); 110 | return lm; 111 | }).toList(); 112 | data['lastMessages'] = lastMessages; 113 | } 114 | return types.Room.fromJson(data); 115 | } 116 | -------------------------------------------------------------------------------- /lib/src/widgets/user_online_status.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import '../../flutter_supabase_chat_core.dart'; 3 | 4 | /// This widget allows you to observe the online/offline status of a specific user 5 | class UserOnlineStatusWidget extends StatelessWidget { 6 | /// [uid] of the user to be observed 7 | final String uid; 8 | 9 | /// [builder] the function that is called at each user state change event 10 | final Widget Function(UserOnlineStatus status) builder; 11 | 12 | /// [UserOnlineStatusWidget] Constructor 13 | /// Required [uid] : uid of the user to be observed 14 | /// Required [builder] : is the function that is called at each user state change event 15 | const UserOnlineStatusWidget({ 16 | super.key, 17 | required this.uid, 18 | required this.builder, 19 | }); 20 | 21 | @override 22 | Widget build(BuildContext context) => StreamBuilder( 23 | stream: SupabaseChatCore.instance.userOnlineStatus(uid), 24 | initialData: UserOnlineStatus.offline, 25 | builder: (context, snapshot) => 26 | builder(snapshot.data ?? UserOnlineStatus.offline), 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /lib/src/widgets/user_status_detector/other_platform.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'platforms_widgets_binding.dart'; 4 | 5 | PlatformsWidgetsBinding getInstance() => OtherPlatformsWidgetsBinding(); 6 | 7 | class OtherPlatformsWidgetsBinding extends PlatformsWidgetsBinding { 8 | @override 9 | void addObserver(WidgetsBindingObserver state) { 10 | WidgetsBinding.instance.addObserver(state); 11 | } 12 | 13 | @override 14 | void removeObserver(WidgetsBindingObserver state) { 15 | WidgetsBinding.instance.removeObserver(state); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/src/widgets/user_status_detector/platforms_widgets_binding.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | abstract class PlatformsWidgetsBinding { 4 | void addObserver(WidgetsBindingObserver state); 5 | void removeObserver(WidgetsBindingObserver state); 6 | } 7 | -------------------------------------------------------------------------------- /lib/src/widgets/user_status_detector/web_platform.dart: -------------------------------------------------------------------------------- 1 | // ignore: avoid_web_libraries_in_flutter 2 | import 'dart:html'; 3 | 4 | import 'package:flutter/material.dart'; 5 | 6 | import 'platforms_widgets_binding.dart'; 7 | 8 | PlatformsWidgetsBinding getInstance() => WebWidgetsBinding(); 9 | 10 | class WebWidgetsBinding extends PlatformsWidgetsBinding { 11 | @override 12 | void addObserver(WidgetsBindingObserver state) { 13 | window.addEventListener('focus', (event) => onFocus(event, state)); 14 | window.addEventListener('blur', (event) => onBlur(event, state)); 15 | } 16 | 17 | @override 18 | void removeObserver(WidgetsBindingObserver state) { 19 | window.removeEventListener('focus', (event) => onFocus(event, state)); 20 | window.removeEventListener('blur', (event) => onBlur(event, state)); 21 | } 22 | 23 | void onFocus(Event e, WidgetsBindingObserver state) { 24 | state.didChangeAppLifecycleState(AppLifecycleState.resumed); 25 | } 26 | 27 | void onBlur(Event e, WidgetsBindingObserver state) { 28 | state.didChangeAppLifecycleState(AppLifecycleState.paused); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/src/widgets/user_status_detector/widgets_binding.dart: -------------------------------------------------------------------------------- 1 | import 'other_platform.dart' if (dart.library.html) './web_platform.dart'; 2 | import 'platforms_widgets_binding.dart'; 3 | 4 | PlatformsWidgetsBinding get widgetsBinding => getInstance(); 5 | -------------------------------------------------------------------------------- /lib/src/widgets/user_status_observer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../class/supabase_chat_core.dart'; 4 | import '../class/user_online_status.dart'; 5 | import 'user_status_detector/widgets_binding.dart'; 6 | 7 | /// This widget takes care of observing the state of the application if it is foregrounded 8 | /// or if it is reduced. At each state change it notifies on the Supabase Realtime channel the state 9 | /// online/offline of the user 10 | class UserOnlineStateObserver extends StatefulWidget { 11 | /// [child] Child widgets in the widget tree 12 | final Widget child; 13 | 14 | /// [UserOnlineStateObserver] Widget constructor 15 | const UserOnlineStateObserver({ 16 | super.key, 17 | required this.child, 18 | }); 19 | 20 | @override 21 | State createState() => 22 | _UserOnlineStateObserverState(); 23 | } 24 | 25 | class _UserOnlineStateObserverState extends State 26 | with WidgetsBindingObserver { 27 | @override 28 | void initState() { 29 | widgetsBinding.addObserver(this); 30 | super.initState(); 31 | } 32 | 33 | @override 34 | void dispose() { 35 | widgetsBinding.removeObserver(this); 36 | super.dispose(); 37 | } 38 | 39 | @override 40 | void didChangeAppLifecycleState(AppLifecycleState state) { 41 | switch (state) { 42 | case AppLifecycleState.resumed: 43 | SupabaseChatCore.instance.setPresenceStatus(UserOnlineStatus.online); 44 | break; 45 | case AppLifecycleState.inactive: 46 | case AppLifecycleState.paused: 47 | case AppLifecycleState.hidden: 48 | case AppLifecycleState.detached: 49 | SupabaseChatCore.instance.setPresenceStatus(UserOnlineStatus.offline); 50 | break; 51 | } 52 | } 53 | 54 | @override 55 | Widget build(BuildContext context) => widget.child; 56 | } 57 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_supabase_chat_core 2 | description: > 3 | Actively maintained, community-driven Supabase BaaS for chat applications 4 | with an optional chat UI. 5 | version: 1.5.0 6 | homepage: https://flutter-supabase-chat-core.insideapp.it 7 | repository: https://github.com/insideapp-srl/flutter_supabase_chat_core 8 | 9 | environment: 10 | sdk: '>=3.4.0 <4.0.0' 11 | flutter: '>=3.22.0' 12 | 13 | dependencies: 14 | flutter: 15 | sdk: flutter 16 | flutter_chat_types: ^3.6.2 17 | meta: ^1.15.0 18 | mime: '>=1.0.2 <3.0.0' 19 | supabase_flutter: ^2.8.3 20 | uuid: ^4.5.1 21 | 22 | dev_dependencies: 23 | flutter_lints: ^5.0.0 24 | 25 | screenshots: 26 | - description: 'Flutter Supabase Chat Core - screenshot 1' 27 | path: doc/static/img/flayer_chat_ui_screenshot.png --------------------------------------------------------------------------------