├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── documentation_improvements.md │ ├── feature_request.md │ └── questions.md ├── .gitignore ├── .metadata ├── .pubignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── documentation.md ├── example ├── catalog │ ├── .gitignore │ ├── .metadata │ ├── README.md │ ├── android │ │ ├── .gitignore │ │ ├── app │ │ │ ├── build.gradle │ │ │ └── src │ │ │ │ ├── debug │ │ │ │ └── AndroidManifest.xml │ │ │ │ ├── main │ │ │ │ ├── AndroidManifest.xml │ │ │ │ ├── kotlin │ │ │ │ │ └── com │ │ │ │ │ │ └── example │ │ │ │ │ │ └── catalog2 │ │ │ │ │ │ └── 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 │ ├── catalog.gif │ ├── ios │ │ ├── .gitignore │ │ ├── Flutter │ │ │ ├── AppFrameworkInfo.plist │ │ │ ├── Debug.xcconfig │ │ │ └── Release.xcconfig │ │ ├── Runner.xcodeproj │ │ │ ├── project.pbxproj │ │ │ ├── project.xcworkspace │ │ │ │ ├── contents.xcworkspacedata │ │ │ │ └── xcshareddata │ │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ │ └── WorkspaceSettings.xcsettings │ │ │ └── xcshareddata │ │ │ │ └── xcschemes │ │ │ │ └── Runner.xcscheme │ │ ├── Runner.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ └── WorkspaceSettings.xcsettings │ │ └── Runner │ │ │ ├── AppDelegate.swift │ │ │ ├── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ ├── Contents.json │ │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ │ ├── Icon-App-20x20@1x.png │ │ │ │ ├── Icon-App-20x20@2x.png │ │ │ │ ├── Icon-App-20x20@3x.png │ │ │ │ ├── Icon-App-29x29@1x.png │ │ │ │ ├── Icon-App-29x29@2x.png │ │ │ │ ├── Icon-App-29x29@3x.png │ │ │ │ ├── Icon-App-40x40@1x.png │ │ │ │ ├── Icon-App-40x40@2x.png │ │ │ │ ├── Icon-App-40x40@3x.png │ │ │ │ ├── Icon-App-60x60@2x.png │ │ │ │ ├── Icon-App-60x60@3x.png │ │ │ │ ├── Icon-App-76x76@1x.png │ │ │ │ ├── Icon-App-76x76@2x.png │ │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ │ └── LaunchImage.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── LaunchImage.png │ │ │ │ ├── LaunchImage@2x.png │ │ │ │ ├── LaunchImage@3x.png │ │ │ │ └── README.md │ │ │ ├── Base.lproj │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ │ ├── Info.plist │ │ │ └── Runner-Bridging-Header.h │ ├── lib │ │ ├── catalog_main_page.dart │ │ ├── main.dart │ │ └── product.dart │ ├── pubspec.yaml │ └── web │ │ ├── index.html │ │ └── manifest.json ├── random_numbers │ ├── .gitignore │ ├── .metadata │ ├── README.md │ ├── android │ │ ├── .gitignore │ │ ├── app │ │ │ ├── build.gradle │ │ │ └── src │ │ │ │ ├── debug │ │ │ │ └── AndroidManifest.xml │ │ │ │ ├── main │ │ │ │ ├── AndroidManifest.xml │ │ │ │ ├── kotlin │ │ │ │ │ └── com │ │ │ │ │ │ └── example │ │ │ │ │ │ └── tracking2 │ │ │ │ │ │ └── MainActivity.kt │ │ │ │ └── res │ │ │ │ │ ├── drawable-v21 │ │ │ │ │ └── launch_background.xml │ │ │ │ │ ├── drawable │ │ │ │ │ └── launch_background.xml │ │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── values-night │ │ │ │ │ └── styles.xml │ │ │ │ │ └── values │ │ │ │ │ └── styles.xml │ │ │ │ └── profile │ │ │ │ └── AndroidManifest.xml │ │ ├── build.gradle │ │ ├── gradle.properties │ │ ├── gradle │ │ │ └── wrapper │ │ │ │ └── gradle-wrapper.properties │ │ └── settings.gradle │ ├── ios │ │ ├── .gitignore │ │ ├── Flutter │ │ │ ├── AppFrameworkInfo.plist │ │ │ ├── Debug.xcconfig │ │ │ └── Release.xcconfig │ │ ├── Runner.xcodeproj │ │ │ ├── project.pbxproj │ │ │ ├── project.xcworkspace │ │ │ │ ├── contents.xcworkspacedata │ │ │ │ └── xcshareddata │ │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ │ └── WorkspaceSettings.xcsettings │ │ │ └── xcshareddata │ │ │ │ └── xcschemes │ │ │ │ └── Runner.xcscheme │ │ ├── Runner.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ └── WorkspaceSettings.xcsettings │ │ └── Runner │ │ │ ├── AppDelegate.swift │ │ │ ├── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ ├── Contents.json │ │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ │ ├── Icon-App-20x20@1x.png │ │ │ │ ├── Icon-App-20x20@2x.png │ │ │ │ ├── Icon-App-20x20@3x.png │ │ │ │ ├── Icon-App-29x29@1x.png │ │ │ │ ├── Icon-App-29x29@2x.png │ │ │ │ ├── Icon-App-29x29@3x.png │ │ │ │ ├── Icon-App-40x40@1x.png │ │ │ │ ├── Icon-App-40x40@2x.png │ │ │ │ ├── Icon-App-40x40@3x.png │ │ │ │ ├── Icon-App-60x60@2x.png │ │ │ │ ├── Icon-App-60x60@3x.png │ │ │ │ ├── Icon-App-76x76@1x.png │ │ │ │ ├── Icon-App-76x76@2x.png │ │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ │ └── LaunchImage.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── LaunchImage.png │ │ │ │ ├── LaunchImage@2x.png │ │ │ │ ├── LaunchImage@3x.png │ │ │ │ └── README.md │ │ │ ├── Base.lproj │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ │ ├── Info.plist │ │ │ └── Runner-Bridging-Header.h │ ├── lib │ │ ├── main.dart │ │ └── random_numbers_app.dart │ ├── pubspec.yaml │ ├── random_numbers.gif │ └── web │ │ ├── index.html │ │ └── manifest.json └── simple_chat │ ├── .gitignore │ ├── .metadata │ ├── README.md │ ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── chat2 │ │ │ │ │ └── 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 │ ├── chat.gif │ ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── Runner-Bridging-Header.h │ ├── lib │ ├── MessageWidget.dart │ ├── conversation_page.dart │ ├── main.dart │ └── signin_page.dart │ ├── pubspec.yaml │ └── web │ ├── index.html │ └── manifest.json ├── lib ├── constants.dart ├── data │ └── models │ │ ├── askless_error_model.dart │ │ └── internal_response_model.dart ├── domain │ ├── entities │ │ ├── internal_response_cli_entity.dart │ │ └── response_entity.dart │ ├── services │ │ ├── authenticate_service.dart │ │ ├── call_service.dart │ │ ├── connection_service.dart │ │ └── requests_service.dart │ └── utils │ │ └── logger.dart ├── index.dart ├── injection_container.dart ├── middleware │ ├── ListeningHandler.dart │ ├── data │ │ ├── Mappable.dart │ │ ├── connection │ │ │ └── PingPong.dart │ │ ├── receivements │ │ │ ├── AuthenticateResponseCli.dart │ │ │ ├── ConfigureConnectionResponseCli.dart │ │ │ ├── NewDataForListener.dart │ │ │ ├── ServerConfirmReceiptCli.dart │ │ │ ├── StopListeningEvent.dart │ │ │ └── askless_error_entity.dart │ │ └── request │ │ │ ├── AbstractRequestCli.dart │ │ │ ├── AuthenticateRequestCli.dart │ │ │ ├── ClientConfirmReceiptCli.dart │ │ │ ├── ConfigureConnectionRequestCli.dart │ │ │ └── OperationRequestCli.dart │ ├── receivements │ │ ├── ClientReceived.dart │ │ ├── ClientReceivedIgnore.dart │ │ ├── ClientReceivedNewDataForListener.dart │ │ ├── ClientReceivedResponse.dart │ │ ├── ClientReceivedServerConfirmReceipt.dart │ │ └── ClientReceivedStopListeningEvent.dart │ └── ws_channel │ │ ├── AbstractIOWsChannel.dart │ │ └── IOWsChannel.dart └── tasks │ ├── ReconnectClientWhenDidNotReceivePongFromServerTask.dart │ ├── ReconnectWhenOffline.dart │ ├── SendMessageToServerAgainTask.dart │ ├── SendPingTask.dart │ └── TimedTask.dart └── pubspec.yaml /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior. 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Additional context** 20 | Add any other context about the problem here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation_improvements.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Documentation improvements 3 | about: Help to improve the documentation 4 | title: '' 5 | labels: documentation 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Language** 11 | Type here the documentation's language you read: Portuguese or English 12 | 13 | **Description** 14 | A clear and concise description of what you think can be improved, is there an excerpt from the documentation that you couldn't understand? Is something missing in the documentation? 15 | 16 | **Additional information (optional)** 17 | Extra information, like how the documentation could be instead. If there's an excerpt from documentation that you couldn't understand, please copy and paste it here -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/questions.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Questions 3 | about: Ask a question 4 | title: '' 5 | labels: question 6 | assignees: '' 7 | 8 | --- 9 | 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | pubspec.lock 4 | 5 | # Miscellaneous 6 | *.class 7 | *.log 8 | *.pyc 9 | *.swp 10 | .DS_Store 11 | .atom/ 12 | .buildlog/ 13 | .history 14 | .svn/ 15 | 16 | # IntelliJ related 17 | *.iml 18 | *.ipr 19 | *.iws 20 | .idea/ 21 | 22 | # The .vscode folder contains launch configuration and tasks you configure in 23 | # VS Code which you may wish to be included in version control, so this line 24 | # is commented out by default. 25 | #.vscode/ 26 | 27 | # Flutter/Dart/Pub related 28 | **/doc/api/ 29 | .dart_tool/ 30 | .flutter-plugins 31 | .flutter-plugins-dependencies 32 | .packages 33 | .pub-cache/ 34 | .pub/ 35 | /build/ 36 | 37 | # Web related 38 | lib/generated_plugin_registrant.dart 39 | 40 | # Exceptions to above rules. 41 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 42 | -------------------------------------------------------------------------------- /.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: f139b11009aeb8ed2a3a3aa8b0066e482709dde3 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /.pubignore: -------------------------------------------------------------------------------- 1 | example/ -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 3.1.1 2 | - Bug fixes 3 | 4 | # 3.1.0 5 | - Dependencies have been updated 6 | 7 | # 3.0.2 8 | - Error message was appearing in the wrong moment 9 | 10 | # 3.0.1 11 | - Fix: "Ops, it should perform configure connection first" 12 | 13 | # 3.0.0 14 | Askless was redesigned and is even better! So you can build your App and backend like a pro! 15 | - Several bug fixes 16 | - A new way of building real-time Apps 17 | - Askless now allows you to elevate your App by adding video and audio calls to your Flutter App! 18 | Please follow the [documentation](./documentation.md) to check all changes 19 | 20 | # 2.0.0 21 | - dart sdk updated: >=2.12.0 <3.0.0 (null safety) 22 | - web support 23 | - useDefaultLogger: default as false 24 | - fix: accepting non-maps types on the "body" field of an update request 25 | - other adjustments/fixes 26 | - documentation and README changes 27 | - code refactoring 28 | - unit tests added 29 | - examples updated to null safety 30 | - new dependencies in pubspec.yaml 31 | 32 | ## 1.0.1 33 | 34 | - Fix: prevent old connections data from getting in the way of the last successful connection attempt 35 | - Fix on removing old messages received from server 36 | - Gifs added to examples 37 | - Allowing not encrypted connections on Android & IOS on examples 38 | - Flutter 2.0 examples updates 39 | - Dependencies upgraded 40 | - Readme updated: How to use a not encrypted connection on a test environment 41 | - `readAndBuild` and `listenAndBuild` methods: a new key is set for StreamBuilder/FutureBuilder if the key param is null 42 | - Showing the request error response on logs 43 | 44 | ## 1.0.0 45 | 46 | - Project release 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Rodrigo João Bertotti 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Askless for Flutter 2 | 3 | Build Flutter Apps with PostgreSQL, MySQL, or any database, stream data changes through websockets effortlessly, and handle websocket authentication like a pro 4 | 5 | 🌟 If you want to build your Flutter App without Firebase and stream data from a database (like SQL) to the Flutter App, 6 | Askless will save you a lot of time! 7 | 8 | This is the Flutter side, 9 | **[click here to access the Backend side in Node.js](https://github.com/RodrigoBertotti/Askless)** 10 | 11 | ## Built with Askless 12 | 13 | Check the example of a [Flutter Chat App with PostgreSQL / MySQL databases and Node.js](https://github.com/RodrigoBertotti/flutter_chat_app_with_nodejs). 14 | 15 | https://github.com/RodrigoBertotti/flutter_chat_app_with_nodejs/assets/15431956/42428123-76ab-4c5c-8ba1-29321d11b74b 16 | 17 | 🔊 The video above contains audio, click on the right side to turn it on 18 | 19 | ## Why Askless? 20 | 21 | ### :muscle: **Stream data from PostgreSQL, MySQL or other database** to your Flutter App through websockets 22 | 23 | The Askless Framework (Backend in Node.js) is designed to make your Flutter App 24 | stream data in without Firebase, create your own routes that interact with your database, 25 | call `route.notifyChanges(..)` when something changes so the users will receive the changes! 26 | 27 | ### :airplane: Skip websocket details and focus in your application! 28 | 29 | Askless Design is a result of thorough analysis to make websockets in Flutter easy and productive. 30 | It can be tricky to build websockets to transport data from a database from scratch for your Flutter App: 31 | 32 | :grey_exclamation: you need to add logic to handle different kinds of data that will move through the websockets 33 | 34 | :grey_exclamation: **Authentication events come first**, so you don't want to your client receive data he doesn't have permission to. 35 | 36 | :grey_exclamation: If the websockets disconnect, either because of the user device or because of the server, we need to 37 | **try to connect back again** as soon as possible 38 | 39 | :grey_exclamation: Ops! The user was receiving data by websockets, but he lost connection for a second and connected again, 40 | now we need to **handle authentication BEFORE sending data again** 41 | 42 | :grey_exclamation: We may want to know once the user started streaming data and when the user received data, so we can know 43 | data was received, so an additional event like "message_received" needs to be added and handled in the App and backend, more work to 44 | simply add a double-check icon in your chat app once the message is delivered 45 | 46 | :white_check_mark: **You don't need to mull over implementing these details anymore** :grinning: **Askless is here to make your life easier!** 47 | 48 | 49 | ## Important links 50 | * [Askless Backend in Node.js](https://github.com/RodrigoBertotti/askless) the backend side of this Flutter client 51 | * [Documentation](documentation.md) 52 | * [Askless Node.js Server](https://github.com/RodrigoBertotti/Askless) 53 | 54 | #### Examples 55 | * Level: :red_circle: :white_circle: :white_circle: :white_circle: :white_circle: [Flutter Random Numbers Example](example/random_numbers): Random numbers are generated on the server. 56 | * Level: :red_circle: :red_circle: :white_circle: :white_circle: :white_circle: [Flutter Simple Chat Example](example/simple_chat): Simple chat between the colors blue and green. 57 | * Level: :red_circle: :red_circle: :red_circle: :white_circle: :white_circle: [Flutter Catalog Example](example/catalog): Users adding and removing products from a catalog. 58 | * Level: :red_circle: :red_circle: :red_circle: :red_circle: :red_circle: [Flutter Chat App with MySQL or PostgreSQL](https://github.com/RodrigoBertotti/flutter_chat_app_with_nodejs): A Flutter Chat App with MySQL, WebSockets, and Node.js 59 | 60 | ## Getting Started 61 | 62 | The "Getting Started" is an example of the Flutter client, 63 | an example is executed locally. 64 | 65 | **1 -** First create the server, [click here](https://github.com/RodrigoBertotti/askless) and 66 | follow the server instructions in the section "Getting Started" 67 | 68 | **2 -** (Optional) To use an unencrypted connection in a **test environment** such as this example 69 | (`ws://` connection instead of `wss://`) [follow these instructions](https://flutter.dev/docs/release/breaking-changes/network-policy-ios-android). **Do not apply this on a production environment.** 70 | 71 | 72 | 73 | **3 -** Install 74 | 75 | pubspec.yaml: 76 | 77 | dependencies: 78 | flutter: 79 | sdk: flutter 80 | 81 | # Add this line: 82 | askless: ^3.1.1 83 | 84 | **4 -** Import the package 85 | 86 | import 'package:askless/askless.dart'; 87 | 88 | **5 -** Initialize 89 | informing the server URL with port (default: 3000). 90 | You can also access the `myAsklessServer.localUrl` attribute on your server-side in node.js 91 | to discover what the local URL of your server is. 92 | 93 | **6 -** Start Askless with `AsklessClient.instance.start()` 94 | 95 | Example: 96 | 97 | void main() { 98 | AsklessClient.instance.start(serverUrl:"ws://192.168.0.8:3000"); // TODO: replace with the URL of your Askless Server 99 | runApp(MyApp()); 100 | } 101 | 102 | **7 -** Here we go! Now you can start using building your Flutter App with Askless, 103 | check the **[documentation](documentation.md)** and **[examples](#Examples)**! 104 | 105 | ## Thanks 106 | Thank you for using Askless! 107 | 108 | Thanks also ALL the developers who developed the libraries that Askless 109 | rely on and the authors of these two very good articles! [A Comprehensive Guide to Flutter WebRTC](https://www.100ms.live/blog/flutter-webrtc) and [Flutter-WebRTC: A Complete Guide](https://www.videosdk.live/blog/flutter-webrtc). 110 | 111 | ## License 112 | 113 | [MIT](LICENSE.txt) 114 | -------------------------------------------------------------------------------- /example/catalog/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | -------------------------------------------------------------------------------- /example/catalog/.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: f4abaa0735eba4dfd8f33f73363911d63931fe03 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/catalog/README.md: -------------------------------------------------------------------------------- 1 | # catalog 2 | 3 | #### An example of handling authentication, streaming and security 4 | 5 | This is the client side in Flutter, 6 | **[click here](https://github.com/RodrigoBertotti/Askless/tree/dev/example/random-numbers-ts)** 7 | to access the backend side in Node.js 8 | 9 | In this example, unauthenticated users can only see changes, only authenticated users can perform 10 | changes. 11 | 12 | 13 | -------------------------------------------------------------------------------- /example/catalog/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 | -------------------------------------------------------------------------------- /example/catalog/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 33 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | 35 | defaultConfig { 36 | applicationId "com.example.catalog" 37 | minSdkVersion 21 38 | targetSdkVersion 30 39 | versionCode flutterVersionCode.toInteger() 40 | versionName flutterVersionName 41 | } 42 | 43 | buildTypes { 44 | release { 45 | signingConfig signingConfigs.debug 46 | } 47 | } 48 | } 49 | 50 | flutter { 51 | source '../..' 52 | } 53 | 54 | dependencies { 55 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 56 | } 57 | -------------------------------------------------------------------------------- /example/catalog/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/catalog/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 13 | 17 | 21 | 26 | 30 | 31 | 32 | 33 | 34 | 35 | 37 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /example/catalog/android/app/src/main/kotlin/com/example/catalog2/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.catalog 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /example/catalog/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/catalog/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/catalog/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/catalog/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/catalog/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/catalog/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/catalog/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/catalog/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/catalog/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/catalog/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/catalog/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/catalog/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/catalog/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/catalog/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/catalog/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/catalog/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.7.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:7.3.1' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | mavenCentral() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | tasks.register("clean", Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /example/catalog/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M \ 2 | --add-exports=java.base/sun.nio.ch=ALL-UNNAMED \ 3 | --add-opens=java.base/java.lang=ALL-UNNAMED \ 4 | --add-opens=java.base/java.lang.reflect=ALL-UNNAMED \ 5 | --add-opens=java.base/java.io=ALL-UNNAMED \ 6 | --add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED 7 | android.useAndroidX=true 8 | android.enableJetifier=true -------------------------------------------------------------------------------- /example/catalog/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip 7 | -------------------------------------------------------------------------------- /example/catalog/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /example/catalog/catalog.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/catalog/catalog.gif -------------------------------------------------------------------------------- /example/catalog/ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/ephemeral/ 22 | Flutter/app.flx 23 | Flutter/app.zip 24 | Flutter/flutter_assets/ 25 | Flutter/flutter_export_environment.sh 26 | ServiceDefinitions.json 27 | Runner/GeneratedPluginRegistrant.* 28 | 29 | # Exceptions to above rules. 30 | !default.mode1v3 31 | !default.mode2v3 32 | !default.pbxuser 33 | !default.perspectivev3 34 | -------------------------------------------------------------------------------- /example/catalog/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/catalog/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/catalog/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/catalog/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/catalog/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/catalog/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/catalog/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /example/catalog/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/catalog/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/catalog/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/catalog/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/catalog/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/catalog/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/catalog/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/catalog/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/catalog/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/catalog/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/catalog/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/catalog/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/catalog/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/catalog/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/catalog/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/catalog/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/catalog/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/catalog/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/catalog/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/catalog/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/catalog/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/catalog/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/catalog/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/catalog/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/catalog/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/catalog/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/catalog/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/catalog/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/catalog/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/catalog/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/catalog/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/catalog/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/catalog/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/catalog/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/catalog/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/catalog/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/catalog/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/catalog/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/catalog/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/catalog/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/catalog/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/catalog/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/catalog/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/catalog/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/catalog/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/catalog/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | catalog 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /example/catalog/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/catalog/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:askless/index.dart'; 3 | import 'catalog_main_page.dart'; 4 | 5 | 6 | final serverUrl = ''; // TODO: <-- replace with your nodejs backend URL here like: 7 | // const serverUrl = 'ws://192.168.0.8:3000'; 8 | 9 | void main() { 10 | assert(serverUrl.isNotEmpty, "replace \"serverUrl\" with your nodejs backend URL like: 'ws://192.168.0.8:3000'"); 11 | AsklessClient.instance.start( 12 | serverUrl: serverUrl, 13 | debugLogs: false, 14 | ); 15 | runApp(const CatalogApp()); 16 | } 17 | 18 | class CatalogApp extends StatelessWidget { 19 | const CatalogApp({super.key}); 20 | 21 | @override 22 | Widget build(BuildContext context) { 23 | return MaterialApp( 24 | title: 'Catalog', 25 | debugShowCheckedModeBanner: false, 26 | theme: ThemeData( 27 | primarySwatch: Colors.lightBlue, 28 | ), 29 | home: CatalogMainPage(), 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /example/catalog/lib/product.dart: -------------------------------------------------------------------------------- 1 | 2 | class Product{ 3 | int? id; 4 | late final String name; 5 | late final int price; 6 | 7 | Product({required this.name, required this.price}); 8 | 9 | Product.fromMap(Map map){ 10 | this.id = map['id']; 11 | this.name = map['name']; 12 | this.price = map['price']; 13 | } 14 | 15 | 16 | static List fromMapList(mapList) { 17 | final List res = []; 18 | (mapList ?? []).forEach((mapItem) => res.add(Product.fromMap(mapItem))); 19 | return res; 20 | } 21 | 22 | toMap() { 23 | return { 24 | 'id' : id, 25 | 'name' : name, 26 | 'price' : price 27 | }; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /example/catalog/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: catalog 2 | description: An example of the framework's use 3 | 4 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 5 | version: 1.0.0+1 6 | 7 | environment: 8 | sdk: '>=3.0.5 <4.0.0' 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | askless: 14 | path: ../../ 15 | 16 | flutter: 17 | uses-material-design: true 18 | -------------------------------------------------------------------------------- /example/catalog/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | catalog 27 | 28 | 29 | 30 | 33 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /example/catalog/web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "catalog", 3 | "short_name": "catalog", 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 | } 24 | -------------------------------------------------------------------------------- /example/random_numbers/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | -------------------------------------------------------------------------------- /example/random_numbers/.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: f4abaa0735eba4dfd8f33f73363911d63931fe03 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/random_numbers/README.md: -------------------------------------------------------------------------------- 1 | # Random Numbers App 2 | 3 | #### An example of streaming random numbers generated by the Askless Backend (Node.js) to the Flutter App 4 | 5 | This is the client side in Flutter, 6 | **[click here](https://github.com/RodrigoBertotti/Askless/tree/dev/example/random-numbers-ts)** 7 | to access the backend side in Node.js 8 | 9 | ![Alt Text](random_numbers.gif) 10 | -------------------------------------------------------------------------------- /example/random_numbers/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 | -------------------------------------------------------------------------------- /example/random_numbers/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 33 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | 35 | defaultConfig { 36 | applicationId "com.example.random_numbers" 37 | minSdkVersion 21 38 | targetSdkVersion 30 39 | versionCode flutterVersionCode.toInteger() 40 | versionName flutterVersionName 41 | } 42 | 43 | buildTypes { 44 | release { 45 | // Signing with the debug keys for now, so `flutter run --release` works. 46 | signingConfig signingConfigs.debug 47 | } 48 | } 49 | } 50 | 51 | flutter { 52 | source '../..' 53 | } 54 | 55 | dependencies { 56 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 57 | } 58 | -------------------------------------------------------------------------------- /example/random_numbers/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/random_numbers/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 13 | 17 | 21 | 26 | 30 | 31 | 32 | 33 | 34 | 35 | 37 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /example/random_numbers/android/app/src/main/kotlin/com/example/tracking2/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.random_numbers 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /example/random_numbers/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/random_numbers/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/random_numbers/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/random_numbers/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/random_numbers/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/random_numbers/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/random_numbers/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/random_numbers/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/random_numbers/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/random_numbers/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/random_numbers/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/random_numbers/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/random_numbers/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/random_numbers/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/random_numbers/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/random_numbers/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.7.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:7.3.1' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | mavenCentral() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | tasks.register("clean", Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /example/random_numbers/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M \ 2 | --add-exports=java.base/sun.nio.ch=ALL-UNNAMED \ 3 | --add-opens=java.base/java.lang=ALL-UNNAMED \ 4 | --add-opens=java.base/java.lang.reflect=ALL-UNNAMED \ 5 | --add-opens=java.base/java.io=ALL-UNNAMED \ 6 | --add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED 7 | android.useAndroidX=true 8 | android.enableJetifier=true 9 | -------------------------------------------------------------------------------- /example/random_numbers/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip 7 | -------------------------------------------------------------------------------- /example/random_numbers/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /example/random_numbers/ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/ephemeral/ 22 | Flutter/app.flx 23 | Flutter/app.zip 24 | Flutter/flutter_assets/ 25 | Flutter/flutter_export_environment.sh 26 | ServiceDefinitions.json 27 | Runner/GeneratedPluginRegistrant.* 28 | 29 | # Exceptions to above rules. 30 | !default.mode1v3 31 | !default.mode2v3 32 | !default.pbxuser 33 | !default.perspectivev3 34 | -------------------------------------------------------------------------------- /example/random_numbers/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/random_numbers/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/random_numbers/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/random_numbers/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/random_numbers/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/random_numbers/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/random_numbers/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /example/random_numbers/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/random_numbers/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/random_numbers/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/random_numbers/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/random_numbers/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/random_numbers/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/random_numbers/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/random_numbers/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/random_numbers/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/random_numbers/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/random_numbers/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/random_numbers/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/random_numbers/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/random_numbers/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/random_numbers/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/random_numbers/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/random_numbers/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/random_numbers/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/random_numbers/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/random_numbers/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/random_numbers/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/random_numbers/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/random_numbers/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/random_numbers/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/random_numbers/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/random_numbers/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/random_numbers/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/random_numbers/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/random_numbers/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/random_numbers/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/random_numbers/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/random_numbers/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/random_numbers/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/random_numbers/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/random_numbers/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/random_numbers/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/random_numbers/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/random_numbers/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/random_numbers/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/random_numbers/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/random_numbers/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/random_numbers/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/random_numbers/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/random_numbers/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/random_numbers/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/random_numbers/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | random_numbers 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /example/random_numbers/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/random_numbers/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:askless/index.dart'; 3 | import 'random_numbers_app.dart'; 4 | 5 | const serverUrl = ''; // TODO: <-- replace with your nodejs backend URL here like: 6 | // const serverUrl = 'ws://192.168.0.8:3000'; 7 | 8 | void main() { 9 | assert(serverUrl.isNotEmpty, "replace \"serverUrl\" with your nodejs backend URL like: 'ws://192.168.0.8:3000'"); 10 | AsklessClient.instance.start(serverUrl: serverUrl, debugLogs: false); 11 | runApp(const RandomNumbersApp()); 12 | } 13 | 14 | -------------------------------------------------------------------------------- /example/random_numbers/lib/random_numbers_app.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:askless/index.dart'; 3 | 4 | 5 | class RandomNumbersApp extends StatelessWidget { 6 | const RandomNumbersApp({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return MaterialApp( 11 | title: 'Random Numbers Generated by the Server', 12 | home: _MyHomePage(), 13 | ); 14 | } 15 | } 16 | 17 | class _MyHomePage extends StatefulWidget { 18 | @override 19 | _MyHomePageState createState() => _MyHomePageState(); 20 | } 21 | 22 | class _MyHomePageState extends State<_MyHomePage> { 23 | final _textStyle = const TextStyle(fontSize: 22); 24 | 25 | @override 26 | Widget build(BuildContext context) { 27 | return Scaffold( 28 | appBar: AppBar( 29 | title: const Text("Server Generated Numbers"), 30 | ), 31 | body: Center( 32 | child: Column( 33 | mainAxisAlignment: MainAxisAlignment.center, 34 | children: [ 35 | StreamBuilder( 36 | stream: AsklessClient.instance.readStream(route: 'generated-random-number',), 37 | builder: (context, snapshot) { 38 | if(!snapshot.hasData) { 39 | return Container(); 40 | } 41 | return Text(snapshot.data["currentRandomNumber"].toString(), style: _textStyle); 42 | } 43 | ) 44 | ], 45 | ), 46 | ) 47 | ); 48 | } 49 | 50 | @override 51 | void initState() { 52 | super.initState(); 53 | } 54 | 55 | @override 56 | void dispose() { 57 | print("dispose"); 58 | super.dispose(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /example/random_numbers/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: random_numbers 2 | description: Simple random-numbers-ts example 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `pub publish`. This is preferred for private packages. 6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 7 | 8 | # The following defines the version and build number for your application. 9 | # A version number is three numbers separated by dots, like 1.2.43 10 | # followed by an optional build number separated by a +. 11 | # Both the version and the builder number may be overridden in flutter 12 | # build by specifying --build-name and --build-number, respectively. 13 | # In Android, build-name is used as versionName while build-number used as versionCode. 14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 16 | # Read more about iOS versioning at 17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 18 | version: 1.0.0+1 19 | 20 | environment: 21 | sdk: '>=3.0.5 <4.0.0' 22 | 23 | dependencies: 24 | flutter: 25 | sdk: flutter 26 | askless: 27 | path: ../../ 28 | 29 | # For information on the generic Dart part of this file, see the 30 | # following page: https://dart.dev/tools/pub/pubspec 31 | 32 | # The following section is specific to Flutter. 33 | flutter: 34 | 35 | # The following line ensures that the Material Icons font is 36 | # included with your application, so that you can use the icons in 37 | # the material Icons class. 38 | uses-material-design: true 39 | 40 | # To add assets to your application, add an assets section, like this: 41 | # assets: 42 | # - images/a_dot_burr.jpeg 43 | # - images/a_dot_ham.jpeg 44 | 45 | # An image asset can refer to one or more resolution-specific "variants", see 46 | # https://flutter.dev/assets-and-images/#resolution-aware. 47 | 48 | # For details regarding adding assets from package dependencies, see 49 | # https://flutter.dev/assets-and-images/#from-packages 50 | 51 | # To add custom fonts to your application, add a fonts section here, 52 | # in this "flutter" section. Each entry in this list should have a 53 | # "family" key with the font family name, and a "fonts" key with a 54 | # list giving the asset and other descriptors for the font. For 55 | # example: 56 | # fonts: 57 | # - family: Schyler 58 | # fonts: 59 | # - asset: fonts/Schyler-Regular.ttf 60 | # - asset: fonts/Schyler-Italic.ttf 61 | # style: italic 62 | # - family: Trajan Pro 63 | # fonts: 64 | # - asset: fonts/TrajanPro.ttf 65 | # - asset: fonts/TrajanPro_Bold.ttf 66 | # weight: 700 67 | # 68 | # For details regarding fonts from package dependencies, 69 | # see https://flutter.dev/custom-fonts/#from-packages 70 | -------------------------------------------------------------------------------- /example/random_numbers/random_numbers.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/random_numbers/random_numbers.gif -------------------------------------------------------------------------------- /example/random_numbers/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | random_numbers 27 | 28 | 29 | 30 | 33 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /example/random_numbers/web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "random_numbers", 3 | "short_name": "random_numbers", 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 | } 24 | -------------------------------------------------------------------------------- /example/simple_chat/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | -------------------------------------------------------------------------------- /example/simple_chat/.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: f4abaa0735eba4dfd8f33f73363911d63931fe03 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/simple_chat/README.md: -------------------------------------------------------------------------------- 1 | # simple_chat 2 | 3 | #### A simple Flutter Chat App built with Askless, without Firebase 4 | 5 | This is the client side in Flutter, 6 | **[click here](https://github.com/RodrigoBertotti/Askless/tree/dev/example/simple-chat-ts)** 7 | to access the backend side in Node.js 8 | 9 | ![Alt Text](chat.gif) 10 | -------------------------------------------------------------------------------- /example/simple_chat/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 | -------------------------------------------------------------------------------- /example/simple_chat/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 33 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | 35 | defaultConfig { 36 | applicationId "com.example.chat" 37 | minSdkVersion 21 38 | targetSdkVersion 30 39 | versionCode flutterVersionCode.toInteger() 40 | versionName flutterVersionName 41 | } 42 | 43 | buildTypes { 44 | release { 45 | signingConfig signingConfigs.debug 46 | } 47 | } 48 | } 49 | 50 | flutter { 51 | source '../..' 52 | } 53 | 54 | dependencies { 55 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 56 | } 57 | -------------------------------------------------------------------------------- /example/simple_chat/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/simple_chat/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 13 | 17 | 21 | 26 | 30 | 31 | 32 | 33 | 34 | 35 | 37 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /example/simple_chat/android/app/src/main/kotlin/com/example/chat2/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.chat 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /example/simple_chat/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/simple_chat/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/simple_chat/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/simple_chat/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/simple_chat/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/simple_chat/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/simple_chat/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/simple_chat/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/simple_chat/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/simple_chat/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/simple_chat/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/simple_chat/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/simple_chat/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/simple_chat/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/simple_chat/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/simple_chat/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.7.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:7.3.1' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | mavenCentral() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | tasks.register("clean", Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /example/simple_chat/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M \ 2 | --add-exports=java.base/sun.nio.ch=ALL-UNNAMED \ 3 | --add-opens=java.base/java.lang=ALL-UNNAMED \ 4 | --add-opens=java.base/java.lang.reflect=ALL-UNNAMED \ 5 | --add-opens=java.base/java.io=ALL-UNNAMED \ 6 | --add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED 7 | android.useAndroidX=true 8 | android.enableJetifier=true -------------------------------------------------------------------------------- /example/simple_chat/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip 7 | -------------------------------------------------------------------------------- /example/simple_chat/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /example/simple_chat/chat.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/simple_chat/chat.gif -------------------------------------------------------------------------------- /example/simple_chat/ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/ephemeral/ 22 | Flutter/app.flx 23 | Flutter/app.zip 24 | Flutter/flutter_assets/ 25 | Flutter/flutter_export_environment.sh 26 | ServiceDefinitions.json 27 | Runner/GeneratedPluginRegistrant.* 28 | 29 | # Exceptions to above rules. 30 | !default.mode1v3 31 | !default.mode2v3 32 | !default.pbxuser 33 | !default.perspectivev3 34 | -------------------------------------------------------------------------------- /example/simple_chat/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/simple_chat/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/simple_chat/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/simple_chat/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/simple_chat/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/simple_chat/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/simple_chat/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /example/simple_chat/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/simple_chat/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/simple_chat/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/simple_chat/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/simple_chat/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/simple_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/simple_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/simple_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/simple_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/simple_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/simple_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/simple_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/simple_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/simple_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/simple_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/simple_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/simple_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/simple_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/simple_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/simple_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/simple_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/simple_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/simple_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/simple_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/simple_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/simple_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/simple_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/simple_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/simple_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/simple_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/simple_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/simple_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/simple_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/simple_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/simple_chat/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/simple_chat/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/simple_chat/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/simple_chat/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/simple_chat/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/simple_chat/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/simple_chat/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/example/simple_chat/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/simple_chat/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/simple_chat/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/simple_chat/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/simple_chat/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | chat 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /example/simple_chat/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/simple_chat/lib/MessageWidget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class MessageWidgetOfMyself extends StatelessWidget { 4 | final String text; 5 | 6 | MessageWidgetOfMyself({required this.text}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return Row( 11 | children: [ 12 | Flexible( 13 | child: _buildBox(text: text, context: context), 14 | ), 15 | ], 16 | mainAxisAlignment: MainAxisAlignment.end, 17 | ); 18 | } 19 | } 20 | 21 | class MessageWidgetOfTheir extends StatelessWidget { 22 | final String text; 23 | 24 | MessageWidgetOfTheir({required this.text}); 25 | 26 | @override 27 | Widget build(BuildContext context) { 28 | return Row( 29 | children: [ 30 | Flexible(child: _buildBox(text: text, context: context)), 31 | ], 32 | mainAxisAlignment: MainAxisAlignment.start, 33 | ); 34 | } 35 | } 36 | 37 | _buildBox({required BuildContext context, required String text}){ 38 | return Padding( 39 | child: Container( 40 | decoration: BoxDecoration( 41 | borderRadius: new BorderRadius.circular(10.0), 42 | color: Colors.white 43 | ), 44 | child: Padding( 45 | padding: EdgeInsets.only(top: 5, bottom: 5, left: 10, right: 10), 46 | child: Text(text), 47 | ), 48 | ), 49 | padding: EdgeInsets.only(bottom: 5), 50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /example/simple_chat/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:askless/index.dart'; 3 | import 'signin_page.dart'; 4 | 5 | 6 | const serverUrl = ''; // TODO: <-- replace with your nodejs backend URL here like: 7 | // const serverUrl = 'ws://192.168.0.8:3000'; 8 | 9 | void main() { 10 | assert(serverUrl.isNotEmpty, "replace \"serverUrl\" with your nodejs backend URL like: 'ws://192.168.0.8:3000'"); 11 | 12 | AsklessClient.instance.start( 13 | serverUrl: serverUrl, 14 | debugLogs: !isProduction, // DO NOT DO SHOW ASKLESS LOGS ON THE CONSOLE ON A PRODUCTION ENVIRONMENT 15 | ); 16 | 17 | runApp(const ChatApp()); 18 | } 19 | 20 | const isProduction = false; 21 | 22 | class ChatApp extends StatelessWidget { 23 | 24 | const ChatApp({super.key}); 25 | 26 | @override 27 | Widget build(BuildContext context) { 28 | return MaterialApp( 29 | title: 'Chat', 30 | debugShowCheckedModeBanner: false, 31 | home: SignInPage(), 32 | ); 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /example/simple_chat/lib/signin_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:askless/index.dart'; 3 | import 'conversation_page.dart'; 4 | 5 | class SignInPage extends StatefulWidget { 6 | @override 7 | _SignInPageState createState() => _SignInPageState(); 8 | } 9 | 10 | class _SignInPageState extends State { 11 | String? _tapped; 12 | final Duration _containerAnimationDuration = Duration(seconds: 1); 13 | final Duration _titleAnimationDuration = Duration(milliseconds: 500); 14 | bool _loading = false; 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return Scaffold( 19 | body: Column( 20 | mainAxisAlignment: MainAxisAlignment.center, 21 | children: [ 22 | AnimatedSwitcher(duration: _titleAnimationDuration, child: _tapped!=null ? Container() : RichText(text: const TextSpan( 23 | children: [ 24 | TextSpan(text: 'What co', 25 | style: TextStyle(fontSize: 22, color: Colors.blue)), 26 | TextSpan(text: 'lor am I?', 27 | style: TextStyle(fontSize: 22, color: Colors.green)), 28 | ] 29 | ),),), 30 | AnimatedContainer(height: _tapped==null?40:0, duration: _containerAnimationDuration,), 31 | Row( 32 | mainAxisAlignment: MainAxisAlignment.center, 33 | children: [ 34 | item(color: Colors.blue, myColor: 'blue', isTapped: _tapped == 'blue'), 35 | AnimatedContainer(width: _tapped==null?10:0, duration: _containerAnimationDuration,), 36 | item(color: Colors.green, myColor: 'green', isTapped: _tapped == 'green'), 37 | ], 38 | ), 39 | ], 40 | ), 41 | ); 42 | } 43 | 44 | item({required Color color, required String myColor, required isTapped}) { 45 | final size = _getSizeContainer(name: myColor); 46 | return Row( 47 | children: [ 48 | GestureDetector( 49 | onTap: () { 50 | setState(() { 51 | _tapped = myColor; 52 | _loading = true; 53 | AsklessClient.instance.clearAuthentication(); 54 | AsklessClient.instance.authenticate(credential: { "myColor": myColor }, neverTimeout: true).then((value){ 55 | Future.delayed(_containerAnimationDuration, () { 56 | Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context) => ConversationsPage(myName: _tapped!,)), (route) => false); 57 | }); 58 | }); 59 | }); 60 | }, 61 | child: AnimatedContainer( 62 | duration: _containerAnimationDuration, 63 | width: size['width'], 64 | color: color, 65 | height: size['height'], 66 | alignment: Alignment.bottomCenter, 67 | child: _loading && isTapped ? Column( 68 | mainAxisAlignment: MainAxisAlignment.center, 69 | children: [ 70 | CircularProgressIndicator(valueColor: new AlwaysStoppedAnimation(Colors.white),), 71 | SizedBox(height: 20,), 72 | Text('Disconnecting and connecting again\nto server as ownClientId: '+myColor, style: TextStyle(color: Colors.white, fontSize: 18,), textAlign: TextAlign.center,) 73 | ], 74 | ) : Container(), 75 | ) 76 | ) 77 | ], 78 | ); 79 | } 80 | 81 | _getSizeContainer({required name}) { 82 | if(_tapped==null) { 83 | return { 84 | 'width': MediaQuery.of(context).size.width / 2 - 10, 85 | 'height': MediaQuery.of(context).size.height / 2 86 | }; 87 | } 88 | if(_tapped==name) { 89 | return { 90 | 'width': MediaQuery.of(context).size.width, 91 | 'height': MediaQuery.of(context).size.height 92 | }; 93 | } 94 | return { 95 | 'width': 0.0, 96 | 'height': 0.0 97 | }; 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /example/simple_chat/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: simple_chat 2 | description: An example of the framework's use 3 | 4 | publish_to: 'none' 5 | 6 | version: 1.0.0+1 7 | 8 | environment: 9 | sdk: '>=3.0.5 <4.0.0' 10 | 11 | dependencies: 12 | flutter: 13 | sdk: flutter 14 | flutter_url_image_load_fail: ^1.0.0 15 | askless: 16 | path: ../../ 17 | 18 | 19 | flutter: 20 | uses-material-design: true 21 | 22 | 23 | -------------------------------------------------------------------------------- /example/simple_chat/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | chat 27 | 28 | 29 | 30 | 33 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /example/simple_chat/web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chat", 3 | "short_name": "chat", 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 | } 24 | -------------------------------------------------------------------------------- /lib/constants.dart: -------------------------------------------------------------------------------- 1 | 2 | enum RequestType { 3 | LISTEN, 4 | CONFIRM_RECEIPT, 5 | CONFIGURE_CONNECTION, 6 | AUTHENTICATE, 7 | READ, 8 | CREATE, 9 | UPDATE, 10 | DELETE 11 | } 12 | 13 | const REQUEST_PREFIX = 'REQ-'; 14 | const LISTEN_PREFIX = 'LIS-'; 15 | 16 | // TODO onupdate: 17 | const CLIENT_LIBRARY_VERSION_NAME = '3.1.1'; 18 | const CLIENT_LIBRARY_VERSION_CODE = 8; 19 | 20 | // TODO onupdate: CHECK README (# Add this line: askless: ^3.1.1) 21 | 22 | // TODO onupdate: add changelog 23 | -------------------------------------------------------------------------------- /lib/data/models/askless_error_model.dart: -------------------------------------------------------------------------------- 1 | import '../../index.dart'; 2 | 3 | 4 | class AsklessErrorModel extends AsklessError { 5 | 6 | AsklessErrorModel({required super.code, required super.description}); 7 | 8 | factory AsklessErrorModel.fromMap(map){ 9 | return AsklessErrorModel( 10 | code: map['code'] ?? 'none', 11 | description: map['description'] ?? 'none' 12 | ); 13 | } 14 | } -------------------------------------------------------------------------------- /lib/data/models/internal_response_model.dart: -------------------------------------------------------------------------------- 1 | import '../../domain/entities/internal_response_cli_entity.dart'; 2 | import 'askless_error_model.dart'; 3 | 4 | 5 | class InternalAsklessResponseModel extends InternalAsklessResponseEntity { 6 | static const type = "_class_type_response"; 7 | 8 | 9 | InternalAsklessResponseModel({ 10 | required super.serverId, 11 | required super.clientRequestId, 12 | super.output, 13 | super.error 14 | }); 15 | 16 | InternalAsklessResponseModel.fromMap (map) : super( 17 | serverId: map["serverId"], 18 | clientRequestId: map["clientRequestId"], 19 | error: map['error'] != null ? AsklessErrorModel.fromMap(map['error']) : null, 20 | output: map['output'], 21 | ); 22 | 23 | } -------------------------------------------------------------------------------- /lib/domain/entities/internal_response_cli_entity.dart: -------------------------------------------------------------------------------- 1 | import '../../index.dart'; 2 | import '../entities/response_entity.dart'; 3 | 4 | class InternalAsklessResponseEntity extends AsklessResponse { 5 | final String? serverId; 6 | final String clientRequestId; 7 | 8 | InternalAsklessResponseEntity({ 9 | this.serverId, required this.clientRequestId, 10 | dynamic output, AsklessError? error 11 | }) : super(output: output, error: error); 12 | 13 | } -------------------------------------------------------------------------------- /lib/domain/entities/response_entity.dart: -------------------------------------------------------------------------------- 1 | import '../../index.dart'; 2 | 3 | /// Result of request attempt to the server. 4 | class AsklessResponse { 5 | 6 | /// The output the server sent, or null. 7 | /// 8 | /// Do NOT use this field to check if the operation 9 | /// failed (because it can be null even in case of success) 10 | dynamic output; 11 | 12 | /// Error details in case where [success] == [false] 13 | AsklessError? error; 14 | 15 | AsklessResponse({this.output, this.error}); 16 | 17 | /// Indicates whether the request attempt is a success 18 | bool get success => error == null; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /lib/domain/utils/logger.dart: -------------------------------------------------------------------------------- 1 | enum Level { info, debug, error, warning } 2 | typedef LoggerFunction = void Function (dynamic message, Level level, {dynamic additionalData}); 3 | 4 | class Logger{ 5 | Logger({required bool debugLogs}){ 6 | doLog = (message, Level level, {additionalData}) { 7 | if (level != Level.debug || debugLogs) { 8 | print("Askless [${level.toString().split(".").last.toUpperCase()}]: $message"); 9 | if (additionalData != null) { 10 | print(additionalData.toString()); 11 | } 12 | } 13 | }; 14 | if (debugLogs) { 15 | doLog( 16 | '**********************************************************************************' + 17 | '** WARNING: debugLogs is true, set it to false in a production environment ** **\n' + 18 | '**********************************************************************************', 19 | Level.warning, 20 | ); 21 | } 22 | } 23 | 24 | late final LoggerFunction doLog; 25 | } 26 | 27 | LoggerFunction? _doLog; 28 | void setAsklessLogger (Logger logger) {_doLog = logger.doLog; } 29 | void logger(String message, {Level level=Level.debug, additionalData}) { 30 | if (_doLog != null) { 31 | _doLog!(message, level, additionalData: additionalData); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/injection_container.dart: -------------------------------------------------------------------------------- 1 | import 'package:get_it/get_it.dart'; 2 | import 'domain/services/authenticate_service.dart'; 3 | import 'domain/services/call_service.dart'; 4 | import 'domain/services/connection_service.dart'; 5 | import 'domain/services/requests_service.dart'; 6 | import 'middleware/ListeningHandler.dart'; 7 | import 'middleware/ws_channel/AbstractIOWsChannel.dart'; 8 | import 'middleware/ws_channel/IOWsChannel.dart'; 9 | import 'tasks/SendPingTask.dart'; 10 | import 'tasks/SendMessageToServerAgainTask.dart'; 11 | import 'tasks/ReconnectClientWhenDidNotReceivePongFromServerTask.dart'; 12 | import 'tasks/ReconnectWhenOffline.dart'; 13 | import 'tasks/TimedTask.dart'; 14 | 15 | /// Service locator 16 | final getIt = GetIt.instance; 17 | 18 | void init () { 19 | getIt.registerLazySingleton(() => SendPingTask()); 20 | getIt.registerLazySingleton(() => SendMessageToServerAgainTask()); 21 | getIt.registerLazySingleton(() => ReconnectWhenDidNotReceivePongFromServerTask()); 22 | getIt.registerLazySingleton(() => ConnectionService()); 23 | getIt.registerLazySingleton(() => AuthenticateService()); 24 | getIt.registerLazySingleton(() => RequestsService()); 25 | getIt.registerLazySingleton(() => IOWsChannel()); 26 | getIt.registerLazySingleton(() => ListeningHandler()); 27 | } 28 | 29 | -------------------------------------------------------------------------------- /lib/middleware/data/Mappable.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | abstract class Mappable { 4 | 5 | Map toMap(); 6 | 7 | } -------------------------------------------------------------------------------- /lib/middleware/data/connection/PingPong.dart: -------------------------------------------------------------------------------- 1 | import '../Mappable.dart'; 2 | import '../request/OperationRequestCli.dart'; 3 | 4 | class ListeningTo extends ListenCli { 5 | final String listenId; 6 | 7 | ListeningTo({required this.listenId, required String route, params, required String clientRequestId}) 8 | : super(route: route, params: params, clientRequestId: clientRequestId,); 9 | 10 | @override 11 | Map toMap() { 12 | final map = super.toMap(); 13 | map['listenId'] = listenId; 14 | return map; 15 | } 16 | 17 | static List toMapList(List list){ 18 | List res = []; 19 | for (final v in list) { 20 | res.add(v.toMap()); 21 | } 22 | return res; 23 | } 24 | } 25 | 26 | class PingPong implements Mappable { 27 | static const type = '_class_type_pingpong'; 28 | 29 | List listeningToRoutes; 30 | 31 | PingPong({required this.listeningToRoutes}); 32 | 33 | @override 34 | toMap() { 35 | final map = { 36 | type: '_', 37 | 'listeningToRoutes': ListeningTo.toMapList(listeningToRoutes), 38 | }; 39 | return map; 40 | } 41 | 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /lib/middleware/data/receivements/AuthenticateResponseCli.dart: -------------------------------------------------------------------------------- 1 | import '../../../domain/entities/internal_response_cli_entity.dart'; 2 | import '../../../index.dart'; 3 | 4 | class AuthenticateResponseCli extends InternalAsklessResponseEntity { 5 | static const typeResponse = '_class_type_authenticateresponse'; 6 | 7 | AuthenticateResponseCli ( 8 | dynamic output, 9 | AsklessError? error, 10 | String clientRequestId, 11 | String? serverId 12 | ) : super(clientRequestId: clientRequestId, serverId: serverId, error: error, output: output); 13 | 14 | bool get invalidCredential => !success && error?.code == AsklessErrorCode.invalidCredential; 15 | 16 | Map? get credential => output?["credential"] == null ? null : Map.from(output["credential"]); 17 | 18 | List? get claims => output?["claims"] == null ? null : List.from(output?["claims"]); 19 | 20 | dynamic get userId => output?["userId"]; 21 | 22 | String? get errorDescription => error?.description; 23 | 24 | factory AuthenticateResponseCli.fromResponse(InternalAsklessResponseEntity asklessResponse) { 25 | return AuthenticateResponseCli(asklessResponse.output, asklessResponse.error, asklessResponse.clientRequestId, asklessResponse.serverId); 26 | } 27 | 28 | bool get isCredentialError => output?["credentialErrorCode"] != null; 29 | } 30 | -------------------------------------------------------------------------------- /lib/middleware/data/receivements/ConfigureConnectionResponseCli.dart: -------------------------------------------------------------------------------- 1 | import '../../../constants.dart'; 2 | import '../../../domain/entities/internal_response_cli_entity.dart'; 3 | import '../../../index.dart'; 4 | 5 | class ConfigureConnectionAsklessResponse extends InternalAsklessResponseEntity { 6 | static const typeResponse = '_class_type_configureconnection'; 7 | 8 | ConfigureConnectionAsklessResponse ( 9 | dynamic output, 10 | AsklessError? error, 11 | String clientRequestId, 12 | String? serverId 13 | ) : super(clientRequestId: clientRequestId, serverId: serverId, error: error, output: output); 14 | 15 | ConnectionConfiguration? get connectionConfiguration => success ? ConnectionConfiguration.fromMap(output["connectionConfiguration"]) : null; 16 | 17 | String? get errorDescription => error?.description; 18 | 19 | factory ConfigureConnectionAsklessResponse.fromResponse(InternalAsklessResponseEntity asklessResponse) { 20 | return ConfigureConnectionAsklessResponse(asklessResponse.output, asklessResponse.error, asklessResponse.clientRequestId, asklessResponse.serverId); 21 | } 22 | } 23 | 24 | class ConnectionConfiguration{ 25 | late int intervalInMsServerSendSameMessage; 26 | late int intervalInMsClientSendSameMessage; 27 | late int intervalInMsClientPing; 28 | late int reconnectClientAfterMillisecondsWithoutServerPong; 29 | late int millisecondsToDisconnectClientAfterWithoutClientPing; 30 | late String serverVersion; 31 | late ClientVersionCodeSupported clientVersionCodeSupported; 32 | late bool isFromServer; 33 | late int requestTimeoutInMs; 34 | late int waitForAuthenticationTimeoutInMs; 35 | 36 | ConnectionConfiguration({ 37 | ClientVersionCodeSupported? clientVersionCodeSupported, 38 | this.millisecondsToDisconnectClientAfterWithoutClientPing = 12 * 1000, 39 | this.intervalInMsClientPing = 1 * 1000, 40 | this.intervalInMsClientSendSameMessage = 5 * 1000, 41 | this.intervalInMsServerSendSameMessage = 5 * 1000, 42 | this.isFromServer = false, 43 | this.reconnectClientAfterMillisecondsWithoutServerPong = 6 * 1000, 44 | this.requestTimeoutInMs = 7 * 1000, 45 | this.waitForAuthenticationTimeoutInMs = 4 * 1000, 46 | this.serverVersion = 'none', 47 | }){ 48 | this.clientVersionCodeSupported = clientVersionCodeSupported ?? ClientVersionCodeSupported(); 49 | } 50 | 51 | ConnectionConfiguration.fromMap(map){ 52 | this.intervalInMsServerSendSameMessage = map['intervalInMsServerSendSameMessage']; 53 | this.intervalInMsClientSendSameMessage = map['intervalInMsClientSendSameMessage']; 54 | this.intervalInMsClientPing = map['intervalInMsClientPing']; 55 | this.reconnectClientAfterMillisecondsWithoutServerPong = map['reconnectClientAfterMillisecondsWithoutServerPong']; 56 | this.isFromServer = map['isFromServer']; 57 | this.serverVersion = map['serverVersion']; 58 | this.clientVersionCodeSupported = ClientVersionCodeSupported.fromMap(map['clientVersionCodeSupported']); 59 | this.requestTimeoutInMs = map['requestTimeoutInMs']; 60 | this.millisecondsToDisconnectClientAfterWithoutClientPing = map['millisecondsToDisconnectClientAfterWithoutClientPing']; 61 | this.waitForAuthenticationTimeoutInMs = map['waitForAuthenticationTimeoutInMs']; 62 | } 63 | 64 | bool get incompatibleVersion => (clientVersionCodeSupported.moreThanOrEqual != null && CLIENT_LIBRARY_VERSION_CODE < clientVersionCodeSupported.moreThanOrEqual!) || (clientVersionCodeSupported.lessThanOrEqual != null && CLIENT_LIBRARY_VERSION_CODE > clientVersionCodeSupported.lessThanOrEqual!); 65 | 66 | } 67 | 68 | class ClientVersionCodeSupported{ 69 | 70 | int? lessThanOrEqual; 71 | int? moreThanOrEqual; 72 | 73 | ClientVersionCodeSupported({this.lessThanOrEqual, this.moreThanOrEqual}); 74 | 75 | ClientVersionCodeSupported.fromMap(map){ 76 | lessThanOrEqual = map['lessThanOrEqual']; 77 | moreThanOrEqual = map['moreThanOrEqual']; 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /lib/middleware/data/receivements/NewDataForListener.dart: -------------------------------------------------------------------------------- 1 | 2 | class NewDataForListener { 3 | static const type = '_class_type_newDataForListener'; 4 | 5 | late final dynamic output; 6 | late final String listenId; 7 | 8 | NewDataForListener({required this.output, required this.listenId}); 9 | 10 | @override 11 | NewDataForListener.fromMap(messageMap) { 12 | output = messageMap['output']; 13 | listenId = messageMap['listenId']; 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /lib/middleware/data/receivements/ServerConfirmReceiptCli.dart: -------------------------------------------------------------------------------- 1 | import '../../../data/models/internal_response_model.dart'; 2 | 3 | class ServerConfirmReceiptCli extends InternalAsklessResponseModel { 4 | static const typeResponse = '_class_type_serverconfirmreceipt'; 5 | 6 | @override 7 | ServerConfirmReceiptCli.fromMap(messageMap) : super.fromMap(messageMap); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /lib/middleware/data/receivements/StopListeningEvent.dart: -------------------------------------------------------------------------------- 1 | 2 | class StopListeningEventEvent { 3 | static const type = '_class_type_stoplistening'; 4 | 5 | late final String listenId; 6 | 7 | StopListeningEventEvent({required this.listenId}); 8 | 9 | @override 10 | StopListeningEventEvent.fromMap(messageMap) { 11 | listenId = messageMap['listenId']; 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /lib/middleware/data/receivements/askless_error_entity.dart: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RodrigoBertotti/askless-flutter-client/ffd2852e680f3ac79d3bc2708977e95a5a6b47f9/lib/middleware/data/receivements/askless_error_entity.dart -------------------------------------------------------------------------------- /lib/middleware/data/request/AbstractRequestCli.dart: -------------------------------------------------------------------------------- 1 | import '../../../constants.dart'; 2 | import '../Mappable.dart'; 3 | 4 | abstract class AbstractRequestCli implements Mappable { 5 | static const String fieldClientRequestId = 'clientRequestId'; 6 | static const String fieldRequestType = 'requestType'; 7 | 8 | late RequestType requestType; 9 | String? clientRequestId; 10 | bool waitUntilGetServerConnection; 11 | 12 | AbstractRequestCli(this.requestType, {this.waitUntilGetServerConnection=true, this.clientRequestId}); 13 | 14 | @override 15 | Map toMap(){ 16 | final map = {}; 17 | map[fieldClientRequestId] = clientRequestId; 18 | map[fieldRequestType] = requestType.toString().split('.').last; 19 | return map; 20 | } 21 | 22 | String? getRoute(); 23 | 24 | } 25 | -------------------------------------------------------------------------------- /lib/middleware/data/request/AuthenticateRequestCli.dart: -------------------------------------------------------------------------------- 1 | import '../../../middleware/data/request/AbstractRequestCli.dart'; 2 | import '../../../constants.dart'; 3 | 4 | 5 | 6 | 7 | class AuthenticateRequestCli extends AbstractRequestCli { 8 | static const _type = '_class_type_authenticaterequest'; 9 | static const _clientIdInternalApp = 'clientIdInternalApp'; 10 | static const _clientType = 'clientType'; 11 | static const _credential = 'credential'; 12 | 13 | final String clientIdInternalApp; 14 | final dynamic credential; 15 | 16 | AuthenticateRequestCli({required this.clientIdInternalApp, required this.credential}) : super(RequestType.AUTHENTICATE); 17 | 18 | @override 19 | String? getRoute() { 20 | return null; 21 | } 22 | 23 | @override 24 | Map toMap() { 25 | final map = super.toMap(); 26 | map[_type] = '_'; 27 | map[_clientIdInternalApp] = clientIdInternalApp; 28 | map[_clientType] = 'flutter'; 29 | map[_credential] = credential; 30 | return map; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/middleware/data/request/ClientConfirmReceiptCli.dart: -------------------------------------------------------------------------------- 1 | import 'package:random_string/random_string.dart'; 2 | import '../../../constants.dart'; 3 | import 'AbstractRequestCli.dart'; 4 | 5 | 6 | 7 | 8 | class ClientConfirmReceiptCli extends AbstractRequestCli { 9 | static const fieldType = '_class_type_clientconfirmreceipt'; 10 | static const fieldServerId = 'serverId'; 11 | 12 | String serverId; 13 | 14 | ClientConfirmReceiptCli(this.serverId) : super(RequestType.CONFIRM_RECEIPT, clientRequestId: '${REQUEST_PREFIX}_${randomAlphaNumeric(28)}'); 15 | 16 | @override 17 | Map toMap() { 18 | final map = super.toMap(); 19 | map[fieldType] = '_'; 20 | map[fieldServerId] = serverId; 21 | return map; 22 | } 23 | 24 | @override 25 | String? getRoute() { 26 | return null; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /lib/middleware/data/request/ConfigureConnectionRequestCli.dart: -------------------------------------------------------------------------------- 1 | import '../../../constants.dart'; 2 | import 'AbstractRequestCli.dart'; 3 | 4 | 5 | 6 | 7 | class ConfigureConnectionRequestCli extends AbstractRequestCli { 8 | static const _type = '_class_type_configureconnectionrequest'; 9 | static const _clientIdInternalApp = 'clientIdInternalApp'; 10 | static const _clientType = 'clientType'; 11 | 12 | final String clientIdInternalApp; 13 | final String clientType = 'flutter'; 14 | 15 | ConfigureConnectionRequestCli(this.clientIdInternalApp,) : super(RequestType.CONFIGURE_CONNECTION); 16 | 17 | @override 18 | String? getRoute() { 19 | return null; 20 | } 21 | 22 | @override 23 | Map toMap() { 24 | final map = super.toMap(); 25 | map[_type] = '_'; 26 | map[_clientIdInternalApp] = clientIdInternalApp; 27 | map[_clientType] = clientType; 28 | return map; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/middleware/data/request/OperationRequestCli.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import '../../../constants.dart'; 3 | import 'AbstractRequestCli.dart'; 4 | 5 | abstract class ModifyCli extends AbstractRequestCli{ 6 | static const fieldType = '_class_type_modify'; 7 | static const fieldRoute = 'route'; 8 | static const fieldBody = 'body'; 9 | static const fieldQuery = 'params'; 10 | 11 | String route; 12 | dynamic body; 13 | Map ? params; 14 | 15 | ModifyCli( 16 | this.route, 17 | RequestType requestType, 18 | this.body, 19 | this.params 20 | ) : super(requestType, waitUntilGetServerConnection: false){ 21 | assert(this.requestType == RequestType.CREATE || this.requestType == RequestType.UPDATE || this.requestType == RequestType.DELETE); 22 | } 23 | 24 | @override 25 | String? getRoute() { 26 | return route; 27 | } 28 | 29 | @override 30 | Map toMap() { 31 | final map = super.toMap(); 32 | map[fieldType] = '_'; 33 | map[fieldRoute] = route; 34 | map[fieldBody] = body; 35 | map[fieldQuery] = params; 36 | return map; 37 | } 38 | } 39 | 40 | class CreateCli extends ModifyCli{ 41 | CreateCli({ 42 | required String route, 43 | required body, 44 | Map ? params 45 | }) : super(route, RequestType.CREATE, body, params); 46 | } 47 | 48 | class UpdateCli extends ModifyCli{ 49 | UpdateCli({ 50 | required String route, 51 | required dynamic body, 52 | Map ? params 53 | }) : super(route, RequestType.UPDATE, body, params); 54 | 55 | } 56 | 57 | class DeleteCli extends ModifyCli{ 58 | DeleteCli({ 59 | required String route, 60 | Map ? params 61 | }) : super(route, RequestType.DELETE, {}, params); 62 | } 63 | 64 | class ReadCli extends AbstractRequestCli { 65 | static const fieldType = '_class_type_read'; 66 | static const fieldRoute = 'route'; 67 | static const fieldQuery = 'params'; 68 | 69 | String route; 70 | Map ? params; 71 | 72 | ReadCli({ 73 | required this.route, 74 | this.params 75 | }) : super(RequestType.READ, waitUntilGetServerConnection: false); 76 | 77 | @override 78 | Map toMap() { 79 | final map = super.toMap(); 80 | map[fieldType] = '_'; 81 | map[fieldRoute] = route; 82 | map[fieldQuery] = params; 83 | return map; 84 | } 85 | 86 | @override 87 | String? getRoute() { return route; } 88 | } 89 | 90 | class ListenCli extends AbstractRequestCli { 91 | static const String fieldType = '_class_type_listen'; 92 | static const String fieldListenId = 'listenId'; 93 | static const String fieldRoute = 'route'; 94 | static const String fieldQuery = 'params'; 95 | 96 | String route; 97 | Map? params; 98 | late String listenId; 99 | 100 | ListenCli({ 101 | required this.route, 102 | this.params, 103 | String? clientRequestId, 104 | }) : super(RequestType.LISTEN, clientRequestId: clientRequestId); 105 | 106 | @override 107 | Map toMap(){ 108 | final map = super.toMap(); 109 | map[fieldType] = '_'; 110 | map[fieldRoute] = route; 111 | map[fieldQuery] = params; 112 | map[fieldListenId] = listenId; 113 | return map; 114 | } 115 | 116 | @override 117 | String? getRoute() { 118 | return route; 119 | } 120 | 121 | String get hash { 122 | String hashMap(Map map) { 123 | final List orderedKeyValues = []; 124 | map.forEach((key, value) { orderedKeyValues.add("$key|$value"); }); 125 | orderedKeyValues.sort((a,b) => a.compareTo(b)); 126 | return jsonEncode(orderedKeyValues); 127 | } 128 | 129 | return hashMap({ 130 | fieldRoute: route, 131 | fieldQuery: params == null ? null : hashMap(params!), 132 | AbstractRequestCli.fieldRequestType: requestType.toString().split('.').last, 133 | }); 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /lib/middleware/receivements/ClientReceived.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import '../../../../../../injection_container.dart'; 3 | import '../../data/models/internal_response_model.dart'; 4 | import '../../domain/utils/logger.dart'; 5 | import '../data/receivements/NewDataForListener.dart'; 6 | import '../data/receivements/ServerConfirmReceiptCli.dart'; 7 | import '../data/receivements/StopListeningEvent.dart'; 8 | import '../data/request/ClientConfirmReceiptCli.dart'; 9 | import '../ws_channel/AbstractIOWsChannel.dart'; 10 | import 'ClientReceivedIgnore.dart'; 11 | import 'ClientReceivedNewDataForListener.dart'; 12 | import 'package:collection/collection.dart'; 13 | 14 | import 'ClientReceivedResponse.dart'; 15 | import 'ClientReceivedServerConfirmReceipt.dart'; 16 | import 'ClientReceivedStopListeningEvent.dart'; 17 | 18 | 19 | const int _keepLastMessagesFromServerWithinMs = 10 * 60 * 1000; 20 | 21 | abstract class ClientReceived{ 22 | final bool confirmToServerThatDataHasBeenReceived; 23 | final Map messageMap; 24 | final List lastMessagesFromServer = []; 25 | 26 | ClientReceived(this.messageMap, this.confirmToServerThatDataHasBeenReceived); 27 | 28 | static int get startCheckingLastMessagesFromServerAfterSize => 100; 29 | 30 | factory ClientReceived.from(data){ 31 | if(data == 'pong' || data == 'welcome') { 32 | return ClientReceivedIgnore(); 33 | } 34 | 35 | final Map messageMap = data is String ? jsonDecode(data) : data; 36 | if(messageMap["serverId"]==null) { 37 | throw 'Unknown: ${Map.from(messageMap)}'; 38 | } 39 | if(messageMap[ServerConfirmReceiptCli.typeResponse]!=null) { 40 | return ClientReceivedServerConfirmReceipt(messageMap); 41 | } 42 | if(messageMap[NewDataForListener.type]!=null) { 43 | return ClientReceivedNewDataForListener(messageMap); 44 | } 45 | if(messageMap[InternalAsklessResponseModel.type]!=null) { 46 | return ClientReceivedResponse(messageMap); 47 | } 48 | if(messageMap[StopListeningEventEvent.type]!=null) { 49 | return ClientReceivedStopListeningEvent(messageMap); 50 | } 51 | 52 | throw "TODO: $messageMap"; 53 | } 54 | 55 | void implementation(); 56 | 57 | void handle() async { 58 | final serverId = messageMap["serverId"]; 59 | 60 | if(!confirmToServerThatDataHasBeenReceived){ 61 | implementation(); 62 | return; 63 | } 64 | 65 | confirmReceiptToServer(serverId); 66 | 67 | final LastServerMessage? dataAlreadySentByServerBefore = lastMessagesFromServer.firstWhereOrNull((m) => m.serverId == serverId); 68 | if(dataAlreadySentByServerBefore != null){ 69 | logger("handle, data already received: $serverId"); 70 | dataAlreadySentByServerBefore.messageReceivedAtSinceEpoch = DateTime.now().millisecondsSinceEpoch; 71 | return; 72 | } 73 | 74 | lastMessagesFromServer.add(LastServerMessage(serverId)); 75 | 76 | checkCleanOldMessagesFromServer(); 77 | 78 | implementation(); 79 | } 80 | 81 | void checkCleanOldMessagesFromServer({int removeCount = 10}) { 82 | if(lastMessagesFromServer.length > startCheckingLastMessagesFromServerAfterSize){ 83 | logger("Start of removing old messages received from server... (total: ${lastMessagesFromServer.length})"); 84 | final List remove = []; 85 | for(int i=lastMessagesFromServer.length-1; i >= 0 && remove.length < removeCount; i--){ 86 | final messageReceivedFromServer = lastMessagesFromServer[i]; 87 | if(messageReceivedFromServer.shouldBeRemoved) { 88 | remove.add(messageReceivedFromServer); 89 | } 90 | } 91 | for (final LastServerMessage element in remove) { 92 | lastMessagesFromServer.remove(element); 93 | } 94 | logger("...end of removing old messages received from server (removed: ${remove.length})"); 95 | } 96 | } 97 | 98 | void confirmReceiptToServer(String serverId) { 99 | logger("confirmReceiptToServer $serverId"); 100 | 101 | getIt.get().sinkAdd(map: ClientConfirmReceiptCli(serverId)); 102 | } 103 | } 104 | 105 | 106 | 107 | class LastServerMessage{ 108 | int messageReceivedAtSinceEpoch = DateTime.now().millisecondsSinceEpoch; 109 | 110 | final String serverId; 111 | 112 | LastServerMessage(this.serverId); 113 | 114 | bool get shouldBeRemoved => messageReceivedAtSinceEpoch + _keepLastMessagesFromServerWithinMs < DateTime.now().millisecondsSinceEpoch; 115 | } -------------------------------------------------------------------------------- /lib/middleware/receivements/ClientReceivedIgnore.dart: -------------------------------------------------------------------------------- 1 | import 'ClientReceived.dart'; 2 | 3 | 4 | class ClientReceivedIgnore extends ClientReceived{ 5 | 6 | 7 | ClientReceivedIgnore():super({}, false); 8 | 9 | @override 10 | void implementation() async {} 11 | 12 | } -------------------------------------------------------------------------------- /lib/middleware/receivements/ClientReceivedNewDataForListener.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:collection/collection.dart'; 3 | import '../../../../../../injection_container.dart'; 4 | import '../ListeningHandler.dart'; 5 | import '../data/receivements/NewDataForListener.dart'; 6 | import 'ClientReceived.dart'; 7 | import '../../index.dart'; 8 | import '../../domain/utils/logger.dart'; 9 | 10 | class ClientReceivedNewDataForListener extends ClientReceived{ 11 | ClientReceivedNewDataForListener(messageMap) 12 | : super(messageMap, true); 13 | 14 | @override 15 | void implementation() { 16 | final message = NewDataForListener.fromMap(messageMap); 17 | 18 | final sub = getIt.get() 19 | .listeningTo 20 | .firstWhereOrNull((s) => s.listenId == message.listenId); 21 | if (sub != null) { 22 | logger("found ${message.listenId}!!"); 23 | sub.onReady(() { 24 | logger("running onReady ${message.listenId}!!"); 25 | sub.streamBroadcastController.add(message); 26 | sub.lastReceivementFromServer = message; 27 | }); 28 | } else { 29 | logger ("Not found ${message.listenId}!! ${getIt.get().listeningTo.map((e) => "${e.listenId} ")}"); 30 | getIt.get().unfoundData[message.listenId] = messageMap; 31 | Future.delayed(const Duration(seconds: 15), () { 32 | getIt.get().unfoundData.removeWhere((key, _) => message.listenId == key); 33 | }); 34 | } 35 | } 36 | 37 | 38 | } -------------------------------------------------------------------------------- /lib/middleware/receivements/ClientReceivedResponse.dart: -------------------------------------------------------------------------------- 1 | import '../../../../../../injection_container.dart'; 2 | import '../../data/models/internal_response_model.dart'; 3 | import '../../domain/services/requests_service.dart'; 4 | import 'ClientReceived.dart'; 5 | 6 | 7 | class ClientReceivedResponse extends ClientReceived{ 8 | 9 | ClientReceivedResponse(messageMap) : super(messageMap, true); 10 | 11 | @override 12 | void implementation() { 13 | final responseCli = InternalAsklessResponseModel.fromMap(messageMap); 14 | getIt.get().notifyThatHasBeenReceivedServerResponse(responseCli); 15 | } 16 | 17 | 18 | } -------------------------------------------------------------------------------- /lib/middleware/receivements/ClientReceivedServerConfirmReceipt.dart: -------------------------------------------------------------------------------- 1 | import '../../../../../../injection_container.dart'; 2 | import '../../domain/services/requests_service.dart'; 3 | import '../data/receivements/ServerConfirmReceiptCli.dart'; 4 | import 'ClientReceived.dart'; 5 | 6 | class ClientReceivedServerConfirmReceipt extends ClientReceived{ 7 | 8 | ClientReceivedServerConfirmReceipt(messageMap) : super(messageMap, false); 9 | 10 | @override 11 | void implementation() { 12 | final serverConfirmReceiptCli = ServerConfirmReceiptCli.fromMap(messageMap); 13 | getIt.get().setAsReceivedPendingMessageThatServerShouldReceive( 14 | serverConfirmReceiptCli.clientRequestId 15 | ); 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /lib/middleware/receivements/ClientReceivedStopListeningEvent.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import '../../injection_container.dart'; 3 | import '../ListeningHandler.dart'; 4 | import '../data/receivements/StopListeningEvent.dart'; 5 | import 'ClientReceived.dart'; 6 | import 'package:collection/collection.dart'; 7 | 8 | class ClientReceivedStopListeningEvent extends ClientReceived{ 9 | late final StopListeningEventEvent event; 10 | 11 | ClientReceivedStopListeningEvent(Map messageMap):super({}, false) { 12 | event = StopListeningEventEvent.fromMap(messageMap); 13 | } 14 | 15 | @override 16 | void implementation() async { 17 | final listening = getIt.get().listeningTo.firstWhereOrNull((element) => element.listenId == event.listenId); 18 | if (listening != null) { 19 | getIt.get().listeningTo.remove(listening); 20 | listening.streamBroadcastController.close(); 21 | } 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /lib/middleware/ws_channel/AbstractIOWsChannel.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:meta/meta.dart'; 3 | import '../../../../../../injection_container.dart'; 4 | import '../../index.dart'; 5 | import '../../middleware/data/request/AbstractRequestCli.dart'; 6 | import '../../domain/services/connection_service.dart'; 7 | import '../../domain/utils/logger.dart'; 8 | import '../data/Mappable.dart'; 9 | import '../receivements/ClientReceived.dart'; 10 | 11 | abstract class AbstractIOWsChannel { 12 | int? _lastPongFromServer; 13 | 14 | AbstractIOWsChannel (); 15 | 16 | void sinkAdd({Mappable? map, String? data}){ 17 | assert(map!=null||data!=null); 18 | assert(map==null||data==null); 19 | } 20 | 21 | int? get lastPongFromServer => _lastPongFromServer; 22 | 23 | bool get isReady; 24 | 25 | @protected 26 | Future wsConnect(); 27 | 28 | @protected 29 | void wsClose(); 30 | 31 | @protected 32 | void wsHandleError(void Function(dynamic error)? handleError); 33 | 34 | @protected 35 | void wsListen(void Function(dynamic data) param0, {void Function(dynamic err) onError, void Function() onDone}); 36 | 37 | Future start() async { 38 | final success = await wsConnect(); 39 | if(!success) { 40 | return false; 41 | } 42 | 43 | final completer = Completer(); 44 | 45 | Future.delayed(const Duration(seconds: 10), () { // timeout 46 | print ("completer.isCompleted #3: ${completer.isCompleted}"); 47 | if (!completer.isCompleted) { 48 | logger("Could not connect the websocket, because of the timeout of 10 seconds"); 49 | completer.complete(false); 50 | } 51 | }); 52 | 53 | print ("completer.isCompleted #1: ${completer.isCompleted}"); 54 | 55 | wsListen((data) { 56 | _lastPongFromServer = DateTime.now().millisecondsSinceEpoch; 57 | 58 | if (!completer.isCompleted) { 59 | completer.complete(true); 60 | } 61 | 62 | ClientReceived.from(data).handle(); 63 | 64 | }, onError: (err) { 65 | logger("middleware: channel.stream.listen onError", level: Level.error, additionalData: err.toString()); 66 | }, onDone: () { 67 | _handleConnectionClosed(); 68 | if (!completer.isCompleted) { 69 | completer.complete(false); 70 | } 71 | }); 72 | 73 | wsHandleError((err) { 74 | logger("channel handleError", additionalData: err, level: Level.error); 75 | }); 76 | 77 | return completer.future; 78 | } 79 | 80 | void _handleConnectionClosed([Duration delay=const Duration(seconds: 2)]) { 81 | logger("channel.stream.listen onDone"); 82 | 83 | Future.delayed(delay, () { 84 | getIt.get().disconnectAndClearOnDone?.call(); 85 | getIt.get().disconnectAndClearOnDone = null; 86 | 87 | if (getIt.get().disconnectionReason != DisconnectionReason.unsupportedVersionCode) { 88 | getIt.get().disconnectionReason ??= DisconnectionReason.other; 89 | 90 | if(AsklessClient.instance.connection.status == ConnectionStatus.disconnected){ 91 | getIt.get().reconnect(); 92 | } 93 | logger("${AsklessClient.instance.connection}"); 94 | } 95 | }); 96 | } 97 | 98 | void close() { 99 | logger('close'); 100 | _lastPongFromServer = null; 101 | wsClose(); 102 | } 103 | 104 | } 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /lib/middleware/ws_channel/IOWsChannel.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | import 'package:web_socket_channel/web_socket_channel.dart'; 4 | import '../../../../../../injection_container.dart'; 5 | import '../../domain/services/connection_service.dart'; 6 | import '../../domain/utils/logger.dart'; 7 | import '../data/Mappable.dart'; 8 | import 'AbstractIOWsChannel.dart'; 9 | 10 | 11 | class IOWsChannel extends AbstractIOWsChannel { 12 | WebSocketChannel? _channel; 13 | 14 | @override 15 | Future wsConnect() async { 16 | logger("wsConnect", level: Level.debug); 17 | try { 18 | final uri = Uri.parse(getIt.get().serverUrl); 19 | await HttpClient().get(uri.host, uri.port, uri.path); 20 | _channel = WebSocketChannel.connect(uri); 21 | return true; 22 | }catch (error) { 23 | if (!error.toString().contains("SocketException: Connection")) { 24 | logger(error.toString(), level: Level.error); 25 | _channel = WebSocketChannel.connect(Uri.parse(getIt.get().serverUrl)); 26 | return true; 27 | } else { 28 | logger("Server is not online or App is disconnected from the internet", level: Level.debug); 29 | return false; 30 | } 31 | } 32 | } 33 | 34 | @override 35 | void wsListen(void Function(dynamic event) onData, {void Function(dynamic error)? onError, void Function()? onDone}) { 36 | _channel?.stream.listen(onData, onError: onError, onDone: onDone); 37 | } 38 | 39 | // static final List _lastClientRequestIds = []; 40 | 41 | @override 42 | void sinkAdd({Mappable? map, String? data}) { 43 | super.sinkAdd(map: map, data: data); 44 | 45 | if(map != null) { 46 | _channel?.sink.add(jsonEncode(map.toMap())); 47 | } 48 | if(data!=null){ 49 | _channel?.sink.add(data); 50 | } 51 | } 52 | 53 | @override 54 | void wsHandleError(void Function(dynamic error)? handleError) { 55 | _channel?.stream.handleError(handleError ?? (_){}); 56 | } 57 | 58 | @override 59 | void wsClose() { 60 | _channel?.sink.close(); 61 | _channel = null; 62 | } 63 | 64 | @override 65 | bool get isReady => _channel != null; 66 | 67 | } 68 | -------------------------------------------------------------------------------- /lib/tasks/ReconnectClientWhenDidNotReceivePongFromServerTask.dart: -------------------------------------------------------------------------------- 1 | import '../../../../../injection_container.dart'; 2 | import '../domain/services/connection_service.dart'; 3 | import '../domain/utils/logger.dart'; 4 | import '../index.dart'; 5 | import '../middleware/data/receivements/ConfigureConnectionResponseCli.dart'; 6 | import '../middleware/ws_channel/AbstractIOWsChannel.dart'; 7 | import 'TimedTask.dart'; 8 | 9 | class ReconnectWhenDidNotReceivePongFromServerTask extends TimedTask{ 10 | // OnChangeConnectionWithServerListener onConnectionChange; 11 | late bool _lastPongFromServerBeforeWasNull = false; 12 | bool _isFirst = true; 13 | 14 | ReconnectWhenDidNotReceivePongFromServerTask() : super('ReconnectWhenDidNotReceivePongFromServerTask', ConnectionConfiguration().reconnectClientAfterMillisecondsWithoutServerPong); 15 | 16 | @override 17 | onStop() { 18 | // Future.delayed(Duration(seconds: 3), (){ 19 | // removeOnChangeConnectionWithServer(onConnectionChange); 20 | // }); 21 | } 22 | 23 | @override 24 | onStart() { 25 | _lastPongFromServerBeforeWasNull = false; 26 | } 27 | 28 | 29 | @override 30 | void run() { 31 | if(_isFirst) { 32 | _isFirst = false; 33 | return; 34 | } 35 | final ws = getIt.get(); 36 | final lastPongFromServer = ws.lastPongFromServer; 37 | if((lastPongFromServer == null && _lastPongFromServerBeforeWasNull) 38 | || (lastPongFromServer != null && (lastPongFromServer + intervalInMs) < DateTime.now().millisecondsSinceEpoch)) { 39 | logger('reconnectWhenDidNotReceivePongFromServerTask reconnecting', level: Level.debug); 40 | getIt.get().reconnect(); 41 | } 42 | _lastPongFromServerBeforeWasNull = lastPongFromServer == null; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /lib/tasks/ReconnectWhenOffline.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:connectivity_plus/connectivity_plus.dart'; 3 | import '../../../../../injection_container.dart'; 4 | import '../domain/services/connection_service.dart'; 5 | import '../domain/utils/logger.dart'; 6 | import '../index.dart'; 7 | import '../middleware/data/receivements/ConfigureConnectionResponseCli.dart'; 8 | 9 | class ReconnectWhenOffline{ 10 | final Connectivity _connectivityManager = Connectivity(); 11 | StreamSubscription? _connectivitySubscription; 12 | int secondsToDisconnectWithoutPingFromClient = ConnectionConfiguration().reconnectClientAfterMillisecondsWithoutServerPong; 13 | ConnectivityResult? _connectivity; 14 | bool _isFirstTime = true; 15 | 16 | ConnectivityResult? get connectivity => _connectivity; 17 | 18 | start(){ 19 | stop(); 20 | Future.delayed(const Duration(seconds: 1), (){ 21 | _connectivitySubscription = 22 | _connectivityManager.onConnectivityChanged.listen((ConnectivityResult conn) { 23 | if(_isFirstTime){ 24 | _isFirstTime = false; 25 | return; 26 | } 27 | _connectivity = conn; 28 | 29 | if (conn == ConnectivityResult.none) { 30 | getIt.get().notifyConnectionChanged(ConnectionStatus.disconnected); 31 | logger('Lost internet connection',); 32 | } else { 33 | logger('Got internet connection, reconnecting...', level: Level.debug); 34 | try { 35 | getIt.get().reconnect(); 36 | } catch(e) { 37 | logger('ReconnectWhenOffline', level: Level.error, additionalData: e); 38 | } 39 | } 40 | }); 41 | }); 42 | } 43 | 44 | stop(){ 45 | _connectivitySubscription?.cancel(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/tasks/SendMessageToServerAgainTask.dart: -------------------------------------------------------------------------------- 1 | import '../../../../../injection_container.dart'; 2 | import '../domain/services/requests_service.dart'; 3 | import '../index.dart'; 4 | import '../middleware/data/receivements/ConfigureConnectionResponseCli.dart'; 5 | import 'TimedTask.dart'; 6 | 7 | class SendMessageToServerAgainTask extends TimedTask{ 8 | 9 | SendMessageToServerAgainTask() : super('SendMessageToServerAgainTask', ConnectionConfiguration().intervalInMsClientSendSameMessage); 10 | 11 | @override 12 | void run() { 13 | if (AsklessClient.instance.connection.status == ConnectionStatus.connected) { 14 | getIt.get().sendMessagesToServerAgain(super.intervalInMs); 15 | } 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /lib/tasks/SendPingTask.dart: -------------------------------------------------------------------------------- 1 | import '../../../../../injection_container.dart'; 2 | import '../domain/services/authenticate_service.dart'; 3 | import '../middleware/ListeningHandler.dart'; 4 | import '../middleware/data/connection/PingPong.dart'; 5 | import '../middleware/data/receivements/ConfigureConnectionResponseCli.dart'; 6 | import '../middleware/ws_channel/AbstractIOWsChannel.dart'; 7 | import 'TimedTask.dart'; 8 | 9 | 10 | class SendPingTask extends TimedTask{ 11 | 12 | SendPingTask() : super('SendPingTask', ConnectionConfiguration().intervalInMsClientPing); 13 | 14 | // não está autenticado ainda parece 15 | 16 | @override 17 | void run() { 18 | List listeningTo = []; 19 | if (!getIt.get().shouldBeAuthenticated || getIt.get().authStatus == AuthStatus.authenticated) { 20 | getIt.get().listeningTo.where((element) => element.ready).forEach((listen) { 21 | listeningTo.add( 22 | ListeningTo( 23 | listenId: listen.listenId, 24 | route: listen.route, 25 | params: listen.params, 26 | clientRequestId: listen.clientRequestId!, 27 | ) 28 | ); 29 | }); 30 | } 31 | getIt.get().sinkAdd(map: PingPong(listeningToRoutes: listeningTo)); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /lib/tasks/TimedTask.dart: -------------------------------------------------------------------------------- 1 | import '../domain/utils/logger.dart'; 2 | 3 | abstract class TimedTask{ 4 | late int _intervalInMs; 5 | bool runTask = false; 6 | String taskName; 7 | 8 | TimedTask(this.taskName, int intervalInSeconds){ 9 | changeInterval(intervalInSeconds); 10 | } 11 | 12 | changeInterval(int intervalInMs) { 13 | assert(intervalInMs > 0); 14 | _intervalInMs = intervalInMs; 15 | } 16 | 17 | int get intervalInMs => _intervalInMs; 18 | 19 | void run(); 20 | 21 | Future start() async { 22 | _start(); 23 | } 24 | 25 | Future _start() async{ 26 | onStart(); 27 | Future.delayed(const Duration(milliseconds: 100), () async{ 28 | if(runTask){ 29 | logger("Task '$taskName' already started", level: Level.debug); 30 | return; 31 | } 32 | runTask = true; 33 | 34 | while (runTask){ 35 | run(); 36 | final _lastIntervalInMs = intervalInMs; 37 | await Future.delayed(Duration(milliseconds: _intervalInMs)); 38 | if(intervalInMs!=_lastIntervalInMs) { 39 | await Future.delayed(Duration(milliseconds: _intervalInMs)); 40 | } 41 | } 42 | }); 43 | } 44 | 45 | void stop(){ 46 | runTask = false; 47 | 48 | onStop(); 49 | } 50 | 51 | onStop(){} 52 | onStart(){} 53 | 54 | } 55 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: askless 2 | description: Build Flutter Apps with PostgreSQL, MySQL, or any database, stream data changes through websockets effortlessly, handle websocket authentication like a pro and elevate your Flutter Chat App with video and audio calls! 3 | 4 | version: 3.1.1 # TODO onupdate: 5 | 6 | environment: 7 | sdk: '>=3.1.1 <4.0.0' 8 | 9 | homepage: https://github.com/RodrigoBertotti/askless-flutter-client/tree/dev/ 10 | 11 | repository: https://github.com/RodrigoBertotti/askless-flutter-client/tree/dev/ 12 | 13 | documentation: https://github.com/RodrigoBertotti/askless-flutter-client/blob/dev/documentation/english_documentation.md 14 | 15 | issue_tracker: https://github.com/RodrigoBertotti/askless-flutter-client/issues 16 | 17 | dependencies: 18 | flutter: 19 | sdk: flutter 20 | random_string: ^2.3.1 21 | synchronized: ^3.1.0+1 22 | connectivity_plus: ^5.0.2 23 | web_socket_channel: ^2.4.4 24 | injectable: ^2.3.0 25 | get_it: ^7.6.7 26 | collection: ^1.18.0 27 | meta: ^1.11.0 28 | flutter_webrtc: ^0.9.48+hotfix.1 29 | safe_device: ^1.1.6 30 | 31 | dev_dependencies: 32 | test: ^1.24.6 33 | injectable_generator: ^2.4.0 34 | build_runner: ^2.4.8 35 | 36 | #dev_dependencies: 37 | # flutter_test: 38 | # sdk: flutter 39 | 40 | #flutter: 41 | # uses-material-design: true 42 | --------------------------------------------------------------------------------