Привет, я Андрей, работаю Flutter разработчиком в компании Финам.

В этой части мы сделаем рефакторинг проекта и подключим клиентское Flutter приложение к сервису Umka.

Рефакторинг проектa

Репозиторий для описания и сгенерированного кода проекта Umka

Выделим в отдельный проект описание сервиса Umka на IDL Progobuf и сгенерированные для сервиса файлы. Поместим код в репозиторий umka_proto на Github.

Репозиторий для сервиса Umka

Удалим из проекта Umka Service описание umka.proto и папку для сгенерированного базового Dart кода сервиса lib/generated.

Репозиторий umka_service.

Фактически здесь у нас осталось только 2 файла:

  • Код для серверного приложения service.dart

  • Код для извлечения вопросов из импровизированной базы questions_db_driver.dart

Базовый код подключим в виде зависимости umka_proto:

Теперь все готово для подключения к сервису мобильного Flutter приложения.

Мобильное Flutter приложение для работы с сервисом Umka

Целью даной серии не является описание процесса создания Flutter приложения, поэтому я дам краткий обзор как оно устроено и ссылку на исходный код.

Umka-Flutter это не полноценное мобильное приложение, а лишь прототип для демонстрации использования сервиса Umka, создание которого мы рассмотрели в предыдущих 3х частях цикла, и который также является демонстрационным прототипом сервиса на основе технологии gRPC.

Подробнее мы рассмотрим код отвечающий за взаимодействие с gRPC сервисом.

Для управления состоянием я выбрал Bloc, конкретнее Cubit, как один из наиболее популярных и не очень "вербозных" подходов.

На главном экране расположено 3 вкладки:

  • Quiz, отправка ответа на случайный вопрос.

  • Tutorial, ответы на список вопросов, полученных от сервиса, с индикацией правильного ответа.

  • Exam, импровизированное прохождение экзамена.

Структура проекта мобильного приложения

Подключение нашей библиотеки umka_proto делается точно также как и для сервиса:

Файловая структура проекта выглядит так:

У нас есть home_screen.dart в папке lib/ui/home где реализована навигация по трём вкладкам, а также 3 директории для "фич": lib/ui/quiz, lib/ui/tutorial и lib/ui/exam, в каждой из которых присутствуют .dart файлы с кодом для:

  • хранения состояния (*state.dart)

  • бизнес логики и изменения состояния (*cubit.dart)

  • отображения пользовательского интерфейса (*view.dart)

Удалённые вызовы сервиса Umka описаны в файле lib/services/umka_service.dart. Код вызовов практически полностью идентичен тому, который мы рассматривали в предыдущих трёх частях серии для терминального клиента class UmkaTerminalClient.

Остальные файлы в проекте являются стандартными для Flutter проекта или вспомогательными.

Код мобильного приложения Umka

ссылка на репозиторий с исходным кодом проекта

Логика работы

Работа с удаленными вызовами сервиса осуществляется исключительно в файлах *cubit.dart:

  • Делаем запрос нужной информации на сервисе или отправляем сервису данные.

  • Получаем от сервиса ответ.

  • В зависимости от полученного ответа меняем состояние class *State для соответствующей "фичи" нужным образом, отправляя новое состояние методами emit(newState) расположенными в class *Cubit.

Вкладка Quiz

Демонстрация работы

  • Вводим имя.

  • Нажимаем кнопку "Get Random Question".

  • Отображается вопрос, на который мы вводим ответ.

  • Отправляем ответ на сервис.

  • Сервис оценивает ответ и присылает результат.

И так "по кругу" сколько угодно раз.

За логику работы отвечает класс QuizCubit, находящийся в файле lib/ui/quiz/quiz_cubit.dart.

По заполнению поля с именем появляется кнопка "Get Random Question" и после её нажатия срабатывает метод getRandomQuestion(), в котором происходит удаленный вызов на сервис:

final question = await umkaService.getRandomQuestion(student);.

Вопрос запоминается в QuizState и отображается для студента. Он заполняет поле с ответом и отправляет его сервису:

final evaluation = await umkaService.sendAnswer(answer);.

В ответе evaluation приходит результат правильным был ответ или нет, который и отображается студенту.

Вкладка Tutorial

Работает это так:

  • Вводим имя и нажимаем кнопку "Start".

  • С сервиса начинают потоком поступать вопросы, примерно каждые 2 секунды.

  • Вводим ответ на каждый вопрос.

  • "Чекаем" ответы, и сразу видим результат верно или нет.

  • Исправляем ошибочные ответы.

Работа с сервисом происходит в классе TutorialCubit:

Метод takeTutorial() срабатывает по нажатии кнопки "Start". На строке №20 мы "подписываемся" на поток вопросов от сервиса и обрабатываем их по одному. С сервиса "прилетают" уже отвеченные вопросы, поэтому после проставления галочки, мы немедленно видим результат.

Вкладка Exam

"Экзамен" проводится следующим образом:

  • "Студент" вводит своё имя и нажимает кнопку готовности к экзамену.

  • Сервис присылает все экзаменационные вопросы и первый из них отображается на экране.

  • После ввода ответа экзаменуемый кнопкой "Send..." отправляет ответ в поток соединения с сервисом.

  • Появляется следующий вопрос ... .

  • После отправки ответа на последний вопрос сервис присылает итоговую "оценку".

Взаимодействие с сервисом происходит в классе ExamCubit:

Нажатие кнопки "Send ..." запускает метод takeExam(String name). Вопросы запрашиваются на сервисе и сохраняются в объекте exam:

final exam = await umkaService.getExam(state.student);

Создается соединение с сервисом, на который передается стрим для для отправки ответов:

final evaluation = await umkaService.takeExam(state.student.name, answersStream!.stream);

После получения всех ответов, сервис вернет "оценку", которая помещается в объект evaluation.

Метод sendAnswer(String enteredAnswer) просто добавляет ответы в стрим, переданный сервису.

Запускаем сервис и приложение на локальном компьютере

Я приведу для примера набор команд, с помощью которых можно склонировать исходники и запустить приложение и сервис, чтобы можно было всё "пощупать руками", "погулять" по коду и посмотреть логи.

Чтобы всё сработало на компьютере должны быть установлены Git и Dart и Flutter. Также запустите симулятор или эмулятор мобильного устройства или подсоедините реальное. Командой flutter devices убедитесь, что мобильное устройство видно и находится первым в списке доступных.

  • Открываем терминал, создаем директорию umka_demo и переходим в нее:

mkdir umka_demo & cd umka_demo

  • Клонируем исходники сервиса:

git clone https://github.com/Umka-org/umka_service.git

  • Клонируем исходники мобильного приложения:

git clone https://github.com/Umka-org/umka_flutter_app.git

  • Переходим в папку с сервисом, "подтягиваем" зависимости и запускаем сервис:

cd umka_service && pub get && dart lib/service.dart

  • Открываем рядом второй терминал, переходим в папку с мобильным приложением, добавляем в проект нужные библиотеки и запускаем мобильное приложение:

cd umka_demo/umka_flutter_app && flutter pub get && flutter run

Если все сработало, наслаждаемся, если же что-то пошло не так, сильно не злимся, а устраняем проблемы. Я проверил работоспособность приведённых команд на маке и Ubuntu.

На этом данная серия из четырех статей завершена. Надеюсь было полезно. Спасибо всем, кто дочитал до конца и поддерживал меня.

До встречи!

Комментарии (4)


  1. bus_pro
    26.09.2021 13:54

    flutter рулит!
    Наверное ещё никогда порог входа в новые технологии не был столь мал. И замечательно, что сразу на гребне волны с последними фишками такими, как gRPC и много другого, да ещё под любые платформы.
    Автору большое спасибо, что провел нас от начала до успешного финала с запуском приложения.


    1. Andrey_chik Автор
      26.09.2021 14:55

      Малый порог входа, к сожалению, имеет и "оборотную сторону медали".


      1. artchalet
        26.09.2021 16:23
        +1

        Если не трудно, уточните пожалуйста про "оборотную сторону медали" ?


        1. Andrey_chik Автор
          27.09.2021 13:26
          +1

          Я про фразу "Наверное ещё никогда порог входа в новые технологии не был столь мал." понял так, что имеется ввиду не материальный порог, вроде "Чтобы делать iOs приложения тебе нужен Мак и платная подписка на девелоперский аккаунт Apple", а то, что требуется относительно невысокий уровень квалификации в разработке и/или временные затраты, чтобы научиться делать Flutter приложения, что в основном хорошо.