Первая часть находится здесь Привет, я Андрей, работаю Flutter разработчиком в компании Финам. Давайте развивать сервис Umka основы которого мы заложили в первой части. Для начала чуть изменим нашу "базу вопросов", таким образом, чтобы она содержала правильный ответ к каждому вопросу: В файл lib/questions_db_driver.dart добавим метод getCorrectAnswerById, для получения корректного ответа по идентификатору вопроса и сделаем небольшой рефакторинг кода: В класс UmkaService добавим реализацию метода sendAnswer в котором: получим из "базы" правильный ответ если по какой-то причине "клиент" передал несуществующий идентификатор вопроса "выбросим" ошибку   throw grpc.GrpcError.invalidArgument('Invalid question id!'); оценим ответ (за правильный в поле mark запишем 5, за неверный "влепим двойку") и вернём оценку "клиенту" Остальной код в файле lib/service.dart без изменений. Реализация метода sendAnswer на стороне клиентского приложения такая: создаем "экземпляр" класса Answer добавляем в него текст ответа введённый "студентом" в терминал отправляем ответ на "оценку" дождавшись оценки от сервиса выводим её в консоль Также чуть изменим метод обращения к сервису Umka callService: Здесь все просто: запрашиваем у сервиса вопрос отправляем на него ответ закрываем соединение Для запуска сервиса на localhost из директории проекта выполним команду: dart lib/service.dart В окне терминала сервиса будут видны логи отправленных ответов. Чтобы завершить работу сервиса, можно нажать ctrl+c. Командой dart lib/client.dart в соседнем окне терминала из папки проекта запустим нашего "клиента" и представим себя студентом, которому нужно ответить на полученный вопрос. Для этого читаем вопрос, и в терминал в виде числа вводим ответ. После этого нам "прилетит" оценка mark: 5 или mark: 2. Демонстрация вышеописанного: Давайте "заставим" вызов sendAnswer прислать нам ошибку. Для этого подменим question.id на нелепый, например так: Сервис пришлет нам ошибку Демонстрация: Ошибки, конечно же, требуют корректной обработки. Давайте добавим нашему сервису возможность обучать "студентов".  Для этого организуем периодическую отправку вопросов клиентскому приложению вместе с ответом на него. Здесь нам и пригодится возможность gRPC отправлять stream c сервера "клиентам". Добавим к описанию нашего сервиса в файл protos/umka.proto один тип: И один удалённый вызов: Обратите внимание на аннотацию stream перед возвращаемым типом. Именно она "решает", что при вызове данной процедуры клиент будет получать поток данных, а не одиночный ответ. Теперь описание выглядит так: Мы изменили описание сервиса, поэтому нужна "регенерация" gRPC Dart кода, и мы в папке проекта просто запускаем знакомую уже команду: protoc -I protos/ protos/umka.proto --dart_out=grpc:lib/generated Можно заглянуть во вновь сгенерированный код и убедиться, что там появился новый "вызов" в виде метода getTutorial класса UmkaServiceBase в файле umka.pbgrpc.dart и класс AnsweredQuestion в файле umka.pb.dart. Перейдя в файл lib/service.dart обнаруживаем "ворчание" компилятора на то, что в классе UmkaService отсутствует реализация метода getTutorial. Напишем его код следующим образом: В Dart для того, чтобы функция возвращала стрим её нужно пометить ключевым словом async*. После этого в стрим объекты указанного типа Stream<AnsweredQuestion> отправляются с помощью ключевого слова yield. Метод getTutorial по одному будет брать вопросы из "базы", отправлять их "студенту" в клиентское приложение, делать паузу 2 секунды для "подумать". Процесс будет повторяться пока не закончатся данные. После этого соединение, установленное при вызове метода getTutorial, будет прервано. Изменения здесь небольшие: добавим в UmkaTerminalClient метод запроса урока takeTutorial, изменим обращение к сервису сосредоточившись только на "уроке". Dart конструкция await for позволяет удобно брать данные из потока до тех пор, пока поток не "иссякнет", после чего метод takeTutorial завершится. Текст вопросов с ответами на них просто печатаем в консоль. Запускаем dart lib/service.dart в одном терминальном окне и dart lib/client.dart в другом, и наблюдаем поток вопросов в клиентский терминал: На этом  и завершим вторую часть. Мы поработали над развитием нашей системы добавив полезные "фичи": Отправка на сервер ответа на полученный вопрос. Получение обучающего материала в виде потока задачек с ответами. Посмотрели как "прилетает" ошибка. Думаю, стало понятно, что в gRPC предусмотрена удобная возможность развития системы. До встречи в части №3 где мы продолжим добавлять полезные возможности нашему сервису на основе потока данных от клиента к сервису и двунаправленного потока данных. Третья часть Мобильная разработка, Dart, Flutter