├── LICENSE ├── README.md ├── api └── proto │ └── v1 │ └── chat.proto ├── flutter-grpc-tutorial.code-workspace ├── flutter_client ├── .gitignore ├── .metadata ├── README.md ├── android │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── flutterclient │ │ │ │ └── MainActivity.java │ │ │ └── res │ │ │ ├── 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 │ │ │ └── styles.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle ├── ios │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ └── contents.xcworkspacedata │ └── Runner │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── 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 │ │ └── main.m ├── lib │ ├── api │ │ ├── chat_service.dart │ │ └── v1 │ │ │ ├── chat.pb.dart │ │ │ ├── chat.pbenum.dart │ │ │ ├── chat.pbgrpc.dart │ │ │ ├── chat.pbjson.dart │ │ │ └── google │ │ │ └── protobuf │ │ │ ├── empty.pb.dart │ │ │ ├── empty.pbenum.dart │ │ │ ├── empty.pbjson.dart │ │ │ ├── timestamp.pb.dart │ │ │ ├── timestamp.pbenum.dart │ │ │ ├── timestamp.pbjson.dart │ │ │ ├── wrappers.pb.dart │ │ │ ├── wrappers.pbenum.dart │ │ │ └── wrappers.pbjson.dart │ ├── blocs │ │ ├── application_bloc.dart │ │ ├── bloc_provider.dart │ │ └── message_events.dart │ ├── main.dart │ ├── models │ │ ├── message.dart │ │ ├── message_incoming.dart │ │ └── message_outgoing.dart │ ├── pages │ │ └── home.dart │ ├── theme.dart │ └── widgets │ │ ├── chat_message.dart │ │ ├── chat_message_incoming.dart │ │ └── chat_message_outgoing.dart └── pubspec.yaml ├── go-server ├── .gitignore ├── .vscode │ └── launch.json ├── cmd │ └── server │ │ └── main.go ├── go.mod └── pkg │ ├── api │ └── v1 │ │ └── chat.pb.go │ ├── protocol │ └── grpc │ │ └── server.go │ └── service │ └── v1 │ └── chat.go └── third_party ├── google └── protobuf │ ├── empty.proto │ ├── timestamp.proto │ └── wrappers.proto └── protoc-gen.cmd /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Source code for article [Asynchronous Flutter chat client with Go chat server which are powered by gRPC (simple and streaming)](https://medium.com/@amsokol.com/tutorial-asynchronous-flutter-chat-client-with-go-chat-server-which-are-powered-by-grpc-simple-ce913066861c) 2 | -------------------------------------------------------------------------------- /api/proto/v1/chat.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package v1; 4 | 5 | import "google/protobuf/empty.proto"; 6 | import "google/protobuf/wrappers.proto"; 7 | 8 | // Message is response for ChatService.Subscribe method 9 | message Message{ 10 | // message body 11 | string text = 1; 12 | } 13 | 14 | service ChatService { 15 | // Send sends message to the server 16 | rpc Send(google.protobuf.StringValue) returns (google.protobuf.Empty) {} 17 | 18 | // Subscribe is streaming method to get echo messages from the server 19 | rpc Subscribe(google.protobuf.Empty) returns (stream Message) {} 20 | } 21 | -------------------------------------------------------------------------------- /flutter-grpc-tutorial.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "api" 5 | }, 6 | { 7 | "path": "flutter_client" 8 | }, 9 | { 10 | "path": "go-server" 11 | }, 12 | { 13 | "path": "third_party" 14 | } 15 | ], 16 | "settings": { 17 | "cSpell.words": [ 18 | "Friendlychat", 19 | "Stateful", 20 | "amsokol", 21 | "cupertino", 22 | "grpc", 23 | "noname", 24 | "pbgrpc", 25 | "protobuf", 26 | "ptypes", 27 | "rxdart", 28 | "uuid", 29 | "vsync" 30 | ] 31 | } 32 | } -------------------------------------------------------------------------------- /flutter_client/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.lock 4 | *.log 5 | *.pyc 6 | *.swp 7 | .DS_Store 8 | .atom/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # Visual Studio Code related 20 | .vscode/ 21 | 22 | # Flutter/Dart/Pub related 23 | **/doc/api/ 24 | .dart_tool/ 25 | .flutter-plugins 26 | .packages 27 | .pub-cache/ 28 | .pub/ 29 | build/ 30 | 31 | # Android related 32 | **/android/**/gradle-wrapper.jar 33 | **/android/.gradle 34 | **/android/captures/ 35 | **/android/gradlew 36 | **/android/gradlew.bat 37 | **/android/local.properties 38 | **/android/**/GeneratedPluginRegistrant.java 39 | 40 | # iOS/XCode related 41 | **/ios/**/*.mode1v3 42 | **/ios/**/*.mode2v3 43 | **/ios/**/*.moved-aside 44 | **/ios/**/*.pbxuser 45 | **/ios/**/*.perspectivev3 46 | **/ios/**/*sync/ 47 | **/ios/**/.sconsign.dblite 48 | **/ios/**/.tags* 49 | **/ios/**/.vagrant/ 50 | **/ios/**/DerivedData/ 51 | **/ios/**/Icon? 52 | **/ios/**/Pods/ 53 | **/ios/**/.symlinks/ 54 | **/ios/**/profile 55 | **/ios/**/xcuserdata 56 | **/ios/.generated/ 57 | **/ios/Flutter/App.framework 58 | **/ios/Flutter/Flutter.framework 59 | **/ios/Flutter/Generated.xcconfig 60 | **/ios/Flutter/app.flx 61 | **/ios/Flutter/app.zip 62 | **/ios/Flutter/flutter_assets/ 63 | **/ios/ServiceDefinitions.json 64 | **/ios/Runner/GeneratedPluginRegistrant.* 65 | 66 | # Exceptions to above rules. 67 | !**/ios/**/default.mode1v3 68 | !**/ios/**/default.mode2v3 69 | !**/ios/**/default.pbxuser 70 | !**/ios/**/default.perspectivev3 71 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 72 | -------------------------------------------------------------------------------- /flutter_client/.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: 5391447fae6209bb21a89e6a5a6583cac1af9b4b 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /flutter_client/README.md: -------------------------------------------------------------------------------- 1 | # flutter_client 2 | 3 | A new Flutter project. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.io/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.io/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.io/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /flutter_client/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 from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 26 | 27 | android { 28 | compileSdkVersion 27 29 | 30 | lintOptions { 31 | disable 'InvalidPackage' 32 | } 33 | 34 | defaultConfig { 35 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 36 | applicationId "io.amsokol.flutterclient" 37 | minSdkVersion 16 38 | targetSdkVersion 27 39 | versionCode flutterVersionCode.toInteger() 40 | versionName flutterVersionName 41 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 42 | } 43 | 44 | buildTypes { 45 | release { 46 | // TODO: Add your own signing config for the release build. 47 | // Signing with the debug keys for now, so `flutter run --release` works. 48 | signingConfig signingConfigs.debug 49 | } 50 | } 51 | } 52 | 53 | flutter { 54 | source '../..' 55 | } 56 | 57 | dependencies { 58 | testImplementation 'junit:junit:4.12' 59 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 60 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 61 | } 62 | -------------------------------------------------------------------------------- /flutter_client/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 15 | 19 | 26 | 30 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /flutter_client/android/app/src/main/java/com/example/flutterclient/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.flutterclient; 2 | 3 | import android.os.Bundle; 4 | import io.flutter.app.FlutterActivity; 5 | import io.flutter.plugins.GeneratedPluginRegistrant; 6 | 7 | public class MainActivity extends FlutterActivity { 8 | @Override 9 | protected void onCreate(Bundle savedInstanceState) { 10 | super.onCreate(savedInstanceState); 11 | GeneratedPluginRegistrant.registerWith(this); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /flutter_client/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /flutter_client/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amsokol/flutter-grpc-tutorial/d51eb682daa9528a7026f95b7c5891b3a7a08ec3/flutter_client/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /flutter_client/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amsokol/flutter-grpc-tutorial/d51eb682daa9528a7026f95b7c5891b3a7a08ec3/flutter_client/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /flutter_client/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amsokol/flutter-grpc-tutorial/d51eb682daa9528a7026f95b7c5891b3a7a08ec3/flutter_client/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /flutter_client/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amsokol/flutter-grpc-tutorial/d51eb682daa9528a7026f95b7c5891b3a7a08ec3/flutter_client/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /flutter_client/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amsokol/flutter-grpc-tutorial/d51eb682daa9528a7026f95b7c5891b3a7a08ec3/flutter_client/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /flutter_client/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /flutter_client/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:3.2.1' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | google() 15 | jcenter() 16 | } 17 | } 18 | 19 | rootProject.buildDir = '../build' 20 | subprojects { 21 | project.buildDir = "${rootProject.buildDir}/${project.name}" 22 | } 23 | subprojects { 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /flutter_client/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | -------------------------------------------------------------------------------- /flutter_client/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-4.10.2-all.zip 7 | -------------------------------------------------------------------------------- /flutter_client/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /flutter_client/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 | -------------------------------------------------------------------------------- /flutter_client/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /flutter_client/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /flutter_client/ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */ = {isa = PBXBuildFile; fileRef = 2D5378251FAA1A9400D5DBA9 /* flutter_assets */; }; 12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 13 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; 14 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 15 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; 16 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 17 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; 18 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 19 | 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 20 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 21 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 22 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXCopyFilesBuildPhase section */ 26 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 27 | isa = PBXCopyFilesBuildPhase; 28 | buildActionMask = 2147483647; 29 | dstPath = ""; 30 | dstSubfolderSpec = 10; 31 | files = ( 32 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, 33 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, 34 | ); 35 | name = "Embed Frameworks"; 36 | runOnlyForDeploymentPostprocessing = 0; 37 | }; 38 | /* End PBXCopyFilesBuildPhase section */ 39 | 40 | /* Begin PBXFileReference section */ 41 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 42 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 43 | 2D5378251FAA1A9400D5DBA9 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = Flutter/flutter_assets; sourceTree = SOURCE_ROOT; }; 44 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 45 | 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 46 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 47 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 48 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 49 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 50 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 51 | 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 52 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 53 | 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 54 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 55 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 56 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 57 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 58 | /* End PBXFileReference section */ 59 | 60 | /* Begin PBXFrameworksBuildPhase section */ 61 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 62 | isa = PBXFrameworksBuildPhase; 63 | buildActionMask = 2147483647; 64 | files = ( 65 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, 66 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, 67 | ); 68 | runOnlyForDeploymentPostprocessing = 0; 69 | }; 70 | /* End PBXFrameworksBuildPhase section */ 71 | 72 | /* Begin PBXGroup section */ 73 | 9740EEB11CF90186004384FC /* Flutter */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | 2D5378251FAA1A9400D5DBA9 /* flutter_assets */, 77 | 3B80C3931E831B6300D905FE /* App.framework */, 78 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 79 | 9740EEBA1CF902C7004384FC /* Flutter.framework */, 80 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 81 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 82 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 83 | ); 84 | name = Flutter; 85 | sourceTree = ""; 86 | }; 87 | 97C146E51CF9000F007C117D = { 88 | isa = PBXGroup; 89 | children = ( 90 | 9740EEB11CF90186004384FC /* Flutter */, 91 | 97C146F01CF9000F007C117D /* Runner */, 92 | 97C146EF1CF9000F007C117D /* Products */, 93 | CF3B75C9A7D2FA2A4C99F110 /* Frameworks */, 94 | ); 95 | sourceTree = ""; 96 | }; 97 | 97C146EF1CF9000F007C117D /* Products */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 97C146EE1CF9000F007C117D /* Runner.app */, 101 | ); 102 | name = Products; 103 | sourceTree = ""; 104 | }; 105 | 97C146F01CF9000F007C117D /* Runner */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, 109 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, 110 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 111 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 112 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 113 | 97C147021CF9000F007C117D /* Info.plist */, 114 | 97C146F11CF9000F007C117D /* Supporting Files */, 115 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 116 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 117 | ); 118 | path = Runner; 119 | sourceTree = ""; 120 | }; 121 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | 97C146F21CF9000F007C117D /* main.m */, 125 | ); 126 | name = "Supporting Files"; 127 | sourceTree = ""; 128 | }; 129 | /* End PBXGroup section */ 130 | 131 | /* Begin PBXNativeTarget section */ 132 | 97C146ED1CF9000F007C117D /* Runner */ = { 133 | isa = PBXNativeTarget; 134 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 135 | buildPhases = ( 136 | 9740EEB61CF901F6004384FC /* Run Script */, 137 | 97C146EA1CF9000F007C117D /* Sources */, 138 | 97C146EB1CF9000F007C117D /* Frameworks */, 139 | 97C146EC1CF9000F007C117D /* Resources */, 140 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 141 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 142 | ); 143 | buildRules = ( 144 | ); 145 | dependencies = ( 146 | ); 147 | name = Runner; 148 | productName = Runner; 149 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 150 | productType = "com.apple.product-type.application"; 151 | }; 152 | /* End PBXNativeTarget section */ 153 | 154 | /* Begin PBXProject section */ 155 | 97C146E61CF9000F007C117D /* Project object */ = { 156 | isa = PBXProject; 157 | attributes = { 158 | LastUpgradeCheck = 0910; 159 | ORGANIZATIONNAME = "The Chromium Authors"; 160 | TargetAttributes = { 161 | 97C146ED1CF9000F007C117D = { 162 | CreatedOnToolsVersion = 7.3.1; 163 | }; 164 | }; 165 | }; 166 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 167 | compatibilityVersion = "Xcode 3.2"; 168 | developmentRegion = English; 169 | hasScannedForEncodings = 0; 170 | knownRegions = ( 171 | en, 172 | Base, 173 | ); 174 | mainGroup = 97C146E51CF9000F007C117D; 175 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 176 | projectDirPath = ""; 177 | projectRoot = ""; 178 | targets = ( 179 | 97C146ED1CF9000F007C117D /* Runner */, 180 | ); 181 | }; 182 | /* End PBXProject section */ 183 | 184 | /* Begin PBXResourcesBuildPhase section */ 185 | 97C146EC1CF9000F007C117D /* Resources */ = { 186 | isa = PBXResourcesBuildPhase; 187 | buildActionMask = 2147483647; 188 | files = ( 189 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 190 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 191 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, 192 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 193 | 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */, 194 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 195 | ); 196 | runOnlyForDeploymentPostprocessing = 0; 197 | }; 198 | /* End PBXResourcesBuildPhase section */ 199 | 200 | /* Begin PBXShellScriptBuildPhase section */ 201 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 202 | isa = PBXShellScriptBuildPhase; 203 | buildActionMask = 2147483647; 204 | files = ( 205 | ); 206 | inputPaths = ( 207 | ); 208 | name = "Thin Binary"; 209 | outputPaths = ( 210 | ); 211 | runOnlyForDeploymentPostprocessing = 0; 212 | shellPath = /bin/sh; 213 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; 214 | }; 215 | 9740EEB61CF901F6004384FC /* Run Script */ = { 216 | isa = PBXShellScriptBuildPhase; 217 | buildActionMask = 2147483647; 218 | files = ( 219 | ); 220 | inputPaths = ( 221 | ); 222 | name = "Run Script"; 223 | outputPaths = ( 224 | ); 225 | runOnlyForDeploymentPostprocessing = 0; 226 | shellPath = /bin/sh; 227 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 228 | }; 229 | /* End PBXShellScriptBuildPhase section */ 230 | 231 | /* Begin PBXSourcesBuildPhase section */ 232 | 97C146EA1CF9000F007C117D /* Sources */ = { 233 | isa = PBXSourcesBuildPhase; 234 | buildActionMask = 2147483647; 235 | files = ( 236 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, 237 | 97C146F31CF9000F007C117D /* main.m in Sources */, 238 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 239 | ); 240 | runOnlyForDeploymentPostprocessing = 0; 241 | }; 242 | /* End PBXSourcesBuildPhase section */ 243 | 244 | /* Begin PBXVariantGroup section */ 245 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 246 | isa = PBXVariantGroup; 247 | children = ( 248 | 97C146FB1CF9000F007C117D /* Base */, 249 | ); 250 | name = Main.storyboard; 251 | sourceTree = ""; 252 | }; 253 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 254 | isa = PBXVariantGroup; 255 | children = ( 256 | 97C147001CF9000F007C117D /* Base */, 257 | ); 258 | name = LaunchScreen.storyboard; 259 | sourceTree = ""; 260 | }; 261 | /* End PBXVariantGroup section */ 262 | 263 | /* Begin XCBuildConfiguration section */ 264 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 265 | isa = XCBuildConfiguration; 266 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 267 | buildSettings = { 268 | ALWAYS_SEARCH_USER_PATHS = NO; 269 | CLANG_ANALYZER_NONNULL = YES; 270 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 271 | CLANG_CXX_LIBRARY = "libc++"; 272 | CLANG_ENABLE_MODULES = YES; 273 | CLANG_ENABLE_OBJC_ARC = YES; 274 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 275 | CLANG_WARN_BOOL_CONVERSION = YES; 276 | CLANG_WARN_COMMA = YES; 277 | CLANG_WARN_CONSTANT_CONVERSION = YES; 278 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 279 | CLANG_WARN_EMPTY_BODY = YES; 280 | CLANG_WARN_ENUM_CONVERSION = YES; 281 | CLANG_WARN_INFINITE_RECURSION = YES; 282 | CLANG_WARN_INT_CONVERSION = YES; 283 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 284 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 285 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 286 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 287 | CLANG_WARN_STRICT_PROTOTYPES = YES; 288 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 289 | CLANG_WARN_UNREACHABLE_CODE = YES; 290 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 291 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 292 | COPY_PHASE_STRIP = NO; 293 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 294 | ENABLE_NS_ASSERTIONS = NO; 295 | ENABLE_STRICT_OBJC_MSGSEND = YES; 296 | GCC_C_LANGUAGE_STANDARD = gnu99; 297 | GCC_NO_COMMON_BLOCKS = YES; 298 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 299 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 300 | GCC_WARN_UNDECLARED_SELECTOR = YES; 301 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 302 | GCC_WARN_UNUSED_FUNCTION = YES; 303 | GCC_WARN_UNUSED_VARIABLE = YES; 304 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 305 | MTL_ENABLE_DEBUG_INFO = NO; 306 | SDKROOT = iphoneos; 307 | TARGETED_DEVICE_FAMILY = "1,2"; 308 | VALIDATE_PRODUCT = YES; 309 | }; 310 | name = Profile; 311 | }; 312 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 313 | isa = XCBuildConfiguration; 314 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 315 | buildSettings = { 316 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 317 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 318 | DEVELOPMENT_TEAM = S8QB4VV633; 319 | ENABLE_BITCODE = NO; 320 | FRAMEWORK_SEARCH_PATHS = ( 321 | "$(inherited)", 322 | "$(PROJECT_DIR)/Flutter", 323 | ); 324 | INFOPLIST_FILE = Runner/Info.plist; 325 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 326 | LIBRARY_SEARCH_PATHS = ( 327 | "$(inherited)", 328 | "$(PROJECT_DIR)/Flutter", 329 | ); 330 | PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterClient; 331 | PRODUCT_NAME = "$(TARGET_NAME)"; 332 | VERSIONING_SYSTEM = "apple-generic"; 333 | }; 334 | name = Profile; 335 | }; 336 | 97C147031CF9000F007C117D /* Debug */ = { 337 | isa = XCBuildConfiguration; 338 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 339 | buildSettings = { 340 | ALWAYS_SEARCH_USER_PATHS = NO; 341 | CLANG_ANALYZER_NONNULL = YES; 342 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 343 | CLANG_CXX_LIBRARY = "libc++"; 344 | CLANG_ENABLE_MODULES = YES; 345 | CLANG_ENABLE_OBJC_ARC = YES; 346 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 347 | CLANG_WARN_BOOL_CONVERSION = YES; 348 | CLANG_WARN_COMMA = YES; 349 | CLANG_WARN_CONSTANT_CONVERSION = YES; 350 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 351 | CLANG_WARN_EMPTY_BODY = YES; 352 | CLANG_WARN_ENUM_CONVERSION = YES; 353 | CLANG_WARN_INFINITE_RECURSION = YES; 354 | CLANG_WARN_INT_CONVERSION = YES; 355 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 356 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 357 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 358 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 359 | CLANG_WARN_STRICT_PROTOTYPES = YES; 360 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 361 | CLANG_WARN_UNREACHABLE_CODE = YES; 362 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 363 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 364 | COPY_PHASE_STRIP = NO; 365 | DEBUG_INFORMATION_FORMAT = dwarf; 366 | ENABLE_STRICT_OBJC_MSGSEND = YES; 367 | ENABLE_TESTABILITY = YES; 368 | GCC_C_LANGUAGE_STANDARD = gnu99; 369 | GCC_DYNAMIC_NO_PIC = NO; 370 | GCC_NO_COMMON_BLOCKS = YES; 371 | GCC_OPTIMIZATION_LEVEL = 0; 372 | GCC_PREPROCESSOR_DEFINITIONS = ( 373 | "DEBUG=1", 374 | "$(inherited)", 375 | ); 376 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 377 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 378 | GCC_WARN_UNDECLARED_SELECTOR = YES; 379 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 380 | GCC_WARN_UNUSED_FUNCTION = YES; 381 | GCC_WARN_UNUSED_VARIABLE = YES; 382 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 383 | MTL_ENABLE_DEBUG_INFO = YES; 384 | ONLY_ACTIVE_ARCH = YES; 385 | SDKROOT = iphoneos; 386 | TARGETED_DEVICE_FAMILY = "1,2"; 387 | }; 388 | name = Debug; 389 | }; 390 | 97C147041CF9000F007C117D /* Release */ = { 391 | isa = XCBuildConfiguration; 392 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 393 | buildSettings = { 394 | ALWAYS_SEARCH_USER_PATHS = NO; 395 | CLANG_ANALYZER_NONNULL = YES; 396 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 397 | CLANG_CXX_LIBRARY = "libc++"; 398 | CLANG_ENABLE_MODULES = YES; 399 | CLANG_ENABLE_OBJC_ARC = YES; 400 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 401 | CLANG_WARN_BOOL_CONVERSION = YES; 402 | CLANG_WARN_COMMA = YES; 403 | CLANG_WARN_CONSTANT_CONVERSION = YES; 404 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 405 | CLANG_WARN_EMPTY_BODY = YES; 406 | CLANG_WARN_ENUM_CONVERSION = YES; 407 | CLANG_WARN_INFINITE_RECURSION = YES; 408 | CLANG_WARN_INT_CONVERSION = YES; 409 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 410 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 411 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 412 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 413 | CLANG_WARN_STRICT_PROTOTYPES = YES; 414 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 415 | CLANG_WARN_UNREACHABLE_CODE = YES; 416 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 417 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 418 | COPY_PHASE_STRIP = NO; 419 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 420 | ENABLE_NS_ASSERTIONS = NO; 421 | ENABLE_STRICT_OBJC_MSGSEND = YES; 422 | GCC_C_LANGUAGE_STANDARD = gnu99; 423 | GCC_NO_COMMON_BLOCKS = YES; 424 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 425 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 426 | GCC_WARN_UNDECLARED_SELECTOR = YES; 427 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 428 | GCC_WARN_UNUSED_FUNCTION = YES; 429 | GCC_WARN_UNUSED_VARIABLE = YES; 430 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 431 | MTL_ENABLE_DEBUG_INFO = NO; 432 | SDKROOT = iphoneos; 433 | TARGETED_DEVICE_FAMILY = "1,2"; 434 | VALIDATE_PRODUCT = YES; 435 | }; 436 | name = Release; 437 | }; 438 | 97C147061CF9000F007C117D /* Debug */ = { 439 | isa = XCBuildConfiguration; 440 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 441 | buildSettings = { 442 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 443 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 444 | ENABLE_BITCODE = NO; 445 | FRAMEWORK_SEARCH_PATHS = ( 446 | "$(inherited)", 447 | "$(PROJECT_DIR)/Flutter", 448 | ); 449 | INFOPLIST_FILE = Runner/Info.plist; 450 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 451 | LIBRARY_SEARCH_PATHS = ( 452 | "$(inherited)", 453 | "$(PROJECT_DIR)/Flutter", 454 | ); 455 | PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterClient; 456 | PRODUCT_NAME = "$(TARGET_NAME)"; 457 | VERSIONING_SYSTEM = "apple-generic"; 458 | }; 459 | name = Debug; 460 | }; 461 | 97C147071CF9000F007C117D /* Release */ = { 462 | isa = XCBuildConfiguration; 463 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 464 | buildSettings = { 465 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 466 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 467 | ENABLE_BITCODE = NO; 468 | FRAMEWORK_SEARCH_PATHS = ( 469 | "$(inherited)", 470 | "$(PROJECT_DIR)/Flutter", 471 | ); 472 | INFOPLIST_FILE = Runner/Info.plist; 473 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 474 | LIBRARY_SEARCH_PATHS = ( 475 | "$(inherited)", 476 | "$(PROJECT_DIR)/Flutter", 477 | ); 478 | PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterClient; 479 | PRODUCT_NAME = "$(TARGET_NAME)"; 480 | VERSIONING_SYSTEM = "apple-generic"; 481 | }; 482 | name = Release; 483 | }; 484 | /* End XCBuildConfiguration section */ 485 | 486 | /* Begin XCConfigurationList section */ 487 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 488 | isa = XCConfigurationList; 489 | buildConfigurations = ( 490 | 97C147031CF9000F007C117D /* Debug */, 491 | 97C147041CF9000F007C117D /* Release */, 492 | 249021D3217E4FDB00AE95B9 /* Profile */, 493 | ); 494 | defaultConfigurationIsVisible = 0; 495 | defaultConfigurationName = Release; 496 | }; 497 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 498 | isa = XCConfigurationList; 499 | buildConfigurations = ( 500 | 97C147061CF9000F007C117D /* Debug */, 501 | 97C147071CF9000F007C117D /* Release */, 502 | 249021D4217E4FDB00AE95B9 /* Profile */, 503 | ); 504 | defaultConfigurationIsVisible = 0; 505 | defaultConfigurationName = Release; 506 | }; 507 | /* End XCConfigurationList section */ 508 | }; 509 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 510 | } 511 | -------------------------------------------------------------------------------- /flutter_client/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /flutter_client/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 75 | 77 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /flutter_client/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /flutter_client/ios/Runner/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : FlutterAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /flutter_client/ios/Runner/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #include "AppDelegate.h" 2 | #include "GeneratedPluginRegistrant.h" 3 | 4 | @implementation AppDelegate 5 | 6 | - (BOOL)application:(UIApplication *)application 7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 8 | [GeneratedPluginRegistrant registerWithRegistry:self]; 9 | // Override point for customization after application launch. 10 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 11 | } 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /flutter_client/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 | -------------------------------------------------------------------------------- /flutter_client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amsokol/flutter-grpc-tutorial/d51eb682daa9528a7026f95b7c5891b3a7a08ec3/flutter_client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /flutter_client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amsokol/flutter-grpc-tutorial/d51eb682daa9528a7026f95b7c5891b3a7a08ec3/flutter_client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /flutter_client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amsokol/flutter-grpc-tutorial/d51eb682daa9528a7026f95b7c5891b3a7a08ec3/flutter_client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /flutter_client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amsokol/flutter-grpc-tutorial/d51eb682daa9528a7026f95b7c5891b3a7a08ec3/flutter_client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /flutter_client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amsokol/flutter-grpc-tutorial/d51eb682daa9528a7026f95b7c5891b3a7a08ec3/flutter_client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /flutter_client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amsokol/flutter-grpc-tutorial/d51eb682daa9528a7026f95b7c5891b3a7a08ec3/flutter_client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /flutter_client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amsokol/flutter-grpc-tutorial/d51eb682daa9528a7026f95b7c5891b3a7a08ec3/flutter_client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /flutter_client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amsokol/flutter-grpc-tutorial/d51eb682daa9528a7026f95b7c5891b3a7a08ec3/flutter_client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /flutter_client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amsokol/flutter-grpc-tutorial/d51eb682daa9528a7026f95b7c5891b3a7a08ec3/flutter_client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /flutter_client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amsokol/flutter-grpc-tutorial/d51eb682daa9528a7026f95b7c5891b3a7a08ec3/flutter_client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /flutter_client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amsokol/flutter-grpc-tutorial/d51eb682daa9528a7026f95b7c5891b3a7a08ec3/flutter_client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /flutter_client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amsokol/flutter-grpc-tutorial/d51eb682daa9528a7026f95b7c5891b3a7a08ec3/flutter_client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /flutter_client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amsokol/flutter-grpc-tutorial/d51eb682daa9528a7026f95b7c5891b3a7a08ec3/flutter_client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /flutter_client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amsokol/flutter-grpc-tutorial/d51eb682daa9528a7026f95b7c5891b3a7a08ec3/flutter_client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /flutter_client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amsokol/flutter-grpc-tutorial/d51eb682daa9528a7026f95b7c5891b3a7a08ec3/flutter_client/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /flutter_client/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 | -------------------------------------------------------------------------------- /flutter_client/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amsokol/flutter-grpc-tutorial/d51eb682daa9528a7026f95b7c5891b3a7a08ec3/flutter_client/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /flutter_client/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amsokol/flutter-grpc-tutorial/d51eb682daa9528a7026f95b7c5891b3a7a08ec3/flutter_client/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /flutter_client/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amsokol/flutter-grpc-tutorial/d51eb682daa9528a7026f95b7c5891b3a7a08ec3/flutter_client/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /flutter_client/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. -------------------------------------------------------------------------------- /flutter_client/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 | -------------------------------------------------------------------------------- /flutter_client/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 | -------------------------------------------------------------------------------- /flutter_client/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | flutter_client 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 | -------------------------------------------------------------------------------- /flutter_client/ios/Runner/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char* argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /flutter_client/lib/api/chat_service.dart: -------------------------------------------------------------------------------- 1 | import 'dart:isolate'; 2 | import 'dart:io'; 3 | import 'package:grpc/grpc.dart'; 4 | 5 | import 'package:flutter_client/blocs/message_events.dart'; 6 | import 'package:flutter_client/models/message_outgoing.dart'; 7 | 8 | import 'v1/chat.pbgrpc.dart' as grpc; 9 | import 'v1/google/protobuf/empty.pb.dart'; 10 | import 'v1/google/protobuf/wrappers.pb.dart'; 11 | 12 | /// CHANGE TO IP ADDRESS OF YOUR SERVER IF IT IS NECESSARY 13 | const serverIP = "172.16.1.18"; 14 | const serverPort = 3000; 15 | 16 | /// ChatService client implementation 17 | class ChatService { 18 | // _isolateSending is isolate to send chat messages 19 | Isolate _isolateSending; 20 | 21 | // Port to send message 22 | SendPort _portSending; 23 | 24 | // Port to get status of message sending 25 | ReceivePort _portSendStatus; 26 | 27 | // _isolateReceiving is isolate to receive chat messages 28 | Isolate _isolateReceiving; 29 | 30 | // Port to receive messages 31 | ReceivePort _portReceiving; 32 | 33 | /// Event is raised when message has been sent to the server successfully 34 | final void Function(MessageSentEvent event) onMessageSent; 35 | 36 | /// Event is raised when message sending is failed 37 | final void Function(MessageSendFailedEvent event) onMessageSendFailed; 38 | 39 | /// Event is raised when message has been received from the server 40 | final void Function(MessageReceivedEvent event) onMessageReceived; 41 | 42 | /// Event is raised when message receiving is failed 43 | final void Function(MessageReceiveFailedEvent event) onMessageReceiveFailed; 44 | 45 | /// Constructor 46 | ChatService( 47 | {this.onMessageSent, 48 | this.onMessageSendFailed, 49 | this.onMessageReceived, 50 | this.onMessageReceiveFailed}) 51 | : _portSendStatus = ReceivePort(), 52 | _portReceiving = ReceivePort(); 53 | 54 | // Start threads to send and receive messages 55 | void start() { 56 | _startSending(); 57 | _startReceiving(); 58 | } 59 | 60 | /// Start thread to send messages 61 | void _startSending() async { 62 | // start thread to send messages 63 | _isolateSending = 64 | await Isolate.spawn(_sendingIsolate, _portSendStatus.sendPort); 65 | 66 | // listen send status 67 | await for (var event in _portSendStatus) { 68 | if (event is SendPort) { 69 | _portSending = event; 70 | } else if (event is MessageSentEvent) { 71 | // call for success handler 72 | if (onMessageSent != null) { 73 | onMessageSent(event); 74 | } 75 | } else if (event is MessageSendFailedEvent) { 76 | // call for error handler 77 | if (onMessageSendFailed != null) { 78 | onMessageSendFailed(event); 79 | } 80 | } else { 81 | assert(false, 'Unknown event type ${event.runtimeType}'); 82 | } 83 | } 84 | } 85 | 86 | /// Thread to send messages 87 | static void _sendingIsolate(SendPort portSendStatus) async { 88 | // Port to get messages to send 89 | ReceivePort portSendMessages = ReceivePort(); 90 | 91 | // send port to send messages to the caller 92 | portSendStatus.send(portSendMessages.sendPort); 93 | 94 | ClientChannel client; 95 | 96 | // waiting messages to send 97 | await for (MessageOutgoing message in portSendMessages) { 98 | var sent = false; 99 | do { 100 | // create new client 101 | client ??= ClientChannel( 102 | serverIP, // Your IP here or localhost 103 | port: serverPort, 104 | options: ChannelOptions( 105 | //TODO: Change to secure with server certificates 106 | credentials: ChannelCredentials.insecure(), 107 | idleTimeout: Duration(seconds: 1), 108 | ), 109 | ); 110 | 111 | try { 112 | // try to send 113 | var request = StringValue.create(); 114 | request.value = message.text; 115 | await grpc.ChatServiceClient(client).send(request); 116 | // sent successfully 117 | portSendStatus.send(MessageSentEvent(id: message.id)); 118 | sent = true; 119 | } catch (e) { 120 | // sent failed 121 | portSendStatus.send( 122 | MessageSendFailedEvent(id: message.id, error: e.toString())); 123 | // reset client 124 | client.shutdown(); 125 | client = null; 126 | } 127 | 128 | if (!sent) { 129 | // try to send again 130 | sleep(Duration(seconds: 5)); 131 | } 132 | } while (!sent); 133 | } 134 | } 135 | 136 | /// Start listening messages from the server 137 | void _startReceiving() async { 138 | // start thread to receive messages 139 | _isolateReceiving = 140 | await Isolate.spawn(_receivingIsolate, _portReceiving.sendPort); 141 | 142 | // listen for incoming messages 143 | await for (var event in _portReceiving) { 144 | if (event is MessageReceivedEvent) { 145 | if (onMessageReceived != null) { 146 | onMessageReceived(event); 147 | } 148 | } else if (event is MessageReceiveFailedEvent) { 149 | if (onMessageReceiveFailed != null) { 150 | onMessageReceiveFailed(event); 151 | } 152 | } 153 | } 154 | } 155 | 156 | /// Thread to listen messages from the server 157 | static void _receivingIsolate(SendPort portReceive) async { 158 | ClientChannel client; 159 | 160 | do { 161 | // create new client 162 | client ??= ClientChannel( 163 | serverIP, // Your IP here or localhost 164 | port: serverPort, 165 | options: ChannelOptions( 166 | //TODO: Change to secure with server certificates 167 | credentials: ChannelCredentials.insecure(), 168 | idleTimeout: Duration(seconds: 1), 169 | ), 170 | ); 171 | 172 | var stream = grpc.ChatServiceClient(client).subscribe(Empty.create()); 173 | 174 | try { 175 | await for (var message in stream) { 176 | portReceive.send(MessageReceivedEvent(text: message.text)); 177 | } 178 | } catch (e) { 179 | // notify caller 180 | portReceive.send(MessageReceiveFailedEvent(error: e.toString())); 181 | // reset client 182 | client.shutdown(); 183 | client = null; 184 | } 185 | // try to connect again 186 | sleep(Duration(seconds: 5)); 187 | } while (true); 188 | } 189 | 190 | // Shutdown client 191 | void shutdown() { 192 | // stop sending 193 | _isolateSending?.kill(priority: Isolate.immediate); 194 | _isolateSending = null; 195 | _portSendStatus?.close(); 196 | _portSendStatus = null; 197 | 198 | // stop receiving 199 | _isolateReceiving?.kill(priority: Isolate.immediate); 200 | _isolateReceiving = null; 201 | _portReceiving?.close(); 202 | _portReceiving = null; 203 | } 204 | 205 | /// Send message to the server 206 | void send(MessageOutgoing message) { 207 | assert(_portSending != null, "Port to send message can't be null"); 208 | _portSending.send(message); 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /flutter_client/lib/api/v1/chat.pb.dart: -------------------------------------------------------------------------------- 1 | /// 2 | // Generated code. Do not modify. 3 | // source: chat.proto 4 | /// 5 | // ignore_for_file: non_constant_identifier_names,library_prefixes,unused_import 6 | 7 | // ignore: UNUSED_SHOWN_NAME 8 | import 'dart:core' show int, bool, double, String, List, Map, override; 9 | 10 | import 'package:protobuf/protobuf.dart' as $pb; 11 | 12 | class Message extends $pb.GeneratedMessage { 13 | static final $pb.BuilderInfo _i = new $pb.BuilderInfo('Message', package: const $pb.PackageName('v1')) 14 | ..aOS(1, 'text') 15 | ..hasRequiredFields = false 16 | ; 17 | 18 | Message() : super(); 19 | Message.fromBuffer(List i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) : super.fromBuffer(i, r); 20 | Message.fromJson(String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) : super.fromJson(i, r); 21 | Message clone() => new Message()..mergeFromMessage(this); 22 | Message copyWith(void Function(Message) updates) => super.copyWith((message) => updates(message as Message)); 23 | $pb.BuilderInfo get info_ => _i; 24 | static Message create() => new Message(); 25 | Message createEmptyInstance() => create(); 26 | static $pb.PbList createRepeated() => new $pb.PbList(); 27 | static Message getDefault() => _defaultInstance ??= create()..freeze(); 28 | static Message _defaultInstance; 29 | static void $checkItem(Message v) { 30 | if (v is! Message) $pb.checkItemFailed(v, _i.qualifiedMessageName); 31 | } 32 | 33 | String get text => $_getS(0, ''); 34 | set text(String v) { $_setString(0, v); } 35 | bool hasText() => $_has(0); 36 | void clearText() => clearField(1); 37 | } 38 | 39 | -------------------------------------------------------------------------------- /flutter_client/lib/api/v1/chat.pbenum.dart: -------------------------------------------------------------------------------- 1 | /// 2 | // Generated code. Do not modify. 3 | // source: chat.proto 4 | /// 5 | // ignore_for_file: non_constant_identifier_names,library_prefixes,unused_import 6 | 7 | -------------------------------------------------------------------------------- /flutter_client/lib/api/v1/chat.pbgrpc.dart: -------------------------------------------------------------------------------- 1 | /// 2 | // Generated code. Do not modify. 3 | // source: chat.proto 4 | /// 5 | // ignore_for_file: non_constant_identifier_names,library_prefixes,unused_import 6 | 7 | import 'dart:async' as $async; 8 | 9 | import 'package:grpc/grpc.dart'; 10 | 11 | import 'google/protobuf/wrappers.pb.dart' as $0; 12 | import 'google/protobuf/empty.pb.dart' as $1; 13 | import 'chat.pb.dart'; 14 | export 'chat.pb.dart'; 15 | 16 | class ChatServiceClient extends Client { 17 | static final _$send = new ClientMethod<$0.StringValue, $1.Empty>( 18 | '/v1.ChatService/Send', 19 | ($0.StringValue value) => value.writeToBuffer(), 20 | (List value) => new $1.Empty.fromBuffer(value)); 21 | static final _$subscribe = new ClientMethod<$1.Empty, Message>( 22 | '/v1.ChatService/Subscribe', 23 | ($1.Empty value) => value.writeToBuffer(), 24 | (List value) => new Message.fromBuffer(value)); 25 | 26 | ChatServiceClient(ClientChannel channel, {CallOptions options}) 27 | : super(channel, options: options); 28 | 29 | ResponseFuture<$1.Empty> send($0.StringValue request, {CallOptions options}) { 30 | final call = $createCall(_$send, new $async.Stream.fromIterable([request]), 31 | options: options); 32 | return new ResponseFuture(call); 33 | } 34 | 35 | ResponseStream subscribe($1.Empty request, {CallOptions options}) { 36 | final call = $createCall( 37 | _$subscribe, new $async.Stream.fromIterable([request]), 38 | options: options); 39 | return new ResponseStream(call); 40 | } 41 | } 42 | 43 | abstract class ChatServiceBase extends Service { 44 | String get $name => 'v1.ChatService'; 45 | 46 | ChatServiceBase() { 47 | $addMethod(new ServiceMethod<$0.StringValue, $1.Empty>( 48 | 'Send', 49 | send_Pre, 50 | false, 51 | false, 52 | (List value) => new $0.StringValue.fromBuffer(value), 53 | ($1.Empty value) => value.writeToBuffer())); 54 | $addMethod(new ServiceMethod<$1.Empty, Message>( 55 | 'Subscribe', 56 | subscribe_Pre, 57 | false, 58 | true, 59 | (List value) => new $1.Empty.fromBuffer(value), 60 | (Message value) => value.writeToBuffer())); 61 | } 62 | 63 | $async.Future<$1.Empty> send_Pre( 64 | ServiceCall call, $async.Future request) async { 65 | return send(call, await request); 66 | } 67 | 68 | $async.Stream subscribe_Pre( 69 | ServiceCall call, $async.Future request) async* { 70 | yield* subscribe(call, (await request) as $1.Empty); 71 | } 72 | 73 | $async.Future<$1.Empty> send(ServiceCall call, $0.StringValue request); 74 | $async.Stream subscribe(ServiceCall call, $1.Empty request); 75 | } 76 | -------------------------------------------------------------------------------- /flutter_client/lib/api/v1/chat.pbjson.dart: -------------------------------------------------------------------------------- 1 | /// 2 | // Generated code. Do not modify. 3 | // source: chat.proto 4 | /// 5 | // ignore_for_file: non_constant_identifier_names,library_prefixes,unused_import 6 | 7 | const Message$json = const { 8 | '1': 'Message', 9 | '2': const [ 10 | const {'1': 'text', '3': 1, '4': 1, '5': 9, '10': 'text'}, 11 | ], 12 | }; 13 | 14 | -------------------------------------------------------------------------------- /flutter_client/lib/api/v1/google/protobuf/empty.pb.dart: -------------------------------------------------------------------------------- 1 | /// 2 | // Generated code. Do not modify. 3 | // source: empty.proto 4 | /// 5 | // ignore_for_file: non_constant_identifier_names,library_prefixes,unused_import 6 | 7 | // ignore: UNUSED_SHOWN_NAME 8 | import 'dart:core' show int, bool, double, String, List, Map, override; 9 | 10 | import 'package:protobuf/protobuf.dart' as $pb; 11 | 12 | class Empty extends $pb.GeneratedMessage { 13 | static final $pb.BuilderInfo _i = new $pb.BuilderInfo('Empty', package: const $pb.PackageName('google.protobuf')) 14 | ..hasRequiredFields = false 15 | ; 16 | 17 | Empty() : super(); 18 | Empty.fromBuffer(List i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) : super.fromBuffer(i, r); 19 | Empty.fromJson(String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) : super.fromJson(i, r); 20 | Empty clone() => new Empty()..mergeFromMessage(this); 21 | Empty copyWith(void Function(Empty) updates) => super.copyWith((message) => updates(message as Empty)); 22 | $pb.BuilderInfo get info_ => _i; 23 | static Empty create() => new Empty(); 24 | Empty createEmptyInstance() => create(); 25 | static $pb.PbList createRepeated() => new $pb.PbList(); 26 | static Empty getDefault() => _defaultInstance ??= create()..freeze(); 27 | static Empty _defaultInstance; 28 | static void $checkItem(Empty v) { 29 | if (v is! Empty) $pb.checkItemFailed(v, _i.qualifiedMessageName); 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /flutter_client/lib/api/v1/google/protobuf/empty.pbenum.dart: -------------------------------------------------------------------------------- 1 | /// 2 | // Generated code. Do not modify. 3 | // source: empty.proto 4 | /// 5 | // ignore_for_file: non_constant_identifier_names,library_prefixes,unused_import 6 | 7 | -------------------------------------------------------------------------------- /flutter_client/lib/api/v1/google/protobuf/empty.pbjson.dart: -------------------------------------------------------------------------------- 1 | /// 2 | // Generated code. Do not modify. 3 | // source: empty.proto 4 | /// 5 | // ignore_for_file: non_constant_identifier_names,library_prefixes,unused_import 6 | 7 | const Empty$json = const { 8 | '1': 'Empty', 9 | }; 10 | 11 | -------------------------------------------------------------------------------- /flutter_client/lib/api/v1/google/protobuf/timestamp.pb.dart: -------------------------------------------------------------------------------- 1 | /// 2 | // Generated code. Do not modify. 3 | // source: timestamp.proto 4 | /// 5 | // ignore_for_file: non_constant_identifier_names,library_prefixes,unused_import 6 | 7 | // ignore: UNUSED_SHOWN_NAME 8 | import 'dart:core' show int, bool, double, String, List, Map, override; 9 | 10 | import 'package:fixnum/fixnum.dart'; 11 | import 'package:protobuf/protobuf.dart' as $pb; 12 | 13 | class Timestamp extends $pb.GeneratedMessage { 14 | static final $pb.BuilderInfo _i = new $pb.BuilderInfo('Timestamp', package: const $pb.PackageName('google.protobuf')) 15 | ..aInt64(1, 'seconds') 16 | ..a(2, 'nanos', $pb.PbFieldType.O3) 17 | ..hasRequiredFields = false 18 | ; 19 | 20 | Timestamp() : super(); 21 | Timestamp.fromBuffer(List i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) : super.fromBuffer(i, r); 22 | Timestamp.fromJson(String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) : super.fromJson(i, r); 23 | Timestamp clone() => new Timestamp()..mergeFromMessage(this); 24 | Timestamp copyWith(void Function(Timestamp) updates) => super.copyWith((message) => updates(message as Timestamp)); 25 | $pb.BuilderInfo get info_ => _i; 26 | static Timestamp create() => new Timestamp(); 27 | Timestamp createEmptyInstance() => create(); 28 | static $pb.PbList createRepeated() => new $pb.PbList(); 29 | static Timestamp getDefault() => _defaultInstance ??= create()..freeze(); 30 | static Timestamp _defaultInstance; 31 | static void $checkItem(Timestamp v) { 32 | if (v is! Timestamp) $pb.checkItemFailed(v, _i.qualifiedMessageName); 33 | } 34 | 35 | Int64 get seconds => $_getI64(0); 36 | set seconds(Int64 v) { $_setInt64(0, v); } 37 | bool hasSeconds() => $_has(0); 38 | void clearSeconds() => clearField(1); 39 | 40 | int get nanos => $_get(1, 0); 41 | set nanos(int v) { $_setSignedInt32(1, v); } 42 | bool hasNanos() => $_has(1); 43 | void clearNanos() => clearField(2); 44 | } 45 | 46 | -------------------------------------------------------------------------------- /flutter_client/lib/api/v1/google/protobuf/timestamp.pbenum.dart: -------------------------------------------------------------------------------- 1 | /// 2 | // Generated code. Do not modify. 3 | // source: timestamp.proto 4 | /// 5 | // ignore_for_file: non_constant_identifier_names,library_prefixes,unused_import 6 | 7 | -------------------------------------------------------------------------------- /flutter_client/lib/api/v1/google/protobuf/timestamp.pbjson.dart: -------------------------------------------------------------------------------- 1 | /// 2 | // Generated code. Do not modify. 3 | // source: timestamp.proto 4 | /// 5 | // ignore_for_file: non_constant_identifier_names,library_prefixes,unused_import 6 | 7 | const Timestamp$json = const { 8 | '1': 'Timestamp', 9 | '2': const [ 10 | const {'1': 'seconds', '3': 1, '4': 1, '5': 3, '10': 'seconds'}, 11 | const {'1': 'nanos', '3': 2, '4': 1, '5': 5, '10': 'nanos'}, 12 | ], 13 | }; 14 | 15 | -------------------------------------------------------------------------------- /flutter_client/lib/api/v1/google/protobuf/wrappers.pb.dart: -------------------------------------------------------------------------------- 1 | /// 2 | // Generated code. Do not modify. 3 | // source: wrappers.proto 4 | /// 5 | // ignore_for_file: non_constant_identifier_names,library_prefixes,unused_import 6 | 7 | // ignore: UNUSED_SHOWN_NAME 8 | import 'dart:core' show int, bool, double, String, List, Map, override; 9 | 10 | import 'package:fixnum/fixnum.dart'; 11 | import 'package:protobuf/protobuf.dart' as $pb; 12 | 13 | class DoubleValue extends $pb.GeneratedMessage { 14 | static final $pb.BuilderInfo _i = new $pb.BuilderInfo('DoubleValue', package: const $pb.PackageName('google.protobuf')) 15 | ..a(1, 'value', $pb.PbFieldType.OD) 16 | ..hasRequiredFields = false 17 | ; 18 | 19 | DoubleValue() : super(); 20 | DoubleValue.fromBuffer(List i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) : super.fromBuffer(i, r); 21 | DoubleValue.fromJson(String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) : super.fromJson(i, r); 22 | DoubleValue clone() => new DoubleValue()..mergeFromMessage(this); 23 | DoubleValue copyWith(void Function(DoubleValue) updates) => super.copyWith((message) => updates(message as DoubleValue)); 24 | $pb.BuilderInfo get info_ => _i; 25 | static DoubleValue create() => new DoubleValue(); 26 | DoubleValue createEmptyInstance() => create(); 27 | static $pb.PbList createRepeated() => new $pb.PbList(); 28 | static DoubleValue getDefault() => _defaultInstance ??= create()..freeze(); 29 | static DoubleValue _defaultInstance; 30 | static void $checkItem(DoubleValue v) { 31 | if (v is! DoubleValue) $pb.checkItemFailed(v, _i.qualifiedMessageName); 32 | } 33 | 34 | double get value => $_getN(0); 35 | set value(double v) { $_setDouble(0, v); } 36 | bool hasValue() => $_has(0); 37 | void clearValue() => clearField(1); 38 | } 39 | 40 | class FloatValue extends $pb.GeneratedMessage { 41 | static final $pb.BuilderInfo _i = new $pb.BuilderInfo('FloatValue', package: const $pb.PackageName('google.protobuf')) 42 | ..a(1, 'value', $pb.PbFieldType.OF) 43 | ..hasRequiredFields = false 44 | ; 45 | 46 | FloatValue() : super(); 47 | FloatValue.fromBuffer(List i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) : super.fromBuffer(i, r); 48 | FloatValue.fromJson(String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) : super.fromJson(i, r); 49 | FloatValue clone() => new FloatValue()..mergeFromMessage(this); 50 | FloatValue copyWith(void Function(FloatValue) updates) => super.copyWith((message) => updates(message as FloatValue)); 51 | $pb.BuilderInfo get info_ => _i; 52 | static FloatValue create() => new FloatValue(); 53 | FloatValue createEmptyInstance() => create(); 54 | static $pb.PbList createRepeated() => new $pb.PbList(); 55 | static FloatValue getDefault() => _defaultInstance ??= create()..freeze(); 56 | static FloatValue _defaultInstance; 57 | static void $checkItem(FloatValue v) { 58 | if (v is! FloatValue) $pb.checkItemFailed(v, _i.qualifiedMessageName); 59 | } 60 | 61 | double get value => $_getN(0); 62 | set value(double v) { $_setFloat(0, v); } 63 | bool hasValue() => $_has(0); 64 | void clearValue() => clearField(1); 65 | } 66 | 67 | class Int64Value extends $pb.GeneratedMessage { 68 | static final $pb.BuilderInfo _i = new $pb.BuilderInfo('Int64Value', package: const $pb.PackageName('google.protobuf')) 69 | ..aInt64(1, 'value') 70 | ..hasRequiredFields = false 71 | ; 72 | 73 | Int64Value() : super(); 74 | Int64Value.fromBuffer(List i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) : super.fromBuffer(i, r); 75 | Int64Value.fromJson(String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) : super.fromJson(i, r); 76 | Int64Value clone() => new Int64Value()..mergeFromMessage(this); 77 | Int64Value copyWith(void Function(Int64Value) updates) => super.copyWith((message) => updates(message as Int64Value)); 78 | $pb.BuilderInfo get info_ => _i; 79 | static Int64Value create() => new Int64Value(); 80 | Int64Value createEmptyInstance() => create(); 81 | static $pb.PbList createRepeated() => new $pb.PbList(); 82 | static Int64Value getDefault() => _defaultInstance ??= create()..freeze(); 83 | static Int64Value _defaultInstance; 84 | static void $checkItem(Int64Value v) { 85 | if (v is! Int64Value) $pb.checkItemFailed(v, _i.qualifiedMessageName); 86 | } 87 | 88 | Int64 get value => $_getI64(0); 89 | set value(Int64 v) { $_setInt64(0, v); } 90 | bool hasValue() => $_has(0); 91 | void clearValue() => clearField(1); 92 | } 93 | 94 | class UInt64Value extends $pb.GeneratedMessage { 95 | static final $pb.BuilderInfo _i = new $pb.BuilderInfo('UInt64Value', package: const $pb.PackageName('google.protobuf')) 96 | ..a(1, 'value', $pb.PbFieldType.OU6, Int64.ZERO) 97 | ..hasRequiredFields = false 98 | ; 99 | 100 | UInt64Value() : super(); 101 | UInt64Value.fromBuffer(List i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) : super.fromBuffer(i, r); 102 | UInt64Value.fromJson(String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) : super.fromJson(i, r); 103 | UInt64Value clone() => new UInt64Value()..mergeFromMessage(this); 104 | UInt64Value copyWith(void Function(UInt64Value) updates) => super.copyWith((message) => updates(message as UInt64Value)); 105 | $pb.BuilderInfo get info_ => _i; 106 | static UInt64Value create() => new UInt64Value(); 107 | UInt64Value createEmptyInstance() => create(); 108 | static $pb.PbList createRepeated() => new $pb.PbList(); 109 | static UInt64Value getDefault() => _defaultInstance ??= create()..freeze(); 110 | static UInt64Value _defaultInstance; 111 | static void $checkItem(UInt64Value v) { 112 | if (v is! UInt64Value) $pb.checkItemFailed(v, _i.qualifiedMessageName); 113 | } 114 | 115 | Int64 get value => $_getI64(0); 116 | set value(Int64 v) { $_setInt64(0, v); } 117 | bool hasValue() => $_has(0); 118 | void clearValue() => clearField(1); 119 | } 120 | 121 | class Int32Value extends $pb.GeneratedMessage { 122 | static final $pb.BuilderInfo _i = new $pb.BuilderInfo('Int32Value', package: const $pb.PackageName('google.protobuf')) 123 | ..a(1, 'value', $pb.PbFieldType.O3) 124 | ..hasRequiredFields = false 125 | ; 126 | 127 | Int32Value() : super(); 128 | Int32Value.fromBuffer(List i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) : super.fromBuffer(i, r); 129 | Int32Value.fromJson(String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) : super.fromJson(i, r); 130 | Int32Value clone() => new Int32Value()..mergeFromMessage(this); 131 | Int32Value copyWith(void Function(Int32Value) updates) => super.copyWith((message) => updates(message as Int32Value)); 132 | $pb.BuilderInfo get info_ => _i; 133 | static Int32Value create() => new Int32Value(); 134 | Int32Value createEmptyInstance() => create(); 135 | static $pb.PbList createRepeated() => new $pb.PbList(); 136 | static Int32Value getDefault() => _defaultInstance ??= create()..freeze(); 137 | static Int32Value _defaultInstance; 138 | static void $checkItem(Int32Value v) { 139 | if (v is! Int32Value) $pb.checkItemFailed(v, _i.qualifiedMessageName); 140 | } 141 | 142 | int get value => $_get(0, 0); 143 | set value(int v) { $_setSignedInt32(0, v); } 144 | bool hasValue() => $_has(0); 145 | void clearValue() => clearField(1); 146 | } 147 | 148 | class UInt32Value extends $pb.GeneratedMessage { 149 | static final $pb.BuilderInfo _i = new $pb.BuilderInfo('UInt32Value', package: const $pb.PackageName('google.protobuf')) 150 | ..a(1, 'value', $pb.PbFieldType.OU3) 151 | ..hasRequiredFields = false 152 | ; 153 | 154 | UInt32Value() : super(); 155 | UInt32Value.fromBuffer(List i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) : super.fromBuffer(i, r); 156 | UInt32Value.fromJson(String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) : super.fromJson(i, r); 157 | UInt32Value clone() => new UInt32Value()..mergeFromMessage(this); 158 | UInt32Value copyWith(void Function(UInt32Value) updates) => super.copyWith((message) => updates(message as UInt32Value)); 159 | $pb.BuilderInfo get info_ => _i; 160 | static UInt32Value create() => new UInt32Value(); 161 | UInt32Value createEmptyInstance() => create(); 162 | static $pb.PbList createRepeated() => new $pb.PbList(); 163 | static UInt32Value getDefault() => _defaultInstance ??= create()..freeze(); 164 | static UInt32Value _defaultInstance; 165 | static void $checkItem(UInt32Value v) { 166 | if (v is! UInt32Value) $pb.checkItemFailed(v, _i.qualifiedMessageName); 167 | } 168 | 169 | int get value => $_get(0, 0); 170 | set value(int v) { $_setUnsignedInt32(0, v); } 171 | bool hasValue() => $_has(0); 172 | void clearValue() => clearField(1); 173 | } 174 | 175 | class BoolValue extends $pb.GeneratedMessage { 176 | static final $pb.BuilderInfo _i = new $pb.BuilderInfo('BoolValue', package: const $pb.PackageName('google.protobuf')) 177 | ..aOB(1, 'value') 178 | ..hasRequiredFields = false 179 | ; 180 | 181 | BoolValue() : super(); 182 | BoolValue.fromBuffer(List i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) : super.fromBuffer(i, r); 183 | BoolValue.fromJson(String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) : super.fromJson(i, r); 184 | BoolValue clone() => new BoolValue()..mergeFromMessage(this); 185 | BoolValue copyWith(void Function(BoolValue) updates) => super.copyWith((message) => updates(message as BoolValue)); 186 | $pb.BuilderInfo get info_ => _i; 187 | static BoolValue create() => new BoolValue(); 188 | BoolValue createEmptyInstance() => create(); 189 | static $pb.PbList createRepeated() => new $pb.PbList(); 190 | static BoolValue getDefault() => _defaultInstance ??= create()..freeze(); 191 | static BoolValue _defaultInstance; 192 | static void $checkItem(BoolValue v) { 193 | if (v is! BoolValue) $pb.checkItemFailed(v, _i.qualifiedMessageName); 194 | } 195 | 196 | bool get value => $_get(0, false); 197 | set value(bool v) { $_setBool(0, v); } 198 | bool hasValue() => $_has(0); 199 | void clearValue() => clearField(1); 200 | } 201 | 202 | class StringValue extends $pb.GeneratedMessage { 203 | static final $pb.BuilderInfo _i = new $pb.BuilderInfo('StringValue', package: const $pb.PackageName('google.protobuf')) 204 | ..aOS(1, 'value') 205 | ..hasRequiredFields = false 206 | ; 207 | 208 | StringValue() : super(); 209 | StringValue.fromBuffer(List i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) : super.fromBuffer(i, r); 210 | StringValue.fromJson(String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) : super.fromJson(i, r); 211 | StringValue clone() => new StringValue()..mergeFromMessage(this); 212 | StringValue copyWith(void Function(StringValue) updates) => super.copyWith((message) => updates(message as StringValue)); 213 | $pb.BuilderInfo get info_ => _i; 214 | static StringValue create() => new StringValue(); 215 | StringValue createEmptyInstance() => create(); 216 | static $pb.PbList createRepeated() => new $pb.PbList(); 217 | static StringValue getDefault() => _defaultInstance ??= create()..freeze(); 218 | static StringValue _defaultInstance; 219 | static void $checkItem(StringValue v) { 220 | if (v is! StringValue) $pb.checkItemFailed(v, _i.qualifiedMessageName); 221 | } 222 | 223 | String get value => $_getS(0, ''); 224 | set value(String v) { $_setString(0, v); } 225 | bool hasValue() => $_has(0); 226 | void clearValue() => clearField(1); 227 | } 228 | 229 | class BytesValue extends $pb.GeneratedMessage { 230 | static final $pb.BuilderInfo _i = new $pb.BuilderInfo('BytesValue', package: const $pb.PackageName('google.protobuf')) 231 | ..a>(1, 'value', $pb.PbFieldType.OY) 232 | ..hasRequiredFields = false 233 | ; 234 | 235 | BytesValue() : super(); 236 | BytesValue.fromBuffer(List i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) : super.fromBuffer(i, r); 237 | BytesValue.fromJson(String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) : super.fromJson(i, r); 238 | BytesValue clone() => new BytesValue()..mergeFromMessage(this); 239 | BytesValue copyWith(void Function(BytesValue) updates) => super.copyWith((message) => updates(message as BytesValue)); 240 | $pb.BuilderInfo get info_ => _i; 241 | static BytesValue create() => new BytesValue(); 242 | BytesValue createEmptyInstance() => create(); 243 | static $pb.PbList createRepeated() => new $pb.PbList(); 244 | static BytesValue getDefault() => _defaultInstance ??= create()..freeze(); 245 | static BytesValue _defaultInstance; 246 | static void $checkItem(BytesValue v) { 247 | if (v is! BytesValue) $pb.checkItemFailed(v, _i.qualifiedMessageName); 248 | } 249 | 250 | List get value => $_getN(0); 251 | set value(List v) { $_setBytes(0, v); } 252 | bool hasValue() => $_has(0); 253 | void clearValue() => clearField(1); 254 | } 255 | 256 | -------------------------------------------------------------------------------- /flutter_client/lib/api/v1/google/protobuf/wrappers.pbenum.dart: -------------------------------------------------------------------------------- 1 | /// 2 | // Generated code. Do not modify. 3 | // source: wrappers.proto 4 | /// 5 | // ignore_for_file: non_constant_identifier_names,library_prefixes,unused_import 6 | 7 | -------------------------------------------------------------------------------- /flutter_client/lib/api/v1/google/protobuf/wrappers.pbjson.dart: -------------------------------------------------------------------------------- 1 | /// 2 | // Generated code. Do not modify. 3 | // source: wrappers.proto 4 | /// 5 | // ignore_for_file: non_constant_identifier_names,library_prefixes,unused_import 6 | 7 | const DoubleValue$json = const { 8 | '1': 'DoubleValue', 9 | '2': const [ 10 | const {'1': 'value', '3': 1, '4': 1, '5': 1, '10': 'value'}, 11 | ], 12 | }; 13 | 14 | const FloatValue$json = const { 15 | '1': 'FloatValue', 16 | '2': const [ 17 | const {'1': 'value', '3': 1, '4': 1, '5': 2, '10': 'value'}, 18 | ], 19 | }; 20 | 21 | const Int64Value$json = const { 22 | '1': 'Int64Value', 23 | '2': const [ 24 | const {'1': 'value', '3': 1, '4': 1, '5': 3, '10': 'value'}, 25 | ], 26 | }; 27 | 28 | const UInt64Value$json = const { 29 | '1': 'UInt64Value', 30 | '2': const [ 31 | const {'1': 'value', '3': 1, '4': 1, '5': 4, '10': 'value'}, 32 | ], 33 | }; 34 | 35 | const Int32Value$json = const { 36 | '1': 'Int32Value', 37 | '2': const [ 38 | const {'1': 'value', '3': 1, '4': 1, '5': 5, '10': 'value'}, 39 | ], 40 | }; 41 | 42 | const UInt32Value$json = const { 43 | '1': 'UInt32Value', 44 | '2': const [ 45 | const {'1': 'value', '3': 1, '4': 1, '5': 13, '10': 'value'}, 46 | ], 47 | }; 48 | 49 | const BoolValue$json = const { 50 | '1': 'BoolValue', 51 | '2': const [ 52 | const {'1': 'value', '3': 1, '4': 1, '5': 8, '10': 'value'}, 53 | ], 54 | }; 55 | 56 | const StringValue$json = const { 57 | '1': 'StringValue', 58 | '2': const [ 59 | const {'1': 'value', '3': 1, '4': 1, '5': 9, '10': 'value'}, 60 | ], 61 | }; 62 | 63 | const BytesValue$json = const { 64 | '1': 'BytesValue', 65 | '2': const [ 66 | const {'1': 'value', '3': 1, '4': 1, '5': 12, '10': 'value'}, 67 | ], 68 | }; 69 | 70 | -------------------------------------------------------------------------------- /flutter_client/lib/blocs/application_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:collection'; 2 | 3 | import 'package:rxdart/rxdart.dart'; 4 | 5 | import 'package:flutter_client/models/message.dart'; 6 | import 'package:flutter_client/models/message_incoming.dart'; 7 | import 'package:flutter_client/models/message_outgoing.dart'; 8 | 9 | import 'bloc_provider.dart'; 10 | import 'message_events.dart'; 11 | 12 | /// BLoC class 13 | class ApplicationBloc implements BlocBase { 14 | // Application state (chat messages) 15 | final _messages = Set(); 16 | 17 | /// Controller is used to notify when message created 18 | final _messageCreatedController = 19 | new BehaviorSubject(); 20 | Sink get inNewMessageCreated => 21 | _messageCreatedController.sink; 22 | 23 | /// Controller is used to send message to the server 24 | final _messageSendController = new BehaviorSubject(); 25 | Sink get inMessageSend => _messageSendController.sink; 26 | Stream get outMessageSend => 27 | _messageSendController.stream; 28 | 29 | /// Controller is used to notify when message sent to the server 30 | final _messageSentController = new BehaviorSubject(); 31 | Sink get inMessageSent => _messageSentController.sink; 32 | 33 | /// Controller is used to notify when message failed to send to the server 34 | final _messageSendFailedController = 35 | new BehaviorSubject(); 36 | Sink get inMessageSendFailed => 37 | _messageSendFailedController.sink; 38 | 39 | /// Controller is used to notify when message received from the server 40 | final _messageReceivedController = 41 | new BehaviorSubject(); 42 | Sink get inMessageReceived => 43 | _messageReceivedController.sink; 44 | 45 | /// Controller is used to provide state (chat messages) to the widgets 46 | final _messagesController = new BehaviorSubject>(seedValue: []); 47 | Sink> get _inMessages => _messagesController.sink; 48 | Stream> get outMessages => _messagesController.stream; 49 | 50 | /// Constructor 51 | ApplicationBloc() { 52 | _messageCreatedController.listen(_onNewMessageCreated); 53 | _messageSentController.listen(_onMessageSent); 54 | _messageSendFailedController.listen(_onMessageSendFailed); 55 | _messageReceivedController.listen(_onMessageReceived); 56 | } 57 | 58 | /// Dispose resources 59 | void dispose() { 60 | _messageCreatedController.close(); 61 | _messageSendController.close(); 62 | _messageSentController.close(); 63 | _messageSendFailedController.close(); 64 | _messageReceivedController.close(); 65 | _messagesController.close(); 66 | } 67 | 68 | /// Handle event: new message created 69 | void _onNewMessageCreated(MessageNewCreatedEvent event) { 70 | _messages.add(event.message); 71 | _notify(); 72 | _messageSendController.add(event); 73 | } 74 | 75 | /// Handle event: message sent to the server 76 | void _onMessageSent(MessageSentEvent event) { 77 | _findOutgoingMessage(event.id).status = MessageOutgoingStatus.SENT; 78 | _notify(); 79 | } 80 | 81 | /// Handle event: message failed to send to the server 82 | void _onMessageSendFailed(MessageSendFailedEvent event) { 83 | _findOutgoingMessage(event.id).status = MessageOutgoingStatus.FAILED; 84 | _notify(); 85 | } 86 | 87 | /// Handle event: message received from the server 88 | void _onMessageReceived(MessageReceivedEvent event) { 89 | _messages.add(MessageIncoming(text: event.text)); 90 | _notify(); 91 | } 92 | 93 | /// Publish state (chat messages) to the widgets 94 | void _notify() { 95 | _inMessages.add(UnmodifiableListView(_messages)); 96 | } 97 | 98 | /// Find outgoing message by 'id' in the state (chat messages) 99 | MessageOutgoing _findOutgoingMessage(String id) { 100 | var message = 101 | _messages.firstWhere((message) => message.id == id, orElse: () => null); 102 | assert(message != null, 'Sent message with id="$id" is not found in state'); 103 | assert(message is MessageOutgoing, 104 | 'Invalid message (id="$id") type ${message.runtimeType}; must be MessageOutgoing'); 105 | return message; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /flutter_client/lib/blocs/bloc_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | Type _typeOf() => T; 4 | 5 | abstract class BlocBase { 6 | void dispose(); 7 | } 8 | 9 | class BlocProvider extends StatefulWidget { 10 | BlocProvider({ 11 | Key key, 12 | @required this.child, 13 | @required this.bloc, 14 | }) : super(key: key); 15 | 16 | final Widget child; 17 | final T bloc; 18 | 19 | @override 20 | _BlocProviderState createState() => _BlocProviderState(); 21 | 22 | static T of(BuildContext context) { 23 | final type = _typeOf<_BlocProviderInherited>(); 24 | _BlocProviderInherited provider = 25 | context.ancestorInheritedElementForWidgetOfExactType(type)?.widget; 26 | return provider?.bloc; 27 | } 28 | } 29 | 30 | class _BlocProviderState extends State> { 31 | @override 32 | void dispose() { 33 | widget.bloc?.dispose(); 34 | super.dispose(); 35 | } 36 | 37 | @override 38 | Widget build(BuildContext context) { 39 | return new _BlocProviderInherited( 40 | bloc: widget.bloc, 41 | child: widget.child, 42 | ); 43 | } 44 | } 45 | 46 | class _BlocProviderInherited extends InheritedWidget { 47 | _BlocProviderInherited({ 48 | Key key, 49 | @required Widget child, 50 | @required this.bloc, 51 | }) : super(key: key, child: child); 52 | 53 | final T bloc; 54 | 55 | @override 56 | bool updateShouldNotify(_BlocProviderInherited oldWidget) => false; 57 | } 58 | -------------------------------------------------------------------------------- /flutter_client/lib/blocs/message_events.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import 'package:flutter_client/models/message_outgoing.dart'; 4 | 5 | /// New message created event 6 | class MessageNewCreatedEvent { 7 | final MessageOutgoing message; 8 | 9 | MessageNewCreatedEvent({@required this.message}); 10 | } 11 | 12 | /// Message sent to the server event 13 | class MessageSentEvent { 14 | final String id; 15 | 16 | MessageSentEvent({@required this.id}); 17 | } 18 | 19 | /// Message failed to send to the server event 20 | class MessageSendFailedEvent { 21 | final String id; 22 | final String error; 23 | 24 | MessageSendFailedEvent({@required this.id, @required this.error}); 25 | } 26 | 27 | /// Message received from the server event 28 | class MessageReceivedEvent { 29 | final String text; 30 | 31 | MessageReceivedEvent({@required this.text}); 32 | } 33 | 34 | /// Message failed to receive from the server event 35 | class MessageReceiveFailedEvent { 36 | final String error; 37 | 38 | MessageReceiveFailedEvent({@required this.error}); 39 | } 40 | -------------------------------------------------------------------------------- /flutter_client/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:flutter_client/api/chat_service.dart'; 4 | 5 | import 'package:flutter_client/blocs/application_bloc.dart'; 6 | import 'package:flutter_client/blocs/bloc_provider.dart'; 7 | import 'package:flutter_client/blocs/message_events.dart'; 8 | 9 | import 'package:flutter_client/pages/home.dart'; 10 | 11 | import 'theme.dart'; 12 | 13 | /// main is entry point of Flutter application 14 | void main() { 15 | return runApp(BlocProvider( 16 | bloc: ApplicationBloc(), 17 | child: App(), 18 | )); 19 | } 20 | 21 | // Stateful application widget 22 | class App extends StatefulWidget { 23 | @override 24 | State createState() => _AppState(); 25 | } 26 | 27 | // State for application widget 28 | class _AppState extends State { 29 | // BLoc for application 30 | ApplicationBloc _appBloc; 31 | 32 | /// Chat client service 33 | ChatService _service; 34 | 35 | bool _isInit = false; 36 | 37 | @override 38 | void didChangeDependencies() { 39 | super.didChangeDependencies(); 40 | 41 | // As the context of not yet available at initState() level, 42 | // if not yet initialized, we get application BLoc to start 43 | // gRPC isolates 44 | if (_isInit == false) { 45 | _appBloc = BlocProvider.of(context); 46 | 47 | // initialize Chat client service 48 | _service = ChatService( 49 | onMessageSent: _onMessageSent, 50 | onMessageSendFailed: _onMessageSendFailed, 51 | onMessageReceived: _onMessageReceived, 52 | onMessageReceiveFailed: _onMessageReceiveFailed); 53 | _service.start(); 54 | 55 | _listenMessagesToSend(); 56 | 57 | if (mounted) { 58 | setState(() { 59 | _isInit = true; 60 | }); 61 | } 62 | } 63 | } 64 | 65 | void _listenMessagesToSend() async { 66 | await for (var event in _appBloc.outMessageSend) { 67 | _service.send(event.message); 68 | } 69 | } 70 | 71 | @override 72 | Widget build(BuildContext context) { 73 | return MaterialApp( 74 | title: 'Friendlychat', 75 | theme: isIOS(context) ? kIOSTheme : kDefaultTheme, 76 | home: HomePage(), 77 | ); 78 | } 79 | 80 | @override 81 | void dispose() { 82 | // close Chat client service 83 | _service.shutdown(); 84 | _service = null; 85 | 86 | super.dispose(); 87 | } 88 | 89 | /// 'outgoing message sent to the server' event 90 | void _onMessageSent(MessageSentEvent event) { 91 | debugPrint('Message "${event.id}" sent to the server'); 92 | _appBloc.inMessageSent.add(event); 93 | } 94 | 95 | /// 'failed to send message' event 96 | void _onMessageSendFailed(MessageSendFailedEvent event) { 97 | debugPrint( 98 | 'Failed to send message "${event.id}" to the server: ${event.error}'); 99 | _appBloc.inMessageSendFailed.add(event); 100 | } 101 | 102 | /// 'new incoming message received from the server' event 103 | void _onMessageReceived(MessageReceivedEvent event) { 104 | debugPrint('Message received from the server: ${event.text}'); 105 | _appBloc.inMessageReceived.add(event); 106 | } 107 | 108 | /// 'failed to receive messages' event 109 | void _onMessageReceiveFailed(MessageReceiveFailedEvent event) { 110 | debugPrint('Failed to receive messages from the server: ${event.error}'); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /flutter_client/lib/models/message.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// Message is class defining message data (id and text) 4 | class Message { 5 | /// id is unique ID of message 6 | final String id; 7 | 8 | /// text is content of message 9 | final String text; 10 | 11 | /// Class constructor 12 | Message({String id, @required String text}) 13 | : this.id = id ?? UniqueKey().toString(), 14 | this.text = text; 15 | } 16 | -------------------------------------------------------------------------------- /flutter_client/lib/models/message_incoming.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import 'message.dart'; 4 | 5 | /// MessageIncoming is class defining incoming message data (id and text) 6 | class MessageIncoming extends Message { 7 | /// Constructor 8 | MessageIncoming({String id, @required String text}) 9 | : super(id: id, text: text); 10 | 11 | MessageIncoming.copy(MessageIncoming original) 12 | : super(id: original.id, text: original.text); 13 | } 14 | -------------------------------------------------------------------------------- /flutter_client/lib/models/message_outgoing.dart: -------------------------------------------------------------------------------- 1 | import 'package:meta/meta.dart'; 2 | 3 | import 'message.dart'; 4 | 5 | /// Outgoing message statuses 6 | /// NEW - message just created and is not sent yet 7 | /// SENT - message is sent to the server successfully 8 | /// FAILED - error has happened while sending message 9 | enum MessageOutgoingStatus { NEW, SENT, FAILED } 10 | 11 | /// MessageOutgoing is class defining outgoing message data (id and text) and status 12 | class MessageOutgoing extends Message { 13 | /// Outgoing message status 14 | MessageOutgoingStatus status; 15 | 16 | /// Constructor 17 | MessageOutgoing( 18 | {String id, 19 | @required String text, 20 | MessageOutgoingStatus status = MessageOutgoingStatus.NEW}) 21 | : this.status = status, 22 | super(id: id, text: text); 23 | 24 | MessageOutgoing.copy(MessageOutgoing original) 25 | : this.status = original.status, 26 | super(id: original.id, text: original.text); 27 | } 28 | -------------------------------------------------------------------------------- /flutter_client/lib/pages/home.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | 4 | import 'package:flutter_client/blocs/application_bloc.dart'; 5 | import 'package:flutter_client/blocs/bloc_provider.dart'; 6 | import 'package:flutter_client/blocs/message_events.dart'; 7 | 8 | import 'package:flutter_client/models/message.dart'; 9 | import 'package:flutter_client/models/message_incoming.dart'; 10 | import 'package:flutter_client/models/message_outgoing.dart'; 11 | 12 | import 'package:flutter_client/theme.dart'; 13 | 14 | import 'package:flutter_client/widgets/chat_message.dart'; 15 | import 'package:flutter_client/widgets/chat_message_incoming.dart'; 16 | import 'package:flutter_client/widgets/chat_message_outgoing.dart'; 17 | 18 | /// Host screen widget - main window 19 | class HomePage extends StatefulWidget { 20 | // Constructor 21 | HomePage() : super(key: new ObjectKey("Main window")); 22 | 23 | @override 24 | State createState() => HomePageState(); 25 | } 26 | 27 | /// State for main window 28 | class HomePageState extends State with TickerProviderStateMixin { 29 | // BLoc for application 30 | ApplicationBloc _appBloc; 31 | 32 | /// Chat messages list to display into ListView 33 | final List _messages = []; 34 | 35 | /// Look at the https://codelabs.developers.google.com/codelabs/flutter/#4 36 | final TextEditingController _textController = TextEditingController(); 37 | bool _isComposing = false; 38 | 39 | bool _isInit = false; 40 | 41 | @override 42 | void didChangeDependencies() { 43 | super.didChangeDependencies(); 44 | 45 | // As the context of not yet available at initState() level, 46 | // if not yet initialized, we get the list of all genres 47 | // and retrieve the currently selected one, as well as the 48 | // filter parameters 49 | if (_isInit == false) { 50 | _appBloc = BlocProvider.of(context); 51 | _isInit = true; 52 | } 53 | } 54 | 55 | @override 56 | void dispose() { 57 | // free UI resources 58 | for (ChatMessage message in _messages) 59 | message.animationController.dispose(); 60 | super.dispose(); 61 | } 62 | 63 | @override 64 | Widget build(BuildContext context) { 65 | return Scaffold( 66 | appBar: AppBar( 67 | title: Text('Friendlychat'), 68 | elevation: isIOS(context) ? 0.0 : 4.0, 69 | ), 70 | body: new Container( 71 | child: new Column( 72 | children: [ 73 | Flexible( 74 | child: StreamBuilder( 75 | stream: _appBloc.outMessages, 76 | builder: (BuildContext context, 77 | AsyncSnapshot> snapshot) { 78 | if (snapshot.hasError) { 79 | return Text("Error: ${snapshot.error}"); 80 | } else if (snapshot.hasData) { 81 | // update messages according to the data 82 | _updateMessages(snapshot.data); 83 | } 84 | return ListView.builder( 85 | padding: EdgeInsets.all(8.0), 86 | reverse: true, 87 | itemBuilder: (_, int index) => _messages[index], 88 | itemCount: _messages.length); 89 | }, 90 | ), 91 | ), 92 | Divider(height: 1.0), 93 | Container( 94 | decoration: BoxDecoration(color: Theme.of(context).cardColor), 95 | child: _buildTextComposer(context), 96 | ), 97 | ], 98 | ), 99 | decoration: isIOS(context) 100 | ? new BoxDecoration( 101 | border: new Border( 102 | top: new BorderSide(color: Colors.grey[200]), 103 | ), 104 | ) 105 | : null), 106 | ); 107 | } 108 | 109 | /// Look at the https://codelabs.developers.google.com/codelabs/flutter/#4 110 | Widget _buildTextComposer(BuildContext context) { 111 | return IconTheme( 112 | data: IconThemeData(color: Theme.of(context).accentColor), 113 | child: Container( 114 | margin: const EdgeInsets.symmetric(horizontal: 8.0), 115 | child: Row( 116 | children: [ 117 | Flexible( 118 | child: isIOS(context) 119 | ? CupertinoTextField( 120 | key: Key('message-text-field'), 121 | maxLines: null, 122 | textInputAction: TextInputAction.send, 123 | controller: _textController, 124 | onChanged: (String text) { 125 | setState(() { 126 | _isComposing = text.length > 0; 127 | }); 128 | }, 129 | onSubmitted: _isComposing ? _handleSubmitted : null, 130 | ) 131 | : TextField( 132 | key: Key('message-text-field'), 133 | maxLines: null, 134 | textInputAction: TextInputAction.send, 135 | controller: _textController, 136 | onChanged: (String text) { 137 | setState(() { 138 | _isComposing = text.length > 0; 139 | }); 140 | }, 141 | onSubmitted: _isComposing ? _handleSubmitted : null, 142 | decoration: 143 | InputDecoration.collapsed(hintText: "Send a message"), 144 | ), 145 | ), 146 | Container( 147 | margin: EdgeInsets.symmetric(horizontal: 4.0), 148 | child: isIOS(context) 149 | ? new CupertinoButton( 150 | child: new Text("Send"), 151 | onPressed: _isComposing 152 | ? () => _handleSubmitted(_textController.text) 153 | : null, 154 | ) 155 | : new IconButton( 156 | key: Key('send-button'), 157 | icon: new Icon(Icons.send), 158 | onPressed: _isComposing 159 | ? () => _handleSubmitted(_textController.text) 160 | : null, 161 | ), 162 | ), 163 | ], 164 | ), 165 | ), 166 | ); 167 | } 168 | 169 | /// 'new outgoing message created' event 170 | void _handleSubmitted(String text) { 171 | _textController.clear(); 172 | _isComposing = false; 173 | 174 | _appBloc.inNewMessageCreated 175 | .add(MessageNewCreatedEvent(message: MessageOutgoing(text: text))); 176 | } 177 | 178 | /// this methods is called to display new (outgoing or incoming) message or 179 | /// update status of existing outgoing message 180 | void _updateMessages(List messages) { 181 | for (var message in messages) { 182 | int i = _messages.indexWhere((msg) => msg.message.id == message.id); 183 | if (i != -1) { 184 | // existing message 185 | if (message is MessageOutgoing) { 186 | // update existing message 187 | var chatMessage = _messages[i]; 188 | if (chatMessage is ChatMessageOutgoing) { 189 | if (chatMessage.message.status != message.status) { 190 | // dispose previous message 191 | chatMessage.animationController.dispose(); 192 | // update status for outgoing message 193 | _messages[i] = ChatMessageOutgoing( 194 | message: MessageOutgoing.copy(message), 195 | animationController: AnimationController( 196 | duration: Duration.zero, 197 | vsync: this, 198 | ), 199 | ); 200 | } 201 | _messages[i].animationController.forward(); 202 | } else { 203 | assert(false, 'Message must be MessageOutcome type'); 204 | } 205 | } 206 | } else { 207 | ChatMessage chatMessage; 208 | // new message 209 | var animationController = AnimationController( 210 | duration: Duration(milliseconds: 700), 211 | vsync: this, 212 | ); 213 | if (message is MessageOutgoing) { 214 | // add new outgoing message 215 | chatMessage = ChatMessageOutgoing( 216 | message: MessageOutgoing.copy(message), 217 | animationController: animationController, 218 | ); 219 | } else if (message is MessageIncoming) { 220 | // add new incoming message 221 | chatMessage = ChatMessageIncoming( 222 | message: MessageIncoming.copy(message), 223 | animationController: animationController, 224 | ); 225 | } else { 226 | assert(false, 'Unknown message type ${message.runtimeType}'); 227 | } 228 | _messages.insert(0, chatMessage); 229 | 230 | // look at the https://codelabs.developers.google.com/codelabs/flutter/#6 231 | chatMessage.animationController.forward(); 232 | } 233 | } 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /flutter_client/lib/theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | final ThemeData kIOSTheme = new ThemeData( 4 | primarySwatch: Colors.orange, 5 | primaryColor: Colors.grey[100], 6 | primaryColorBrightness: Brightness.light, 7 | ); 8 | 9 | final ThemeData kDefaultTheme = ThemeData.light(); 10 | final Color shimmerBaseColor = Colors.black; /*Colors.white;*/ 11 | final Color shimmerHighlightColor = Colors.grey[200]; /*Colors.grey[700];*/ 12 | 13 | bool isIOS(BuildContext context) { 14 | return Theme.of(context).platform == TargetPlatform.iOS; 15 | } 16 | -------------------------------------------------------------------------------- /flutter_client/lib/widgets/chat_message.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:flutter_client/models/message.dart'; 4 | 5 | /// ChatMessage is base abstract class for outgoing and incoming message widgets 6 | abstract class ChatMessage extends Widget { 7 | /// Message content 8 | Message get message; 9 | 10 | /// Controller of animation for message widget 11 | AnimationController get animationController; 12 | } 13 | -------------------------------------------------------------------------------- /flutter_client/lib/widgets/chat_message_incoming.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:flutter_client/models/message_incoming.dart'; 4 | 5 | import 'chat_message.dart'; 6 | 7 | /// Incoming message author name 8 | const String _server = "Server"; 9 | 10 | /// ChatMessageIncoming is widget to display incoming from server message 11 | class ChatMessageIncoming extends StatelessWidget implements ChatMessage { 12 | /// Incoming message content 13 | final MessageIncoming message; 14 | 15 | /// Controller of animation for message widget 16 | final AnimationController animationController; 17 | 18 | /// Constructor 19 | ChatMessageIncoming({this.message, this.animationController}) 20 | : super(key: Key(message.id)); 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return SizeTransition( 25 | sizeFactor: 26 | CurvedAnimation(parent: animationController, curve: Curves.easeOut), 27 | axisAlignment: 0.0, 28 | child: Container( 29 | margin: EdgeInsets.symmetric(vertical: 10.0), 30 | child: Row( 31 | crossAxisAlignment: CrossAxisAlignment.start, 32 | children: [ 33 | Expanded( 34 | child: Column( 35 | crossAxisAlignment: CrossAxisAlignment.end, 36 | children: [ 37 | Text(_server, style: Theme.of(context).textTheme.subhead), 38 | Container( 39 | margin: EdgeInsets.only(top: 5.0), 40 | child: Text(message.text), 41 | ), 42 | ], 43 | ), 44 | ), 45 | Container( 46 | margin: EdgeInsets.only(left: 16.0), 47 | child: CircleAvatar( 48 | backgroundColor: Colors.pink.shade600, 49 | child: Text(_server[0])), 50 | ), 51 | ], 52 | ), 53 | ), 54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /flutter_client/lib/widgets/chat_message_outgoing.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:shimmer/shimmer.dart'; 4 | 5 | import 'package:flutter_client/models/message_outgoing.dart'; 6 | import 'package:flutter_client/theme.dart'; 7 | 8 | import 'chat_message.dart'; 9 | 10 | /// Outgoing message author name 11 | const String _name = "Me"; 12 | 13 | /// ChatMessageOutgoing is widget to display outgoing to server message 14 | class ChatMessageOutgoing extends StatelessWidget implements ChatMessage { 15 | /// Incoming message content 16 | final MessageOutgoing message; 17 | 18 | /// Controller of animation for message widget 19 | final AnimationController animationController; 20 | 21 | /// Constructor 22 | ChatMessageOutgoing({this.message, this.animationController}) 23 | : super(key: Key(message.id)); 24 | 25 | @override 26 | Widget build(BuildContext context) { 27 | return SizeTransition( 28 | sizeFactor: 29 | CurvedAnimation(parent: animationController, curve: Curves.easeOut), 30 | axisAlignment: 0.0, 31 | child: Container( 32 | margin: EdgeInsets.symmetric(vertical: 10.0), 33 | child: Row( 34 | crossAxisAlignment: CrossAxisAlignment.start, 35 | children: [ 36 | Container( 37 | margin: EdgeInsets.only(right: 16.0), 38 | child: CircleAvatar(child: Text(_name[0])), 39 | ), 40 | Expanded( 41 | child: _getMessageContent(context), 42 | ), 43 | Container( 44 | child: Icon(message.status == MessageOutgoingStatus.SENT 45 | ? Icons.done 46 | : Icons.access_time), 47 | ), 48 | ], 49 | ), 50 | ), 51 | ); 52 | } 53 | 54 | Widget _getMessageContent(BuildContext context) { 55 | var content = Column( 56 | crossAxisAlignment: CrossAxisAlignment.start, 57 | children: [ 58 | Text(_name, style: Theme.of(context).textTheme.subhead), 59 | Container( 60 | margin: EdgeInsets.only(top: 5.0), 61 | child: Text(message.text), 62 | ), 63 | ], 64 | ); 65 | if (message.status != MessageOutgoingStatus.SENT) { 66 | return Shimmer.fromColors( 67 | baseColor: shimmerBaseColor, 68 | highlightColor: shimmerHighlightColor, 69 | child: content, 70 | ); 71 | } 72 | return content; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /flutter_client/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_client 2 | description: Client for tutotrial "How to use gRPC (simple and server-side streaming) within Flutter application". 3 | 4 | # The following defines the version and build number for your application. 5 | # A version number is three numbers separated by dots, like 1.2.43 6 | # followed by an optional build number separated by a +. 7 | # Both the version and the builder number may be overridden in flutter 8 | # build by specifying --build-name and --build-number, respectively. 9 | # Read more about versioning at semver.org. 10 | version: 1.0.1+1 11 | 12 | environment: 13 | sdk: ">=2.0.0-dev.68.0 <3.0.0" 14 | 15 | dependencies: 16 | flutter: 17 | sdk: flutter 18 | 19 | # The following adds the Cupertino Icons font to your application. 20 | # Use with the CupertinoIcons class for iOS style icons. 21 | cupertino_icons: ^0.1.2 22 | 23 | grpc: ^1.0.1 24 | protobuf: ^0.13.1 25 | 26 | rxdart: ^0.20.0 27 | 28 | shimmer: ^1.0.0 29 | 30 | dev_dependencies: 31 | flutter_test: 32 | sdk: flutter 33 | 34 | 35 | # For information on the generic Dart part of this file, see the 36 | # following page: https://www.dartlang.org/tools/pub/pubspec 37 | 38 | # The following section is specific to Flutter. 39 | flutter: 40 | 41 | # The following line ensures that the Material Icons font is 42 | # included with your application, so that you can use the icons in 43 | # the material Icons class. 44 | uses-material-design: true 45 | 46 | # To add assets to your application, add an assets section, like this: 47 | # assets: 48 | # - images/a_dot_burr.jpeg 49 | # - images/a_dot_ham.jpeg 50 | 51 | # An image asset can refer to one or more resolution-specific "variants", see 52 | # https://flutter.io/assets-and-images/#resolution-aware. 53 | 54 | # For details regarding adding assets from package dependencies, see 55 | # https://flutter.io/assets-and-images/#from-packages 56 | 57 | # To add custom fonts to your application, add a fonts section here, 58 | # in this "flutter" section. Each entry in this list should have a 59 | # "family" key with the font family name, and a "fonts" key with a 60 | # list giving the asset and other descriptors for the font. For 61 | # example: 62 | # fonts: 63 | # - family: Schyler 64 | # fonts: 65 | # - asset: fonts/Schyler-Regular.ttf 66 | # - asset: fonts/Schyler-Italic.ttf 67 | # style: italic 68 | # - family: Trajan Pro 69 | # fonts: 70 | # - asset: fonts/TrajanPro.ttf 71 | # - asset: fonts/TrajanPro_Bold.ttf 72 | # weight: 700 73 | # 74 | # For details regarding fonts from package dependencies, 75 | # see https://flutter.io/custom-fonts/#from-packages 76 | -------------------------------------------------------------------------------- /go-server/.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | /go.sum -------------------------------------------------------------------------------- /go-server/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Launch", 9 | "type": "go", 10 | "request": "launch", 11 | "mode": "auto", 12 | "program": "${workspaceFolder}/cmd/server/main.go", 13 | "env": {}, 14 | "args": [] 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /go-server/cmd/server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "os" 7 | 8 | "github.com/amsokol/flutter-grpc-tutorial/go-server/pkg/protocol/grpc" 9 | "github.com/amsokol/flutter-grpc-tutorial/go-server/pkg/service/v1" 10 | ) 11 | 12 | func main() { 13 | if err := grpc.RunServer(context.Background(), v1.NewChatServiceServer(), "3000"); err != nil { 14 | fmt.Fprintf(os.Stderr, "%v\n", err) 15 | os.Exit(1) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /go-server/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/amsokol/flutter-grpc-tutorial/go-server 2 | 3 | require ( 4 | github.com/golang/protobuf v1.2.1-0.20190109072247-347cf4a86c1c 5 | golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 // indirect 6 | google.golang.org/grpc v1.18.0 7 | ) 8 | -------------------------------------------------------------------------------- /go-server/pkg/api/v1/chat.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: chat.proto 3 | 4 | package v1 5 | 6 | import ( 7 | context "context" 8 | fmt "fmt" 9 | proto "github.com/golang/protobuf/proto" 10 | empty "github.com/golang/protobuf/ptypes/empty" 11 | wrappers "github.com/golang/protobuf/ptypes/wrappers" 12 | grpc "google.golang.org/grpc" 13 | math "math" 14 | ) 15 | 16 | // Reference imports to suppress errors if they are not otherwise used. 17 | var _ = proto.Marshal 18 | var _ = fmt.Errorf 19 | var _ = math.Inf 20 | 21 | // This is a compile-time assertion to ensure that this generated file 22 | // is compatible with the proto package it is being compiled against. 23 | // A compilation error at this line likely means your copy of the 24 | // proto package needs to be updated. 25 | const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package 26 | 27 | // Message is response for ChatService.Subscribe method 28 | type Message struct { 29 | // message body 30 | Text string `protobuf:"bytes,1,opt,name=text,proto3" json:"text,omitempty"` 31 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 32 | XXX_unrecognized []byte `json:"-"` 33 | XXX_sizecache int32 `json:"-"` 34 | } 35 | 36 | func (m *Message) Reset() { *m = Message{} } 37 | func (m *Message) String() string { return proto.CompactTextString(m) } 38 | func (*Message) ProtoMessage() {} 39 | func (*Message) Descriptor() ([]byte, []int) { 40 | return fileDescriptor_8c585a45e2093e54, []int{0} 41 | } 42 | 43 | func (m *Message) XXX_Unmarshal(b []byte) error { 44 | return xxx_messageInfo_Message.Unmarshal(m, b) 45 | } 46 | func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 47 | return xxx_messageInfo_Message.Marshal(b, m, deterministic) 48 | } 49 | func (m *Message) XXX_Merge(src proto.Message) { 50 | xxx_messageInfo_Message.Merge(m, src) 51 | } 52 | func (m *Message) XXX_Size() int { 53 | return xxx_messageInfo_Message.Size(m) 54 | } 55 | func (m *Message) XXX_DiscardUnknown() { 56 | xxx_messageInfo_Message.DiscardUnknown(m) 57 | } 58 | 59 | var xxx_messageInfo_Message proto.InternalMessageInfo 60 | 61 | func (m *Message) GetText() string { 62 | if m != nil { 63 | return m.Text 64 | } 65 | return "" 66 | } 67 | 68 | func init() { 69 | proto.RegisterType((*Message)(nil), "v1.Message") 70 | } 71 | 72 | func init() { proto.RegisterFile("chat.proto", fileDescriptor_8c585a45e2093e54) } 73 | 74 | var fileDescriptor_8c585a45e2093e54 = []byte{ 75 | // 191 bytes of a gzipped FileDescriptorProto 76 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4a, 0xce, 0x48, 0x2c, 77 | 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x2a, 0x33, 0x94, 0x92, 0x4e, 0xcf, 0xcf, 0x4f, 78 | 0xcf, 0x49, 0xd5, 0x07, 0x8b, 0x24, 0x95, 0xa6, 0xe9, 0xa7, 0xe6, 0x16, 0x94, 0x54, 0x42, 0x14, 79 | 0x48, 0xc9, 0xa1, 0x4b, 0x96, 0x17, 0x25, 0x16, 0x14, 0xa4, 0x16, 0x15, 0x43, 0xe4, 0x95, 0x64, 80 | 0xb9, 0xd8, 0x7d, 0x53, 0x8b, 0x8b, 0x13, 0xd3, 0x53, 0x85, 0x84, 0xb8, 0x58, 0x4a, 0x52, 0x2b, 81 | 0x4a, 0x24, 0x18, 0x15, 0x18, 0x35, 0x38, 0x83, 0xc0, 0x6c, 0xa3, 0x66, 0x46, 0x2e, 0x6e, 0xe7, 82 | 0x8c, 0xc4, 0x92, 0xe0, 0xd4, 0xa2, 0xb2, 0xcc, 0xe4, 0x54, 0x21, 0x3b, 0x2e, 0x96, 0xe0, 0xd4, 83 | 0xbc, 0x14, 0x21, 0x19, 0x3d, 0x88, 0xb9, 0x7a, 0x30, 0x73, 0xf5, 0x82, 0x4b, 0x8a, 0x32, 0xf3, 84 | 0xd2, 0xc3, 0x12, 0x73, 0x4a, 0x53, 0xa5, 0xc4, 0x30, 0x64, 0x5d, 0x41, 0x4e, 0x52, 0x62, 0x10, 85 | 0x32, 0xe1, 0xe2, 0x0c, 0x2e, 0x4d, 0x2a, 0x4e, 0x2e, 0xca, 0x4c, 0x4a, 0x15, 0xc2, 0xa1, 0x4c, 86 | 0x8a, 0x5b, 0xaf, 0xcc, 0x50, 0x0f, 0xea, 0x2a, 0x25, 0x06, 0x03, 0xc6, 0x24, 0x36, 0xb0, 0x02, 87 | 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfb, 0x39, 0xcd, 0xdf, 0xfa, 0x00, 0x00, 0x00, 88 | } 89 | 90 | // Reference imports to suppress errors if they are not otherwise used. 91 | var _ context.Context 92 | var _ grpc.ClientConn 93 | 94 | // This is a compile-time assertion to ensure that this generated file 95 | // is compatible with the grpc package it is being compiled against. 96 | const _ = grpc.SupportPackageIsVersion4 97 | 98 | // ChatServiceClient is the client API for ChatService service. 99 | // 100 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. 101 | type ChatServiceClient interface { 102 | // Send sends message to the server 103 | Send(ctx context.Context, in *wrappers.StringValue, opts ...grpc.CallOption) (*empty.Empty, error) 104 | // Subscribe is streaming method to get echo messages from the server 105 | Subscribe(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (ChatService_SubscribeClient, error) 106 | } 107 | 108 | type chatServiceClient struct { 109 | cc *grpc.ClientConn 110 | } 111 | 112 | func NewChatServiceClient(cc *grpc.ClientConn) ChatServiceClient { 113 | return &chatServiceClient{cc} 114 | } 115 | 116 | func (c *chatServiceClient) Send(ctx context.Context, in *wrappers.StringValue, opts ...grpc.CallOption) (*empty.Empty, error) { 117 | out := new(empty.Empty) 118 | err := c.cc.Invoke(ctx, "/v1.ChatService/Send", in, out, opts...) 119 | if err != nil { 120 | return nil, err 121 | } 122 | return out, nil 123 | } 124 | 125 | func (c *chatServiceClient) Subscribe(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (ChatService_SubscribeClient, error) { 126 | stream, err := c.cc.NewStream(ctx, &_ChatService_serviceDesc.Streams[0], "/v1.ChatService/Subscribe", opts...) 127 | if err != nil { 128 | return nil, err 129 | } 130 | x := &chatServiceSubscribeClient{stream} 131 | if err := x.ClientStream.SendMsg(in); err != nil { 132 | return nil, err 133 | } 134 | if err := x.ClientStream.CloseSend(); err != nil { 135 | return nil, err 136 | } 137 | return x, nil 138 | } 139 | 140 | type ChatService_SubscribeClient interface { 141 | Recv() (*Message, error) 142 | grpc.ClientStream 143 | } 144 | 145 | type chatServiceSubscribeClient struct { 146 | grpc.ClientStream 147 | } 148 | 149 | func (x *chatServiceSubscribeClient) Recv() (*Message, error) { 150 | m := new(Message) 151 | if err := x.ClientStream.RecvMsg(m); err != nil { 152 | return nil, err 153 | } 154 | return m, nil 155 | } 156 | 157 | // ChatServiceServer is the server API for ChatService service. 158 | type ChatServiceServer interface { 159 | // Send sends message to the server 160 | Send(context.Context, *wrappers.StringValue) (*empty.Empty, error) 161 | // Subscribe is streaming method to get echo messages from the server 162 | Subscribe(*empty.Empty, ChatService_SubscribeServer) error 163 | } 164 | 165 | func RegisterChatServiceServer(s *grpc.Server, srv ChatServiceServer) { 166 | s.RegisterService(&_ChatService_serviceDesc, srv) 167 | } 168 | 169 | func _ChatService_Send_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 170 | in := new(wrappers.StringValue) 171 | if err := dec(in); err != nil { 172 | return nil, err 173 | } 174 | if interceptor == nil { 175 | return srv.(ChatServiceServer).Send(ctx, in) 176 | } 177 | info := &grpc.UnaryServerInfo{ 178 | Server: srv, 179 | FullMethod: "/v1.ChatService/Send", 180 | } 181 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 182 | return srv.(ChatServiceServer).Send(ctx, req.(*wrappers.StringValue)) 183 | } 184 | return interceptor(ctx, in, info, handler) 185 | } 186 | 187 | func _ChatService_Subscribe_Handler(srv interface{}, stream grpc.ServerStream) error { 188 | m := new(empty.Empty) 189 | if err := stream.RecvMsg(m); err != nil { 190 | return err 191 | } 192 | return srv.(ChatServiceServer).Subscribe(m, &chatServiceSubscribeServer{stream}) 193 | } 194 | 195 | type ChatService_SubscribeServer interface { 196 | Send(*Message) error 197 | grpc.ServerStream 198 | } 199 | 200 | type chatServiceSubscribeServer struct { 201 | grpc.ServerStream 202 | } 203 | 204 | func (x *chatServiceSubscribeServer) Send(m *Message) error { 205 | return x.ServerStream.SendMsg(m) 206 | } 207 | 208 | var _ChatService_serviceDesc = grpc.ServiceDesc{ 209 | ServiceName: "v1.ChatService", 210 | HandlerType: (*ChatServiceServer)(nil), 211 | Methods: []grpc.MethodDesc{ 212 | { 213 | MethodName: "Send", 214 | Handler: _ChatService_Send_Handler, 215 | }, 216 | }, 217 | Streams: []grpc.StreamDesc{ 218 | { 219 | StreamName: "Subscribe", 220 | Handler: _ChatService_Subscribe_Handler, 221 | ServerStreams: true, 222 | }, 223 | }, 224 | Metadata: "chat.proto", 225 | } 226 | -------------------------------------------------------------------------------- /go-server/pkg/protocol/grpc/server.go: -------------------------------------------------------------------------------- 1 | package grpc 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "net" 7 | 8 | "google.golang.org/grpc" 9 | 10 | "github.com/amsokol/flutter-grpc-tutorial/go-server/pkg/api/v1" 11 | ) 12 | 13 | // RunServer registers gRPC service and run server 14 | func RunServer(ctx context.Context, srv v1.ChatServiceServer, port string) error { 15 | listen, err := net.Listen("tcp", ":"+port) 16 | if err != nil { 17 | return err 18 | } 19 | 20 | // register service 21 | server := grpc.NewServer() 22 | v1.RegisterChatServiceServer(server, srv) 23 | 24 | // start gRPC server 25 | log.Println("starting gRPC server...") 26 | return server.Serve(listen) 27 | } 28 | -------------------------------------------------------------------------------- /go-server/pkg/service/v1/chat.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | 8 | "github.com/golang/protobuf/ptypes/empty" 9 | "github.com/golang/protobuf/ptypes/wrappers" 10 | 11 | "github.com/amsokol/flutter-grpc-tutorial/go-server/pkg/api/v1" 12 | ) 13 | 14 | // chatServiceServer is implementation of v1.ChatServiceServer proto interface 15 | type chatServiceServer struct { 16 | msg chan string 17 | } 18 | 19 | // NewChatServiceServer creates Chat service object 20 | func NewChatServiceServer() v1.ChatServiceServer { 21 | return &chatServiceServer{msg: make(chan string, 1000)} 22 | } 23 | 24 | // Send sends message to the server 25 | func (s *chatServiceServer) Send(ctx context.Context, message *wrappers.StringValue) (*empty.Empty, error) { 26 | if message != nil { 27 | log.Printf("Send requested: message=%v", *message) 28 | s.msg <- message.Value 29 | } else { 30 | log.Print("Send requested: message=") 31 | } 32 | 33 | return &empty.Empty{}, nil 34 | } 35 | 36 | // Subscribe is streaming method to get echo messages from the server 37 | func (s *chatServiceServer) Subscribe(e *empty.Empty, stream v1.ChatService_SubscribeServer) error { 38 | log.Print("Subscribe requested") 39 | for { 40 | m := <-s.msg 41 | n := v1.Message{Text: fmt.Sprintf("I have received from you: %s. Thanks!", m)} 42 | if err := stream.Send(&n); err != nil { 43 | // put message back to the channel 44 | s.msg <- m 45 | log.Printf("Stream connection failed: %v", err) 46 | return nil 47 | } 48 | log.Printf("Message sent: %+v", n) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /third_party/google/protobuf/empty.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // https://developers.google.com/protocol-buffers/ 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions are 7 | // met: 8 | // 9 | // * Redistributions of source code must retain the above copyright 10 | // notice, this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above 12 | // copyright notice, this list of conditions and the following disclaimer 13 | // in the documentation and/or other materials provided with the 14 | // distribution. 15 | // * Neither the name of Google Inc. nor the names of its 16 | // contributors may be used to endorse or promote products derived from 17 | // this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | syntax = "proto3"; 32 | 33 | package google.protobuf; 34 | 35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes"; 36 | option go_package = "github.com/golang/protobuf/ptypes/empty"; 37 | option java_package = "com.google.protobuf"; 38 | option java_outer_classname = "EmptyProto"; 39 | option java_multiple_files = true; 40 | option objc_class_prefix = "GPB"; 41 | option cc_enable_arenas = true; 42 | 43 | // A generic empty message that you can re-use to avoid defining duplicated 44 | // empty messages in your APIs. A typical example is to use it as the request 45 | // or the response type of an API method. For instance: 46 | // 47 | // service Foo { 48 | // rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); 49 | // } 50 | // 51 | // The JSON representation for `Empty` is empty JSON object `{}`. 52 | message Empty {} 53 | -------------------------------------------------------------------------------- /third_party/google/protobuf/timestamp.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // https://developers.google.com/protocol-buffers/ 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions are 7 | // met: 8 | // 9 | // * Redistributions of source code must retain the above copyright 10 | // notice, this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above 12 | // copyright notice, this list of conditions and the following disclaimer 13 | // in the documentation and/or other materials provided with the 14 | // distribution. 15 | // * Neither the name of Google Inc. nor the names of its 16 | // contributors may be used to endorse or promote products derived from 17 | // this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | syntax = "proto3"; 32 | 33 | package google.protobuf; 34 | 35 | option csharp_namespace = "Google.Protobuf.WellKnownTypes"; 36 | option cc_enable_arenas = true; 37 | option go_package = "github.com/golang/protobuf/ptypes/timestamp"; 38 | option java_package = "com.google.protobuf"; 39 | option java_outer_classname = "TimestampProto"; 40 | option java_multiple_files = true; 41 | option objc_class_prefix = "GPB"; 42 | 43 | // A Timestamp represents a point in time independent of any time zone 44 | // or calendar, represented as seconds and fractions of seconds at 45 | // nanosecond resolution in UTC Epoch time. It is encoded using the 46 | // Proleptic Gregorian Calendar which extends the Gregorian calendar 47 | // backwards to year one. It is encoded assuming all minutes are 60 48 | // seconds long, i.e. leap seconds are "smeared" so that no leap second 49 | // table is needed for interpretation. Range is from 50 | // 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. 51 | // By restricting to that range, we ensure that we can convert to 52 | // and from RFC 3339 date strings. 53 | // See [https://www.ietf.org/rfc/rfc3339.txt](https://www.ietf.org/rfc/rfc3339.txt). 54 | // 55 | // # Examples 56 | // 57 | // Example 1: Compute Timestamp from POSIX `time()`. 58 | // 59 | // Timestamp timestamp; 60 | // timestamp.set_seconds(time(NULL)); 61 | // timestamp.set_nanos(0); 62 | // 63 | // Example 2: Compute Timestamp from POSIX `gettimeofday()`. 64 | // 65 | // struct timeval tv; 66 | // gettimeofday(&tv, NULL); 67 | // 68 | // Timestamp timestamp; 69 | // timestamp.set_seconds(tv.tv_sec); 70 | // timestamp.set_nanos(tv.tv_usec * 1000); 71 | // 72 | // Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`. 73 | // 74 | // FILETIME ft; 75 | // GetSystemTimeAsFileTime(&ft); 76 | // UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; 77 | // 78 | // // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z 79 | // // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z. 80 | // Timestamp timestamp; 81 | // timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL)); 82 | // timestamp.set_nanos((INT32) ((ticks % 10000000) * 100)); 83 | // 84 | // Example 4: Compute Timestamp from Java `System.currentTimeMillis()`. 85 | // 86 | // long millis = System.currentTimeMillis(); 87 | // 88 | // Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000) 89 | // .setNanos((int) ((millis % 1000) * 1000000)).build(); 90 | // 91 | // 92 | // Example 5: Compute Timestamp from current time in Python. 93 | // 94 | // timestamp = Timestamp() 95 | // timestamp.GetCurrentTime() 96 | // 97 | // # JSON Mapping 98 | // 99 | // In JSON format, the Timestamp type is encoded as a string in the 100 | // [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the 101 | // format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" 102 | // where {year} is always expressed using four digits while {month}, {day}, 103 | // {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional 104 | // seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), 105 | // are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone 106 | // is required. A proto3 JSON serializer should always use UTC (as indicated by 107 | // "Z") when printing the Timestamp type and a proto3 JSON parser should be 108 | // able to accept both UTC and other timezones (as indicated by an offset). 109 | // 110 | // For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past 111 | // 01:30 UTC on January 15, 2017. 112 | // 113 | // In JavaScript, one can convert a Date object to this format using the 114 | // standard [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString] 115 | // method. In Python, a standard `datetime.datetime` object can be converted 116 | // to this format using [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) 117 | // with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one 118 | // can use the Joda Time's [`ISODateTimeFormat.dateTime()`]( 119 | // http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime-- 120 | // ) to obtain a formatter capable of generating timestamps in this format. 121 | // 122 | // 123 | message Timestamp { 124 | 125 | // Represents seconds of UTC time since Unix epoch 126 | // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to 127 | // 9999-12-31T23:59:59Z inclusive. 128 | int64 seconds = 1; 129 | 130 | // Non-negative fractions of a second at nanosecond resolution. Negative 131 | // second values with fractions must still have non-negative nanos values 132 | // that count forward in time. Must be from 0 to 999,999,999 133 | // inclusive. 134 | int32 nanos = 2; 135 | } 136 | -------------------------------------------------------------------------------- /third_party/google/protobuf/wrappers.proto: -------------------------------------------------------------------------------- 1 | // Protocol Buffers - Google's data interchange format 2 | // Copyright 2008 Google Inc. All rights reserved. 3 | // https://developers.google.com/protocol-buffers/ 4 | // 5 | // Redistribution and use in source and binary forms, with or without 6 | // modification, are permitted provided that the following conditions are 7 | // met: 8 | // 9 | // * Redistributions of source code must retain the above copyright 10 | // notice, this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above 12 | // copyright notice, this list of conditions and the following disclaimer 13 | // in the documentation and/or other materials provided with the 14 | // distribution. 15 | // * Neither the name of Google Inc. nor the names of its 16 | // contributors may be used to endorse or promote products derived from 17 | // this software without specific prior written permission. 18 | // 19 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | // Wrappers for primitive (non-message) types. These types are useful 32 | // for embedding primitives in the `google.protobuf.Any` type and for places 33 | // where we need to distinguish between the absence of a primitive 34 | // typed field and its default value. 35 | 36 | syntax = "proto3"; 37 | 38 | package google.protobuf; 39 | 40 | option csharp_namespace = "Google.Protobuf.WellKnownTypes"; 41 | option cc_enable_arenas = true; 42 | option go_package = "github.com/golang/protobuf/ptypes/wrappers"; 43 | option java_package = "com.google.protobuf"; 44 | option java_outer_classname = "WrappersProto"; 45 | option java_multiple_files = true; 46 | option objc_class_prefix = "GPB"; 47 | 48 | // Wrapper message for `double`. 49 | // 50 | // The JSON representation for `DoubleValue` is JSON number. 51 | message DoubleValue { 52 | // The double value. 53 | double value = 1; 54 | } 55 | 56 | // Wrapper message for `float`. 57 | // 58 | // The JSON representation for `FloatValue` is JSON number. 59 | message FloatValue { 60 | // The float value. 61 | float value = 1; 62 | } 63 | 64 | // Wrapper message for `int64`. 65 | // 66 | // The JSON representation for `Int64Value` is JSON string. 67 | message Int64Value { 68 | // The int64 value. 69 | int64 value = 1; 70 | } 71 | 72 | // Wrapper message for `uint64`. 73 | // 74 | // The JSON representation for `UInt64Value` is JSON string. 75 | message UInt64Value { 76 | // The uint64 value. 77 | uint64 value = 1; 78 | } 79 | 80 | // Wrapper message for `int32`. 81 | // 82 | // The JSON representation for `Int32Value` is JSON number. 83 | message Int32Value { 84 | // The int32 value. 85 | int32 value = 1; 86 | } 87 | 88 | // Wrapper message for `uint32`. 89 | // 90 | // The JSON representation for `UInt32Value` is JSON number. 91 | message UInt32Value { 92 | // The uint32 value. 93 | uint32 value = 1; 94 | } 95 | 96 | // Wrapper message for `bool`. 97 | // 98 | // The JSON representation for `BoolValue` is JSON `true` and `false`. 99 | message BoolValue { 100 | // The bool value. 101 | bool value = 1; 102 | } 103 | 104 | // Wrapper message for `string`. 105 | // 106 | // The JSON representation for `StringValue` is JSON string. 107 | message StringValue { 108 | // The string value. 109 | string value = 1; 110 | } 111 | 112 | // Wrapper message for `bytes`. 113 | // 114 | // The JSON representation for `BytesValue` is JSON string. 115 | message BytesValue { 116 | // The bytes value. 117 | bytes value = 1; 118 | } 119 | -------------------------------------------------------------------------------- /third_party/protoc-gen.cmd: -------------------------------------------------------------------------------- 1 | @echo Compiling proto file(s)... 2 | 3 | @protoc empty.proto timestamp.proto wrappers.proto ^ 4 | --proto_path=../third_party/google/protobuf ^ 5 | --plugin=protoc-gen-dart=%USERPROFILE%/AppData/Roaming/Pub/Cache/bin/protoc-gen-dart.bat ^ 6 | --dart_out=grpc:../flutter_client/lib/api/v1/google/protobuf 7 | 8 | @protoc chat.proto ^ 9 | --proto_path=../api/proto/v1 ^ 10 | --proto_path=. ^ 11 | --go_out=plugins=grpc:../go-server/pkg/api/v1 12 | 13 | @protoc chat.proto ^ 14 | --proto_path=../api/proto/v1 ^ 15 | --proto_path=. ^ 16 | --plugin=protoc-gen-dart=%USERPROFILE%/AppData/Roaming/Pub/Cache/bin/protoc-gen-dart.bat ^ 17 | --dart_out=grpc:../flutter_client/lib/api/v1 18 | 19 | @echo Done --------------------------------------------------------------------------------